summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Studio
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-10-06 11:49:09 +0000
committerOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-10-06 16:58:43 +0200
commit07840524085bd1785b8f9142b03d942f678c5c51 (patch)
tree499c1674fdd1b3e0c07688d8e4e56748dfea2ca3 /src/Authoring/Studio
Initial import
Diffstat (limited to 'src/Authoring/Studio')
-rw-r--r--src/Authoring/Studio/Application/MsgRouter.h125
-rw-r--r--src/Authoring/Studio/Application/StudioConst.h158
-rw-r--r--src/Authoring/Studio/Application/StudioDefs.h44
-rw-r--r--src/Authoring/Studio/Application/StudioInstance.cpp86
-rw-r--r--src/Authoring/Studio/Application/StudioInstance.h49
-rw-r--r--src/Authoring/Studio/Application/StudioTutorialWidget.cpp236
-rw-r--r--src/Authoring/Studio/Application/StudioTutorialWidget.h94
-rw-r--r--src/Authoring/Studio/Application/StudioTutorialWidget.ui184
-rw-r--r--src/Authoring/Studio/Controls/BaseMeasure.cpp206
-rw-r--r--src/Authoring/Studio/Controls/BaseMeasure.h81
-rw-r--r--src/Authoring/Studio/Controls/BlankControl.cpp118
-rw-r--r--src/Authoring/Studio/Controls/BlankControl.h57
-rw-r--r--src/Authoring/Studio/Controls/BreadCrumbControl.cpp326
-rw-r--r--src/Authoring/Studio/Controls/BreadCrumbControl.h94
-rw-r--r--src/Authoring/Studio/Controls/ButtonControl.cpp425
-rw-r--r--src/Authoring/Studio/Controls/ButtonControl.h124
-rw-r--r--src/Authoring/Studio/Controls/ClickableLabel.cpp51
-rw-r--r--src/Authoring/Studio/Controls/ClickableLabel.h48
-rw-r--r--src/Authoring/Studio/Controls/ComboTextBox.h65
-rw-r--r--src/Authoring/Studio/Controls/Control.cpp1806
-rw-r--r--src/Authoring/Studio/Controls/Control.h263
-rw-r--r--src/Authoring/Studio/Controls/ControlData.cpp763
-rw-r--r--src/Authoring/Studio/Controls/ControlData.h276
-rw-r--r--src/Authoring/Studio/Controls/ControlGraph.cpp188
-rw-r--r--src/Authoring/Studio/Controls/ControlGraph.h68
-rw-r--r--src/Authoring/Studio/Controls/ControlGraphIterators.h156
-rw-r--r--src/Authoring/Studio/Controls/EditInPlace.h280
-rw-r--r--src/Authoring/Studio/Controls/FloatEdit.cpp524
-rw-r--r--src/Authoring/Studio/Controls/FloatEdit.h115
-rw-r--r--src/Authoring/Studio/Controls/FlowLayout.cpp1009
-rw-r--r--src/Authoring/Studio/Controls/FlowLayout.h140
-rw-r--r--src/Authoring/Studio/Controls/GenericComboDropDown.h99
-rw-r--r--src/Authoring/Studio/Controls/GenericEdit.h94
-rw-r--r--src/Authoring/Studio/Controls/InsertionLine.cpp142
-rw-r--r--src/Authoring/Studio/Controls/InsertionLine.h76
-rw-r--r--src/Authoring/Studio/Controls/InsertionOverlay.cpp112
-rw-r--r--src/Authoring/Studio/Controls/InsertionOverlay.h74
-rw-r--r--src/Authoring/Studio/Controls/LazyFlow.cpp318
-rw-r--r--src/Authoring/Studio/Controls/LazyFlow.h267
-rw-r--r--src/Authoring/Studio/Controls/ListBoxItem.cpp277
-rw-r--r--src/Authoring/Studio/Controls/ListBoxItem.h101
-rw-r--r--src/Authoring/Studio/Controls/ListBoxStringItem.cpp108
-rw-r--r--src/Authoring/Studio/Controls/ListBoxStringItem.h72
-rw-r--r--src/Authoring/Studio/Controls/ListLayout.cpp185
-rw-r--r--src/Authoring/Studio/Controls/ListLayout.h60
-rw-r--r--src/Authoring/Studio/Controls/NameEdit.cpp138
-rw-r--r--src/Authoring/Studio/Controls/NameEdit.h70
-rw-r--r--src/Authoring/Studio/Controls/OverlayControl.cpp240
-rw-r--r--src/Authoring/Studio/Controls/OverlayControl.h83
-rw-r--r--src/Authoring/Studio/Controls/ProceduralButton.h569
-rw-r--r--src/Authoring/Studio/Controls/Renderer.cpp74
-rw-r--r--src/Authoring/Studio/Controls/Renderer.h105
-rw-r--r--src/Authoring/Studio/Controls/SIcon.cpp165
-rw-r--r--src/Authoring/Studio/Controls/SIcon.h74
-rw-r--r--src/Authoring/Studio/Controls/ScrollController.cpp211
-rw-r--r--src/Authoring/Studio/Controls/ScrollController.h82
-rw-r--r--src/Authoring/Studio/Controls/Scroller.cpp829
-rw-r--r--src/Authoring/Studio/Controls/Scroller.h171
-rw-r--r--src/Authoring/Studio/Controls/ScrollerBackground.cpp202
-rw-r--r--src/Authoring/Studio/Controls/ScrollerBackground.h69
-rw-r--r--src/Authoring/Studio/Controls/ScrollerBar.cpp333
-rw-r--r--src/Authoring/Studio/Controls/ScrollerBar.h120
-rw-r--r--src/Authoring/Studio/Controls/ScrollerButtonControl.cpp97
-rw-r--r--src/Authoring/Studio/Controls/ScrollerButtonControl.h76
-rw-r--r--src/Authoring/Studio/Controls/ScrollerThumb.cpp197
-rw-r--r--src/Authoring/Studio/Controls/ScrollerThumb.h76
-rw-r--r--src/Authoring/Studio/Controls/SplashControl.cpp107
-rw-r--r--src/Authoring/Studio/Controls/SplashControl.h70
-rw-r--r--src/Authoring/Studio/Controls/SplitBar.cpp205
-rw-r--r--src/Authoring/Studio/Controls/SplitBar.h97
-rw-r--r--src/Authoring/Studio/Controls/Splitter.cpp419
-rw-r--r--src/Authoring/Studio/Controls/Splitter.h74
-rw-r--r--src/Authoring/Studio/Controls/StringEdit.cpp246
-rw-r--r--src/Authoring/Studio/Controls/StringEdit.h73
-rw-r--r--src/Authoring/Studio/Controls/TextButton.h350
-rw-r--r--src/Authoring/Studio/Controls/TextEdit.cpp1478
-rw-r--r--src/Authoring/Studio/Controls/TextEdit.h215
-rw-r--r--src/Authoring/Studio/Controls/TextEditContextMenu.cpp135
-rw-r--r--src/Authoring/Studio/Controls/TextEditContextMenu.h77
-rw-r--r--src/Authoring/Studio/Controls/TextEditInPlace.cpp324
-rw-r--r--src/Authoring/Studio/Controls/TextEditInPlace.h92
-rw-r--r--src/Authoring/Studio/Controls/TimeEdit.cpp195
-rw-r--r--src/Authoring/Studio/Controls/TimeEdit.h81
-rw-r--r--src/Authoring/Studio/Controls/ToggleButton.cpp140
-rw-r--r--src/Authoring/Studio/Controls/ToggleButton.h89
-rw-r--r--src/Authoring/Studio/Controls/TreeControl.cpp749
-rw-r--r--src/Authoring/Studio/Controls/TreeControl.h140
-rw-r--r--src/Authoring/Studio/Controls/TreeItem.cpp939
-rw-r--r--src/Authoring/Studio/Controls/TreeItem.h158
-rw-r--r--src/Authoring/Studio/Controls/WidgetControl.cpp439
-rw-r--r--src/Authoring/Studio/Controls/WidgetControl.h110
-rw-r--r--src/Authoring/Studio/Docs/CmdLineOptions.txt27
-rw-r--r--src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.cpp266
-rw-r--r--src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.h64
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropContainer.cpp96
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropContainer.h81
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropSource.cpp131
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropSource.h100
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropTarget.cpp57
-rw-r--r--src/Authoring/Studio/DragAndDrop/DropTarget.h67
-rw-r--r--src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.cpp123
-rw-r--r--src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.h70
-rw-r--r--src/Authoring/Studio/DragAndDrop/FileDropSource.cpp199
-rw-r--r--src/Authoring/Studio/DragAndDrop/FileDropSource.h74
-rw-r--r--src/Authoring/Studio/DragAndDrop/ListBoxDropSource.cpp83
-rw-r--r--src/Authoring/Studio/DragAndDrop/ListBoxDropSource.h60
-rw-r--r--src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.cpp116
-rw-r--r--src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.h65
-rw-r--r--src/Authoring/Studio/DragAndDrop/ProjectDropTarget.cpp229
-rw-r--r--src/Authoring/Studio/DragAndDrop/ProjectDropTarget.h62
-rw-r--r--src/Authoring/Studio/DragAndDrop/SceneDropTarget.cpp242
-rw-r--r--src/Authoring/Studio/DragAndDrop/SceneDropTarget.h67
-rw-r--r--src/Authoring/Studio/DragAndDrop/TimelineDropSource.cpp167
-rw-r--r--src/Authoring/Studio/DragAndDrop/TimelineDropSource.h66
-rw-r--r--src/Authoring/Studio/DragAndDrop/TimelineDropTarget.cpp230
-rw-r--r--src/Authoring/Studio/DragAndDrop/TimelineDropTarget.h88
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/Static.stribin0 -> 50010 bytes
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/Static.strobin0 -> 48720 bytes
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/Strings.h301
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/StringsReadme.htm124
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/compile.py245
-rw-r--r--src/Authoring/Studio/English.lproj/Strings/compile32.py272
-rw-r--r--src/Authoring/Studio/MainFrm.cpp2049
-rw-r--r--src/Authoring/Studio/MainFrm.h249
-rw-r--r--src/Authoring/Studio/MainFrm.qrc83
-rw-r--r--src/Authoring/Studio/MainFrm.ui799
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionContextMenu.cpp76
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionContextMenu.h58
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionModel.cpp225
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionModel.h77
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionView.cpp848
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionView.h201
-rw-r--r--src/Authoring/Studio/Palettes/Action/ActionView.qml426
-rw-r--r--src/Authoring/Studio/Palettes/Action/EventsBrowser.qml163
-rw-r--r--src/Authoring/Studio/Palettes/Action/EventsBrowserView.cpp99
-rw-r--r--src/Authoring/Studio/Palettes/Action/EventsBrowserView.h70
-rw-r--r--src/Authoring/Studio/Palettes/Action/EventsModel.cpp262
-rw-r--r--src/Authoring/Studio/Palettes/Action/EventsModel.h96
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerEmitSignal.qml51
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerFireEvent.qml55
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerGenericBaseColor.qml80
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerGenericCheckbox.qml59
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerGenericColor.qml53
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerGenericText.qml53
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerGoToSlide.qml62
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerMultilineText.qml84
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerProperty.qml255
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseSlider.qml164
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseXYZ.qml91
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerPropertyCombo.qml55
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerPropertySlider.qml64
-rw-r--r--src/Authoring/Studio/Palettes/Action/HandlerPropertyXYZ.qml63
-rw-r--r--src/Authoring/Studio/Palettes/Action/PropertyModel.cpp219
-rw-r--r--src/Authoring/Studio/Palettes/Action/PropertyModel.h80
-rw-r--r--src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.cpp119
-rw-r--r--src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.h99
-rw-r--r--src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.cpp74
-rw-r--r--src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.h52
-rw-r--r--src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.qml88
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ChooserDelegate.qml81
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp592
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.h119
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.cpp56
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.h58
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/FileChooser.qml65
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/FileChooserModel.cpp50
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/FileChooserModel.h46
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/FileChooserView.cpp108
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/FileChooserView.h67
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/GuideInspectable.cpp322
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/GuideInspectable.h86
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/HandlerFilesChooser.qml48
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/HandlerGenericChooser.qml47
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/IEasyInspectorRowListener.h48
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/IInspectableItem.h211
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ImageChooser.qml65
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.cpp49
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.h47
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ImageChooserView.cpp117
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ImageChooserView.h67
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectableBase.cpp38
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectableBase.h67
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp862
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h180
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp429
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h125
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml771
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorGroup.cpp61
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/InspectorGroup.h67
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/MeshChooser.qml63
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.cpp64
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.h47
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/MeshChooserView.cpp131
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/MeshChooserView.h68
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectBrowser.qml175
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp117
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.h94
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp336
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h120
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectReference.qml41
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.cpp53
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.h45
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.cpp101
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.h65
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.cpp120
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.h65
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/TextureChooser.qml70
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/TextureChooserView.cpp107
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/TextureChooserView.h67
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.cpp325
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.h77
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.cpp83
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.h78
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.cpp75
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.h92
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.cpp226
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.h60
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.cpp77
-rw-r--r--src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.h66
-rw-r--r--src/Authoring/Studio/Palettes/Master/MasterControl.cpp295
-rw-r--r--src/Authoring/Studio/Palettes/Master/MasterControl.h114
-rw-r--r--src/Authoring/Studio/Palettes/Master/MasterView.cpp154
-rw-r--r--src/Authoring/Studio/Palettes/Master/MasterView.h78
-rw-r--r--src/Authoring/Studio/Palettes/Progress/ProgressCallback.h57
-rw-r--r--src/Authoring/Studio/Palettes/Progress/ProgressControl.cpp133
-rw-r--r--src/Authoring/Studio/Palettes/Progress/ProgressControl.h72
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp81
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h53
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp587
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h107
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectView.cpp250
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectView.h102
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectView.qml231
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideContextMenu.cpp71
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideContextMenu.h53
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideModel.cpp273
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideModel.h81
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideView.cpp315
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideView.h113
-rw-r--r--src/Authoring/Studio/Palettes/Slide/SlideView.qml249
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.cpp64
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.h53
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.cpp440
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.h110
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp1139
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseStateRow.h205
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.cpp186
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.h80
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.cpp685
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.h125
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.cpp65
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.h61
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.cpp115
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.h66
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.cpp115
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.h63
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/IKeyframeSelector.h52
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItem.h73
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h190
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemProperty.h81
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h55
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineTimebar.h74
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.cpp97
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.h78
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.cpp506
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.h108
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.cpp317
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.h77
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.cpp336
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.h83
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.cpp73
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.h65
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/PasteKeyframesCommandHelper.h118
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.cpp44
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.h69
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.cpp58
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.h65
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.cpp104
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.h121
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.cpp239
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.h74
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.cpp599
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.h160
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.cpp80
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.h69
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimeline.h42
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.cpp1260
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.h225
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.cpp579
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.h119
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.cpp213
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.h84
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.cpp263
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.h90
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.cpp169
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.h65
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.cpp86
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.h60
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ColorControl.cpp307
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ColorControl.h98
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/CommentEdit.cpp231
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/CommentEdit.h77
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.cpp309
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.h107
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/FilterToolbar.cpp271
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/FilterToolbar.h80
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/IBreadCrumbProvider.h72
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ITimelineControl.h53
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.cpp405
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.h154
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.cpp30
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.h149
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Playhead.cpp266
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Playhead.h82
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.cpp89
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.h55
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.cpp152
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.h73
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyRow.cpp377
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyRow.h102
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.cpp234
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.h70
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.cpp508
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.h103
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.cpp374
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.h95
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.cpp110
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.h58
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.cpp190
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.h68
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ScalableScroller.cpp78
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ScalableScroller.h57
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.cpp359
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.h112
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/SlideRow.cpp124
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/SlideRow.h63
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.cpp78
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.h60
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Snapper.cpp455
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/Snapper.h118
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateRow.cpp311
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateRow.h90
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateRowFactory.cpp60
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateRowFactory.h50
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.cpp156
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.h67
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.cpp364
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.h87
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.cpp49
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.h51
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeMeasure.cpp340
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeMeasure.h74
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeToolbar.cpp135
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimeToolbar.h75
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimebarControl.cpp690
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimebarControl.h144
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimebarTip.cpp235
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimebarTip.h84
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineControl.cpp587
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineControl.h158
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineFilter.cpp246
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineFilter.h84
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.cpp75
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.h57
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineRow.cpp170
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineRow.h87
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.cpp67
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.h56
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.cpp713
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.h153
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.cpp360
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.h122
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.cpp86
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.h60
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleControl.cpp171
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleControl.h63
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.cpp205
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.h82
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.cpp83
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.h63
-rw-r--r--src/Authoring/Studio/Palettes/controls/BrowserCombo.qml79
-rw-r--r--src/Authoring/Studio/Palettes/controls/FloatTextField.qml98
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledComboBox.qml155
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledLabel.qml41
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledMenuItem.qml48
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledMenuSeparator.qml45
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledTextField.qml59
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledToolButton.qml54
-rw-r--r--src/Authoring/Studio/Palettes/controls/StyledTooltip.qml44
-rw-r--r--src/Authoring/Studio/PreviewHelper.cpp291
-rw-r--r--src/Authoring/Studio/PreviewHelper.h85
-rw-r--r--src/Authoring/Studio/Render/IStudioRenderer.h88
-rw-r--r--src/Authoring/Studio/Render/PathWidget.cpp512
-rw-r--r--src/Authoring/Studio/Render/PathWidget.h49
-rw-r--r--src/Authoring/Studio/Render/StudioPickValues.h217
-rw-r--r--src/Authoring/Studio/Render/StudioRenderer.cpp883
-rw-r--r--src/Authoring/Studio/Render/StudioRendererImpl.h134
-rw-r--r--src/Authoring/Studio/Render/StudioRendererTranslation.cpp3827
-rw-r--r--src/Authoring/Studio/Render/StudioRendererTranslation.h691
-rw-r--r--src/Authoring/Studio/Render/StudioRotationWidget.cpp437
-rw-r--r--src/Authoring/Studio/Render/StudioScaleWidget.cpp261
-rw-r--r--src/Authoring/Studio/Render/StudioTranslationWidget.cpp172
-rw-r--r--src/Authoring/Studio/Render/StudioWidget.cpp315
-rw-r--r--src/Authoring/Studio/Render/StudioWidget.h140
-rw-r--r--src/Authoring/Studio/Render/StudioWidgetImpl.h346
-rw-r--r--src/Authoring/Studio/Render/WGLRenderContext.cpp213
-rw-r--r--src/Authoring/Studio/Render/WGLRenderContext.h107
-rw-r--r--src/Authoring/Studio/UI/ContextMenu.h150
-rw-r--r--src/Authoring/Studio/UI/CustomReBar.cpp54
-rw-r--r--src/Authoring/Studio/UI/CustomReBar.h56
-rw-r--r--src/Authoring/Studio/UI/PaletteState.cpp160
-rw-r--r--src/Authoring/Studio/UI/PaletteState.h69
-rw-r--r--src/Authoring/Studio/Utils/CmdLineParser.cpp193
-rw-r--r--src/Authoring/Studio/Utils/CmdLineParser.h64
-rw-r--r--src/Authoring/Studio/Utils/ITickTock.h86
-rw-r--r--src/Authoring/Studio/Utils/ImportUtils.cpp90
-rw-r--r--src/Authoring/Studio/Utils/ImportUtils.h72
-rw-r--r--src/Authoring/Studio/Utils/MouseCursor.h91
-rw-r--r--src/Authoring/Studio/Utils/ResourceCache.cpp144
-rw-r--r--src/Authoring/Studio/Utils/ResourceCache.h64
-rw-r--r--src/Authoring/Studio/Utils/StringLoader.cpp151
-rw-r--r--src/Authoring/Studio/Utils/StringLoader.h59
-rw-r--r--src/Authoring/Studio/Utils/StudioUtils.cpp229
-rw-r--r--src/Authoring/Studio/Utils/StudioUtils.h69
-rw-r--r--src/Authoring/Studio/Utils/SystemPreferences.cpp105
-rw-r--r--src/Authoring/Studio/Utils/SystemPreferences.h49
-rw-r--r--src/Authoring/Studio/Utils/TickTock.cpp312
-rw-r--r--src/Authoring/Studio/Workspace/Dialogs.h188
-rw-r--r--src/Authoring/Studio/Workspace/Views/Views.h91
-rw-r--r--src/Authoring/Studio/_Win/Application/AboutDlg.cpp314
-rw-r--r--src/Authoring/Studio/_Win/Application/AboutDlg.h95
-rw-r--r--src/Authoring/Studio/_Win/Application/AboutDlg.ui180
-rw-r--r--src/Authoring/Studio/_Win/Application/BaseLink.cpp252
-rw-r--r--src/Authoring/Studio/_Win/Application/BaseLink.h76
-rw-r--r--src/Authoring/Studio/_Win/Application/MsgRouter.cpp131
-rw-r--r--src/Authoring/Studio/_Win/Application/StudioApp.cpp1835
-rw-r--r--src/Authoring/Studio/_Win/Application/StudioApp.h255
-rw-r--r--src/Authoring/Studio/_Win/Application/StudioColors.h137
-rw-r--r--src/Authoring/Studio/_Win/Application/SubPresentationsDlg.cpp172
-rw-r--r--src/Authoring/Studio/_Win/Application/SubPresentationsDlg.h82
-rw-r--r--src/Authoring/Studio/_Win/Application/SubPresentationsDlg.ui152
-rw-r--r--src/Authoring/Studio/_Win/Application/TextLink.cpp100
-rw-r--r--src/Authoring/Studio/_Win/Application/TextLink.h73
-rw-r--r--src/Authoring/Studio/_Win/Application/WebLink.cpp440
-rw-r--r--src/Authoring/Studio/_Win/Application/WebLink.h100
-rw-r--r--src/Authoring/Studio/_Win/Controls/AppFonts.cpp89
-rw-r--r--src/Authoring/Studio/_Win/Controls/AppFonts.h61
-rw-r--r--src/Authoring/Studio/_Win/Controls/BufferedRenderer.cpp214
-rw-r--r--src/Authoring/Studio/_Win/Controls/BufferedRenderer.h60
-rw-r--r--src/Authoring/Studio/_Win/Controls/MFCEditControl.cpp278
-rw-r--r--src/Authoring/Studio/_Win/Controls/MFCEditControl.h85
-rw-r--r--src/Authoring/Studio/_Win/Controls/OffscreenRenderer.cpp66
-rw-r--r--src/Authoring/Studio/_Win/Controls/OffscreenRenderer.h61
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformEditControl.cpp144
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformEditControl.h85
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformStaticControl.cpp68
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformStaticControl.h66
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformWindowControl.cpp96
-rw-r--r--src/Authoring/Studio/_Win/Controls/PlatformWindowControl.h69
-rw-r--r--src/Authoring/Studio/_Win/Controls/WinRenderer.cpp525
-rw-r--r--src/Authoring/Studio/_Win/Controls/WinRenderer.h130
-rw-r--r--src/Authoring/Studio/_Win/Controls/WndControl.cpp1006
-rw-r--r--src/Authoring/Studio/_Win/Controls/WndControl.h148
-rw-r--r--src/Authoring/Studio/_Win/DragNDrop/DropProxy.cpp341
-rw-r--r--src/Authoring/Studio/_Win/DragNDrop/DropProxy.h82
-rw-r--r--src/Authoring/Studio/_Win/DragNDrop/WinDnd.cpp119
-rw-r--r--src/Authoring/Studio/_Win/DragNDrop/WinDnd.h64
-rw-r--r--src/Authoring/Studio/_Win/Include/StudioPrefixWin32.h39
-rw-r--r--src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp902
-rw-r--r--src/Authoring/Studio/_Win/Palettes/PaletteManager.h153
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.cpp127
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.h58
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.cpp178
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.h80
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.cpp94
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.h54
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Splash/SplashView.cpp92
-rw-r--r--src/Authoring/Studio/_Win/Palettes/Splash/SplashView.h71
-rw-r--r--src/Authoring/Studio/_Win/Studio/stdafx.cpp33
-rw-r--r--src/Authoring/Studio/_Win/Studio/stdafx.h221
-rw-r--r--src/Authoring/Studio/_Win/UI/ContextMenu.cpp292
-rw-r--r--src/Authoring/Studio/_Win/UI/ControlButton.cpp471
-rw-r--r--src/Authoring/Studio/_Win/UI/ControlButton.h119
-rw-r--r--src/Authoring/Studio/_Win/UI/CrashDlg.cpp212
-rw-r--r--src/Authoring/Studio/_Win/UI/CrashDlg.h96
-rw-r--r--src/Authoring/Studio/_Win/UI/EditCameraBar.cpp226
-rw-r--r--src/Authoring/Studio/_Win/UI/EditCameraBar.h99
-rw-r--r--src/Authoring/Studio/_Win/UI/EditorPane.cpp90
-rw-r--r--src/Authoring/Studio/_Win/UI/EditorPane.h65
-rw-r--r--src/Authoring/Studio/_Win/UI/FileDialogEX.cpp451
-rw-r--r--src/Authoring/Studio/_Win/UI/FileDialogEX.h111
-rw-r--r--src/Authoring/Studio/_Win/UI/GLVersionDlg.cpp91
-rw-r--r--src/Authoring/Studio/_Win/UI/GLVersionDlg.h75
-rw-r--r--src/Authoring/Studio/_Win/UI/InterpolationDlg.cpp78
-rw-r--r--src/Authoring/Studio/_Win/UI/InterpolationDlg.h66
-rw-r--r--src/Authoring/Studio/_Win/UI/InterpolationDlg.ui305
-rw-r--r--src/Authoring/Studio/_Win/UI/MemoryDC.cpp258
-rw-r--r--src/Authoring/Studio/_Win/UI/MemoryDC.h91
-rw-r--r--src/Authoring/Studio/_Win/UI/MenuEdit.cpp169
-rw-r--r--src/Authoring/Studio/_Win/UI/MenuEdit.h72
-rw-r--r--src/Authoring/Studio/_Win/UI/MenuItem.cpp58
-rw-r--r--src/Authoring/Studio/_Win/UI/MenuItem.h60
-rw-r--r--src/Authoring/Studio/_Win/UI/NumericEdit.cpp347
-rw-r--r--src/Authoring/Studio/_Win/UI/NumericEdit.h101
-rw-r--r--src/Authoring/Studio/_Win/UI/PlayerContainerWnd.cpp510
-rw-r--r--src/Authoring/Studio/_Win/UI/PlayerContainerWnd.h106
-rw-r--r--src/Authoring/Studio/_Win/UI/PlayerWnd.cpp278
-rw-r--r--src/Authoring/Studio/_Win/UI/PlayerWnd.h99
-rw-r--r--src/Authoring/Studio/_Win/UI/PopupWnd.cpp248
-rw-r--r--src/Authoring/Studio/_Win/UI/PopupWnd.h104
-rw-r--r--src/Authoring/Studio/_Win/UI/PreviewOutputDlg.cpp366
-rw-r--r--src/Authoring/Studio/_Win/UI/PreviewOutputDlg.h92
-rw-r--r--src/Authoring/Studio/_Win/UI/RecentItems.cpp145
-rw-r--r--src/Authoring/Studio/_Win/UI/RecentItems.h88
-rw-r--r--src/Authoring/Studio/_Win/UI/ReportDlg.cpp198
-rw-r--r--src/Authoring/Studio/_Win/UI/ReportDlg.h100
-rw-r--r--src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.cpp66
-rw-r--r--src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.h57
-rw-r--r--src/Authoring/Studio/_Win/UI/SceneView.cpp861
-rw-r--r--src/Authoring/Studio/_Win/UI/SceneView.h174
-rw-r--r--src/Authoring/Studio/_Win/UI/StartupDlg.cpp174
-rw-r--r--src/Authoring/Studio/_Win/UI/StartupDlg.h114
-rw-r--r--src/Authoring/Studio/_Win/UI/StartupDlg.ui171
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.cpp590
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.h121
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.ui183
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioPaletteBar.cpp260
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioPaletteBar.h88
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.cpp155
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.h105
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.ui102
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.cpp385
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.h103
-rw-r--r--src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.ui125
-rw-r--r--src/Authoring/Studio/_Win/UI/TimeEditDlg.cpp524
-rw-r--r--src/Authoring/Studio/_Win/UI/TimeEditDlg.h190
-rw-r--r--src/Authoring/Studio/_Win/UI/timeeditdlg.ui364
-rw-r--r--src/Authoring/Studio/_Win/Utils/MouseCursor.cpp199
-rw-r--r--src/Authoring/Studio/_Win/Utils/WinUtils.cpp600
-rw-r--r--src/Authoring/Studio/_Win/Utils/WinUtils.h65
-rw-r--r--src/Authoring/Studio/_Win/Workspace/Dialogs.cpp1332
-rw-r--r--src/Authoring/Studio/_Win/Workspace/Views.cpp195
-rw-r--r--src/Authoring/Studio/images.qrc179
-rw-r--r--src/Authoring/Studio/images/Action-Action.pngbin0 -> 2927 bytes
-rw-r--r--src/Authoring/Studio/images/Action-ChildAction.pngbin0 -> 2836 bytes
-rw-r--r--src/Authoring/Studio/images/Action-ChildMasterAction.pngbin0 -> 2840 bytes
-rw-r--r--src/Authoring/Studio/images/Action-ComponentAction.pngbin0 -> 2836 bytes
-rw-r--r--src/Authoring/Studio/images/Action-ComponentMasterAction.pngbin0 -> 2844 bytes
-rw-r--r--src/Authoring/Studio/images/Action-MasterAction.pngbin0 -> 2911 bytes
-rw-r--r--src/Authoring/Studio/images/Action-Trash-Disabled.pngbin0 -> 533 bytes
-rw-r--r--src/Authoring/Studio/images/Action-Trash-Disabled@2x.pngbin0 -> 656 bytes
-rw-r--r--src/Authoring/Studio/images/Action-Trash-Normal.pngbin0 -> 534 bytes
-rw-r--r--src/Authoring/Studio/images/Action-Trash-Normal@2x.pngbin0 -> 656 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Alias-Normal.pngbin0 -> 284 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Alias-Normal@2x.pngbin0 -> 545 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Camera-Normal.pngbin0 -> 347 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Camera-Normal@2x.pngbin0 -> 547 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Component-Normal.pngbin0 -> 402 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Component-Normal@2x.pngbin0 -> 797 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cone-Normal.pngbin0 -> 517 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cone-Normal@2x.pngbin0 -> 1001 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cube-Normal.pngbin0 -> 550 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cube-Normal@2x.pngbin0 -> 885 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cylinder-Normal.pngbin0 -> 508 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Cylinder-Normal@2x.pngbin0 -> 938 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Group-Normal.pngbin0 -> 606 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Group-Normal@2x.pngbin0 -> 1206 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Layer-Normal.pngbin0 -> 181 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Layer-Normal@2x.pngbin0 -> 280 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Light-Normal.pngbin0 -> 517 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Light-Normal@2x.pngbin0 -> 923 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Rectangle-Normal.pngbin0 -> 248 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Rectangle-Normal@2x.pngbin0 -> 345 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Sphere-Normal.pngbin0 -> 421 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Sphere-Normal@2x.pngbin0 -> 672 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Text-Normal.pngbin0 -> 151 bytes
-rw-r--r--src/Authoring/Studio/images/Asset-Text-Normal@2x.pngbin0 -> 188 bytes
-rw-r--r--src/Authoring/Studio/images/Insert-Left.pngbin0 -> 273 bytes
-rw-r--r--src/Authoring/Studio/images/Insert-Rearrange-Left.pngbin0 -> 196 bytes
-rw-r--r--src/Authoring/Studio/images/Insert-Rearrange-Right.pngbin0 -> 196 bytes
-rw-r--r--src/Authoring/Studio/images/Insert-Right.pngbin0 -> 275 bytes
-rw-r--r--src/Authoring/Studio/images/Inspector-AnimateToggle-Active.pngbin0 -> 641 bytes
-rw-r--r--src/Authoring/Studio/images/Inspector-AnimateToggle-Active@2x.pngbin0 -> 972 bytes
-rw-r--r--src/Authoring/Studio/images/Inspector-AnimateToggle-Normal.pngbin0 -> 637 bytes
-rw-r--r--src/Authoring/Studio/images/Inspector-AnimateToggle-Normal@2x.pngbin0 -> 978 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Master-Disabled.pngbin0 -> 3009 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Master-Normal.pngbin0 -> 2924 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Master-Selected.pngbin0 -> 2985 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.pngbin0 -> 3010 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.pngbin0 -> 2981 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterLeft-Normal.pngbin0 -> 2890 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterLeft-Selected.pngbin0 -> 2939 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterLeft-disabled.pngbin0 -> 2935 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Normal.pngbin0 -> 2957 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Selected.pngbin0 -> 2934 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterRight-Normal.pngbin0 -> 2879 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterRight-Selected.pngbin0 -> 2904 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterRight-disabled.pngbin0 -> 2918 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Normal.pngbin0 -> 2912 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Selected.pngbin0 -> 2904 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Property-Disabled.pngbin0 -> 2989 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Property-Normal.pngbin0 -> 2924 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-Property-Selected.pngbin0 -> 2981 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.pngbin0 -> 2971 bytes
-rw-r--r--src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.pngbin0 -> 2973 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Alias-Disabled.pngbin0 -> 555 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Alias-Disabled@2x.pngbin0 -> 621 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Alias-Normal.pngbin0 -> 241 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Alias-Normal@2x.pngbin0 -> 391 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Behavior-Disabled.pngbin0 -> 466 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Behavior-Disabled@2x.pngbin0 -> 507 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Behavior-Normal.pngbin0 -> 152 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Behavior-Normal@2x.pngbin0 -> 179 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Camera-Disabled.pngbin0 -> 542 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Camera-Disabled@2x.pngbin0 -> 653 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Camera-Normal.pngbin0 -> 259 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Camera-Normal@2x.pngbin0 -> 418 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Component-Disabled.pngbin0 -> 583 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Component-Disabled@2x.pngbin0 -> 726 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Component-Normal.pngbin0 -> 266 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Component-Normal@2x.pngbin0 -> 460 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Effect-Disabled.pngbin0 -> 631 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Effect-Disabled@2x.pngbin0 -> 869 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Effect-Normal.pngbin0 -> 381 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Effect-Normal@2x.pngbin0 -> 709 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Folder-Disabled.pngbin0 -> 468 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Folder-Disabled@2x.pngbin0 -> 499 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Folder-Normal.pngbin0 -> 469 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Folder-Normal@2x.pngbin0 -> 498 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Group-Disabled.pngbin0 -> 569 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Group-Disabled@2x.pngbin0 -> 836 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Group-Normal.pngbin0 -> 288 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Group-Normal@2x.pngbin0 -> 595 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Image-Disabled.pngbin0 -> 589 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Image-Disabled@2x.pngbin0 -> 704 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Image-Normal.pngbin0 -> 281 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Image-Normal@2.pngbin0 -> 457 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Layer-Disabled.pngbin0 -> 507 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Layer-Disabled@2x.pngbin0 -> 541 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Layer-Normal.pngbin0 -> 151 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Layer-Normal@2x.pngbin0 -> 203 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Light-Disabled.pngbin0 -> 606 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Light-Disabled@2x.pngbin0 -> 803 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Light-Normal.pngbin0 -> 325 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Light-Normal@2x.pngbin0 -> 640 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Material-Disabled.pngbin0 -> 610 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Material-Disabled@2x.pngbin0 -> 768 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Material-Normal.pngbin0 -> 287 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Material-Normal@2x.pngbin0 -> 511 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Model-Disabled.pngbin0 -> 636 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Model-Disabled@2x.pngbin0 -> 832 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Model-Normal.pngbin0 -> 336 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Model-Normal@2x.pngbin0 -> 545 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Property-Disabled.pngbin0 -> 480 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Property-Disabled@2x.pngbin0 -> 518 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Property-Normal.pngbin0 -> 481 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Property-Normal@2x.pngbin0 -> 515 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Scene-Disabled.pngbin0 -> 489 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Scene-Disabled@2x.pngbin0 -> 533 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Scene-Normal.pngbin0 -> 199 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Scene-Normal@2x.pngbin0 -> 282 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Text-Disabled.pngbin0 -> 470 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Text-Disabled@2x.pngbin0 -> 495 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Text-Normal.pngbin0 -> 151 bytes
-rw-r--r--src/Authoring/Studio/images/Objects-Text-Normal@2x.pngbin0 -> 188 bytes
-rw-r--r--src/Authoring/Studio/images/PlaybackHead.pngbin0 -> 233 bytes
-rw-r--r--src/Authoring/Studio/images/PlaybackHead@2x.pngbin0 -> 318 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Active.pngbin0 -> 1562 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Active@2x.pngbin0 -> 2663 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Master-Active.pngbin0 -> 1471 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Master-Active@2x.pngbin0 -> 4190 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Normal.pngbin0 -> 1461 bytes
-rw-r--r--src/Authoring/Studio/images/Slide-Normal@2x.pngbin0 -> 2534 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-Empty.pngbin0 -> 2855 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-HideShow-disabled.pngbin0 -> 238 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-HideShow-disabled@2x.pngbin0 -> 371 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-HideShow.pngbin0 -> 248 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-HideShow@2x.pngbin0 -> 454 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-Lock.pngbin0 -> 224 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-Lock@2x.pngbin0 -> 356 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-Shy.pngbin0 -> 289 bytes
-rw-r--r--src/Authoring/Studio/images/Toggle-Shy@2x.pngbin0 -> 485 bytes
-rw-r--r--src/Authoring/Studio/images/add.pngbin0 -> 454 bytes
-rw-r--r--src/Authoring/Studio/images/add@2x.pngbin0 -> 515 bytes
-rw-r--r--src/Authoring/Studio/images/arrow.pngbin0 -> 523 bytes
-rw-r--r--src/Authoring/Studio/images/arrow@2x.pngbin0 -> 600 bytes
-rw-r--r--src/Authoring/Studio/images/arrow_down.pngbin0 -> 533 bytes
-rw-r--r--src/Authoring/Studio/images/arrow_down@2x.pngbin0 -> 610 bytes
-rw-r--r--src/Authoring/Studio/images/breadcrumb_component_button.pngbin0 -> 3402 bytes
-rw-r--r--src/Authoring/Studio/images/breadcrumb_component_colon_button.pngbin0 -> 262 bytes
-rw-r--r--src/Authoring/Studio/images/breadcrumb_component_grey_button.pngbin0 -> 3374 bytes
-rw-r--r--src/Authoring/Studio/images/breadcrumb_component_scene.pngbin0 -> 3506 bytes
-rw-r--r--src/Authoring/Studio/images/checkbox-checked.pngbin0 -> 3196 bytes
-rw-r--r--src/Authoring/Studio/images/checkbox-unchecked.pngbin0 -> 2946 bytes
-rw-r--r--src/Authoring/Studio/images/empty-pixel.pngbin0 -> 154 bytes
-rw-r--r--src/Authoring/Studio/images/filter-shy-down.pngbin0 -> 289 bytes
-rw-r--r--src/Authoring/Studio/images/filter-shy-down@2x.pngbin0 -> 485 bytes
-rw-r--r--src/Authoring/Studio/images/filter-toggle-eye-down.pngbin0 -> 238 bytes
-rw-r--r--src/Authoring/Studio/images/filter-toggle-eye-down@2x.pngbin0 -> 371 bytes
-rw-r--r--src/Authoring/Studio/images/filter-toggle-eye-up.pngbin0 -> 248 bytes
-rw-r--r--src/Authoring/Studio/images/filter-toggle-eye-up@2x.pngbin0 -> 454 bytes
-rw-r--r--src/Authoring/Studio/images/obsolete_placeholder.pngbin0 -> 158 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-down-depressed.pngbin0 -> 2918 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-down-disabled.pngbin0 -> 2889 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-down-normal.pngbin0 -> 2893 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-left-depressed.pngbin0 -> 2914 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-left-disabled.pngbin0 -> 2886 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-left-normal.pngbin0 -> 2887 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-right-depressed.pngbin0 -> 2916 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-right-disabled.pngbin0 -> 2884 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-right-normal.pngbin0 -> 2885 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-up-depressed.pngbin0 -> 2917 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-up-disabled.pngbin0 -> 2881 bytes
-rw-r--r--src/Authoring/Studio/images/scrollbar-arrows-up-normal.pngbin0 -> 2879 bytes
-rw-r--r--src/Authoring/Studio/images/timebarhandle-disabled-left.pngbin0 -> 276 bytes
-rw-r--r--src/Authoring/Studio/images/timebarhandle-disabled-right.pngbin0 -> 283 bytes
-rw-r--r--src/Authoring/Studio/images/timebarhandle-left.pngbin0 -> 298 bytes
-rw-r--r--src/Authoring/Studio/images/timebarhandle-right.pngbin0 -> 297 bytes
-rw-r--r--src/Authoring/Studio/qt3dstudio.qrc16
-rw-r--r--src/Authoring/Studio/remoteproject.cpp202
-rw-r--r--src/Authoring/Studio/remoteproject.h61
-rw-r--r--src/Authoring/Studio/res/About Icon.bmpbin0 -> 297132 bytes
-rw-r--r--src/Authoring/Studio/res/Flippy-Right.bmpbin0 -> 824 bytes
-rw-r--r--src/Authoring/Studio/res/Flippy-down.bmpbin0 -> 824 bytes
-rw-r--r--src/Authoring/Studio/res/Studio.icobin0 -> 370070 bytes
-rw-r--r--src/Authoring/Studio/res/Studio.manifest22
-rw-r--r--src/Authoring/Studio/res/Studio.pngbin0 -> 16918 bytes
-rw-r--r--src/Authoring/Studio/res/StudioDoc.icobin0 -> 370070 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-00.pngbin0 -> 229 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-01.pngbin0 -> 190 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-02.pngbin0 -> 180 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-03.pngbin0 -> 169 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-04.pngbin0 -> 209 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-05.pngbin0 -> 230 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-06.pngbin0 -> 150 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar-07.pngbin0 -> 155 bytes
-rw-r--r--src/Authoring/Studio/res/Toolbar.bmpbin0 -> 1078 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/button_back.pngbin0 -> 807 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/button_back@2x.pngbin0 -> 1510 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/button_next.pngbin0 -> 782 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/button_next@2x.pngbin0 -> 1499 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/1.pngbin0 -> 79204 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/2.pngbin0 -> 128317 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/3.pngbin0 -> 113936 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/4.pngbin0 -> 108052 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/5.pngbin0 -> 100198 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/6.pngbin0 -> 98135 bytes
-rw-r--r--src/Authoring/Studio/res/Tutorial/screens/7.pngbin0 -> 99190 bytes
-rw-r--r--src/Authoring/Studio/res/arrow_down.pngbin0 -> 213 bytes
-rw-r--r--src/Authoring/Studio/res/arrow_down@2x.pngbin0 -> 273 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_grayed_out.bmpbin0 -> 5432 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-00.pngbin0 -> 302 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-00@2x.pngbin0 -> 465 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-01.pngbin0 -> 231 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-01@2x.pngbin0 -> 324 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-02.pngbin0 -> 311 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-02@2x.pngbin0 -> 554 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-03.pngbin0 -> 149 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-03@2x.pngbin0 -> 190 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-04.pngbin0 -> 349 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-04@2x.pngbin0 -> 848 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-05.pngbin0 -> 359 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-05@2x.pngbin0 -> 671 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-06.pngbin0 -> 162 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color-06@2x.pngbin0 -> 230 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_hi_color.bmpbin0 -> 5432 bytes
-rw-r--r--src/Authoring/Studio/res/client_tools_low_color.bmpbin0 -> 1014 bytes
-rw-r--r--src/Authoring/Studio/res/cursors.bmpbin0 -> 3190 bytes
-rw-r--r--src/Authoring/Studio/res/edit_camera_pan.pngbin0 -> 556 bytes
-rw-r--r--src/Authoring/Studio/res/edit_camera_rot.pngbin0 -> 567 bytes
-rw-r--r--src/Authoring/Studio/res/edit_camera_zoom.pngbin0 -> 567 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_disabled.bmpbin0 -> 4664 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-00.pngbin0 -> 324 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-00@2x.pngbin0 -> 623 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-00_disabled.pngbin0 -> 339 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-00_disabled@2x.pngbin0 -> 487 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-01.pngbin0 -> 395 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-01@2x.pngbin0 -> 730 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-01_disabled.pngbin0 -> 298 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-01_disabled@2x.pngbin0 -> 468 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-02.pngbin0 -> 670 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-02@2x.pngbin0 -> 972 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-02_disabled.pngbin0 -> 672 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-02_disabled@2x.pngbin0 -> 979 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-03.pngbin0 -> 494 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-03@2x.pngbin0 -> 966 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-03_disabled.pngbin0 -> 361 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-03_disabled@2x.pngbin0 -> 624 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-04.pngbin0 -> 341 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-04@2x.pngbin0 -> 774 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-04_disabled.pngbin0 -> 353 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-04_disabled@2x.pngbin0 -> 605 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-05.pngbin0 -> 328 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-05@2x.pngbin0 -> 720 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-05_disabled.pngbin0 -> 367 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi-05_disabled@2x.pngbin0 -> 582 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_hi.bmpbin0 -> 4664 bytes
-rw-r--r--src/Authoring/Studio/res/editcamera_tools_low.bmpbin0 -> 2614 bytes
-rw-r--r--src/Authoring/Studio/res/group_move.pngbin0 -> 560 bytes
-rw-r--r--src/Authoring/Studio/res/group_rotate.pngbin0 -> 570 bytes
-rw-r--r--src/Authoring/Studio/res/group_scale.pngbin0 -> 540 bytes
-rw-r--r--src/Authoring/Studio/res/item_move.pngbin0 -> 562 bytes
-rw-r--r--src/Authoring/Studio/res/item_rotate.pngbin0 -> 573 bytes
-rw-r--r--src/Authoring/Studio/res/item_scale.pngbin0 -> 536 bytes
-rw-r--r--src/Authoring/Studio/res/keyframe.bmpbin0 -> 978 bytes
-rw-r--r--src/Authoring/Studio/res/library_buttons_hi_color.bmpbin0 -> 10040 bytes
-rw-r--r--src/Authoring/Studio/res/library_buttons_low_color.bmpbin0 -> 1294 bytes
-rw-r--r--src/Authoring/Studio/res/libtoolb.bmpbin0 -> 1590 bytes
-rw-r--r--src/Authoring/Studio/res/menubtn_down.bmpbin0 -> 230 bytes
-rw-r--r--src/Authoring/Studio/res/menubtn_normal.bmpbin0 -> 180 bytes
-rw-r--r--src/Authoring/Studio/res/menubtn_selected.bmpbin0 -> 180 bytes
-rw-r--r--src/Authoring/Studio/res/menuedit.bmpbin0 -> 3894 bytes
-rw-r--r--src/Authoring/Studio/res/menutool.bmpbin0 -> 20790 bytes
-rw-r--r--src/Authoring/Studio/res/open_dialog.pngbin0 -> 42144 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_grayed.bmpbin0 -> 3128 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_hi.bmpbin0 -> 3128 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-00.pngbin0 -> 547 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-00@2x.pngbin0 -> 651 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-01.pngbin0 -> 437 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-01@2x.pngbin0 -> 457 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-02.pngbin0 -> 519 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-02@2x.pngbin0 -> 567 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-03.pngbin0 -> 578 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low-03@2x.pngbin0 -> 693 bytes
-rw-r--r--src/Authoring/Studio/res/playback_tools_low.bmpbin0 -> 630 bytes
-rw-r--r--src/Authoring/Studio/res/prefstab-00.pngbin0 -> 519 bytes
-rw-r--r--src/Authoring/Studio/res/prefstab-00@2x.pngbin0 -> 1037 bytes
-rw-r--r--src/Authoring/Studio/res/prefstab-01.pngbin0 -> 597 bytes
-rw-r--r--src/Authoring/Studio/res/prefstab-01@2x.pngbin0 -> 832 bytes
-rw-r--r--src/Authoring/Studio/res/prefstab.bmpbin0 -> 2102 bytes
-rw-r--r--src/Authoring/Studio/res/preview_tools.bmpbin0 -> 246 bytes
-rw-r--r--src/Authoring/Studio/res/project-icon-new.bmpbin0 -> 3128 bytes
-rw-r--r--src/Authoring/Studio/res/project-icon-open.bmpbin0 -> 3128 bytes
-rw-r--r--src/Authoring/Studio/res/qt3ds_header.bmpbin0 -> 192080 bytes
-rw-r--r--src/Authoring/Studio/res/separator.pngbin0 -> 522 bytes
-rw-r--r--src/Authoring/Studio/res/separator@2x.pngbin0 -> 526 bytes
-rw-r--r--src/Authoring/Studio/res/stmtool.bmpbin0 -> 3894 bytes
-rw-r--r--src/Authoring/Studio/res/storagep.bmpbin0 -> 1494 bytes
-rw-r--r--src/Authoring/Studio/res/tmchmenu.bmpbin0 -> 5094 bytes
-rw-r--r--src/Authoring/Studio/res/tmlntool.bmpbin0 -> 3126 bytes
-rw-r--r--src/Authoring/Studio/res/tmtbmenu.bmpbin0 -> 5094 bytes
-rw-r--r--src/Authoring/Studio/res/weblinkc.bmpbin0 -> 1494 bytes
-rw-r--r--src/Authoring/Studio/res/winxp1.bin22
-rw-r--r--src/Authoring/Studio/style.qss219
-rw-r--r--src/Authoring/Studio/version.h35
845 files changed, 105367 insertions, 0 deletions
diff --git a/src/Authoring/Studio/Application/MsgRouter.h b/src/Authoring/Studio/Application/MsgRouter.h
new file mode 100644
index 00000000..78dbf4f6
--- /dev/null
+++ b/src/Authoring/Studio/Application/MsgRouter.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_MSG_ROUTER_H
+#define INCLUDED_MSG_ROUTER_H 1
+#pragma once
+
+//==============================================================================
+// Defines
+//==============================================================================
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioConst.h"
+
+#include <QEvent>
+#include <QObject>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CCmd;
+class CCore;
+
+class CRoutedMessageBase
+{
+public:
+ CRoutedMessageBase() {}
+ virtual ~CRoutedMessageBase() {}
+
+ virtual void Notify() = 0;
+};
+
+template <typename TObject, typename TData>
+class CRoutedMessageImpl : public CRoutedMessageBase
+{
+protected:
+ typedef void (TObject::*TMethod)(const TData &);
+
+public:
+ TObject *m_Object;
+ TMethod m_Method;
+ TData m_Data;
+
+ CRoutedMessageImpl(TObject *inObject, TMethod inMethod, const TData &inData)
+ : m_Object(inObject)
+ , m_Method(inMethod)
+ , m_Data(inData)
+ {
+ }
+
+ virtual void Notify() { (m_Object->*m_Method)(m_Data); }
+};
+
+//==============================================================================
+/**
+ * Routes user-defined messages between different threads of an application.
+ */
+class CMsgRouter : public QObject
+{
+protected:
+ class SMessageData;
+
+public:
+ typedef void (CMsgRouter::*TMainThreadMethod)(SMessageData *inMessageData);
+
+public:
+ static CMsgRouter *GetInstance();
+ virtual ~CMsgRouter();
+
+ void SendCommand(CCmd *inCommand, CCore *inCore);
+
+protected:
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
+ CMsgRouter(); ///< This is a singleton so the constructor is not public (call GetInstance)
+
+ class SMessageData : public QEvent
+ {
+ public:
+ SMessageData(int eventType);
+
+ TMainThreadMethod Method;
+ void *Data;
+ void *Data2;
+ };
+
+ void OnAsyncNotification(SMessageData *inMessageData);
+ void OnCommand(SMessageData *inMessageData);
+
+private:
+ int m_eventType;
+};
+
+#endif // INCLUDED_MSG_ROUTER_H
diff --git a/src/Authoring/Studio/Application/StudioConst.h b/src/Authoring/Studio/Application/StudioConst.h
new file mode 100644
index 00000000..6463cb7d
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioConst.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+// User Windows messages
+#ifndef WM_APP
+#define WM_APP 0x8000
+#endif
+#define WM_STUDIO_INITIALIZE_PALETTES (WM_APP + 1)
+#define WM_STUDIO_DUPLICATE_SERIAL (WM_APP + 2)
+#define WM_STUDIO_SHOWCOMPRESSION (WM_APP + 3)
+#define WM_STUDIO_LOADPROGRESS (WM_APP + 4)
+#define WM_STUDIO_NOTIFYLOADCOMPLETE (WM_APP + 5)
+#define WM_STUDIO_TIMER (WM_APP + 6)
+#define WM_STUDIO_MESSAGE_ROUTER (WM_APP + 7)
+#define WM_STUDIO_OPEN_RECENT_MIN (WM_APP + 9)
+#define WM_STUDIO_OPEN_RECENT_MAX (WM_APP + 19)
+#define WM_STUDIO_QUICKSTART_MIN (WM_APP + 31)
+#define WM_STUDIO_QUICKSTART_MAX (WM_APP + 44)
+#define WM_CUSTOMIZE_TOOLBAR (WM_APP + 45)
+
+enum EPreviewApplication { INTERNET_EXPLORER = 0, UIC_MEDIA_PLAYER, WINDOWS_MEDIA_PLAYER };
+
+// property pages
+const long PAGE_STUDIOAPPPREFERENCES = 0;
+const long PAGE_STUDIOPROJECTSETTINGS = 1;
+
+// Playhead moving with keyboard:
+const long SHIFT_PLAYHEAD_SMALL_TICK = 0;
+const long SHIFT_PLAYHEAD_LARGE_TICK = 1;
+
+// Used to reset default preferences
+const long PREFS_RESET_DEFAULTS = 999;
+
+// Message values
+#define STUDIO_PLAYHEADUPDATEONLY -1
+#define STUDIO_PLAYHEADSYNCPRESENTATION -2
+
+const unsigned long PARAMINDEX_RED = 0;
+const unsigned long PARAMINDEX_GREEN = 1;
+const unsigned long PARAMINDEX_BLUE = 2;
+const unsigned long PARAMINDEX_ALPHA = 3;
+
+const unsigned long PARAMINDEX_X = 0;
+const unsigned long PARAMINDEX_Y = 1;
+const unsigned long PARAMINDEX_Z = 2;
+
+// These are the new operation update masks
+namespace FILTERMASK {
+const long FILTER_MODELS = 0x02; // Mask for filtering out Models
+const long FILTER_IMAGES = 0x20; // Mask for filtering out Images
+const long FILTER_ALL = 0xff; // Used for adding all files back to display mode
+}
+
+// Used for timebar display - should we display Comments or Time Start/End/Duration information?
+const long TIMEBAR_COMMENTS = 1;
+const long TIMEBAR_TIMEINFO = 2;
+
+// Used to set the number of decimal places in the float dialog boxes.
+const long FLOAT_NUM_DECIMAL_PLACES = 3;
+
+// Default palette positions
+const long DEFAULT_MAINFRM_STARTWIDTH = 25;
+const long DEFAULT_MAINFRM_WIDTH = 50;
+const long DEFAULT_MAINFRM_STARTHEIGHT = 0;
+const long DEFAULT_MAINFRM_HEIGHT = 50;
+
+const long DEFAULT_INSPECTOR_STARTHEIGHT = 50;
+const long DEFAULT_INSPECTOR_HEIGHT = 50;
+const long DEFAULT_INSPECTOR_STARTWIDTH = 0;
+const long DEFAULT_INSPECTOR_WIDTH = 25;
+
+const long DEFAULT_LIBRARY_STARTHEIGHT = 0;
+const long DEFAULT_LIBRARY_HEIGHT = 50;
+const long DEFAULT_LIBRARY_STARTWIDTH = 0;
+const long DEFAULT_LIBRARY_WIDTH = 25;
+
+const long DEFAULT_TIMELINE_STARTHEIGHT = 50;
+const long DEFAULT_TIMELINE_HEIGHT = 50;
+const long DEFAULT_TIMELINE_STARTWIDTH = 25;
+const long DEFAULT_TIMELINE_WIDTH = 75;
+
+const long DEFAULT_HISTORY_STARTHEIGHT = 0;
+const long DEFAULT_HISTORY_HEIGHT = 50;
+const long DEFAULT_HISTORY_STARTWIDTH = 75;
+const long DEFAULT_HISTORY_WIDTH = 25;
+
+const long DEFAULT_STORAGE_STARTHEIGHT = 0;
+const long DEFAULT_STORAGE_HEIGHT = 50;
+const long DEFAULT_STORAGE_STARTWIDTH = 75;
+const long DEFAULT_STORAGE_WIDTH = 25;
+
+const long DEFAULT_HELP_STARTHEIGHT = 25;
+const long DEFAULT_HELP_HEIGHT = 50;
+const long DEFAULT_HELP_STARTWIDTH = 25;
+const long DEFAULT_HELP_WIDTH = 50;
+
+// Port number for serial number verification on the LAN
+const long SERIAL_NUMBER_PORT = 3133;
+const long SERIAL_NUMBER_BASE = 10;
+const long NUMBER_SERIAL_DIGITS_CHECK = 12;
+
+// Constants for AKTRACE statements
+// These are bitmasks - must be sequential and not overlap.
+const long TRACE_STUDIO_OBJECT = 0x0001;
+const long TRACE_TIMELINE = 0x0002;
+const long TRACE_KEYFRAMEDATA = 0x0004;
+const long TRACE_ANIMTRACKS = 0x0008;
+const long TRACE_UNDOREDO = 0x0010;
+const long TRACE_LIBRARY = 0x0020;
+const long TRACE_MSGHANDLER = 0x0040;
+const long TRACE_SNAPPING = 0x0080;
+const long TRACE_INSPECTOR = 0x0100;
+const long TRACE_CLIENT = 0x0200;
+const long TRACE_STORAGE = 0x0400;
+const long TRACE_TESTING = 0x0800;
+const long TRACE_HELPVIEW = 0x1000;
+const long TRACE_STUDIODOC = 0x2000;
+const long TRACE_MAINFRAME = 0x4000;
+const long TRACE_ALL = 0xffff;
+
+const char CLIENT_CORE_DLL_NAME[] = "AKCore.dll";
+
+// Timeline icon dimensions
+const long TREEICON_WIDTH = 14;
+const long TREEICON_HEIGHT = 14;
+const long TIMER_DRAGHOVEREXPAND = 2;
+const long TIMER_DRAGDROPSCROLL = 1;
+const long DRAGSCROLL_DIRECTION_UP = 1;
+const long DRAGSCROLL_DIRECTION_DOWN = 2;
+const long TIMER_DRAGDROPSCROLL_DELAY = 20;
+const long TIMER_DRAGHOVEREXPAND_DELAY = 1500; // 2000; // 3-second delay
diff --git a/src/Authoring/Studio/Application/StudioDefs.h b/src/Authoring/Studio/Application/StudioDefs.h
new file mode 100644
index 00000000..16465427
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioDefs.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 Anark Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef STUDIO_DEFINITIONS_H
+#define STUDIO_DEFINITIONS_H 1
+// NOTE: no #pragma once otherwise Mac will not compile
+
+//==============================================================================
+// Definitions
+//==============================================================================
+
+// Copyright date (year only) used through out the Studio program; must be a quoted string.
+#define STUDIO_COPYRIGHT_YEAR "2017"
+
+#endif // STUDIO_DEFINITIONS_H
diff --git a/src/Authoring/Studio/Application/StudioInstance.cpp b/src/Authoring/Studio/Application/StudioInstance.cpp
new file mode 100644
index 00000000..177e82c0
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioInstance.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "StudioInstance.h"
+#include "StudioPreferences.h"
+
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+CProductInstance *CStudioInstance::GenerateProductInstance()
+{
+ CProductInstance *theRetVal = new CProductInstance("UIComposer Studio");
+ // CProductInstance* theClient = new CProductInstance( "Client" );
+
+ // theRetVal->AddProduct( "AKPluWAV.dll" );
+ // theRetVal->AddProduct( "AKPluIMG.dll" );
+ // theRetVal->AddProduct( "AKPlu3DS.dll" );
+ // This section will soon be a call into the client dll
+ /*
+ theClient->AddProduct( "AKCore.dll" );
+ theClient->AddProduct( "AKCorENU.dll" );
+ theClient->AddProduct( "AKProENU.dll" );
+ theRetVal->AddInstance( theClient );
+ */
+
+ long theMajorVersion = 0;
+ long theMinorVersion = 0;
+ long theIterationNumber = 0;
+ long theBuildNumber = 0;
+ Q3DStudio::CString theUnparsedNumber;
+ Q3DStudio::CString theStudioVersion = CStudioPreferences::GetVersionString();
+
+ int theIndex = theStudioVersion.Find('.');
+ if (theIndex != Q3DStudio::CString::ENDOFSTRING) {
+ theUnparsedNumber = theStudioVersion.Extract(0, theIndex);
+ theMajorVersion = atoi(theUnparsedNumber.GetCharStar());
+ theStudioVersion = theStudioVersion.Extract(theIndex + 1);
+ }
+
+ theIndex = theStudioVersion.Find('.');
+ if (theIndex != Q3DStudio::CString::ENDOFSTRING) {
+ theUnparsedNumber = theStudioVersion.Extract(0, theIndex);
+ theMinorVersion = atoi(theUnparsedNumber.GetCharStar());
+ theStudioVersion = theStudioVersion.Extract(theIndex + 1);
+ }
+
+ theIndex = theStudioVersion.Find('.');
+ if (theIndex != Q3DStudio::CString::ENDOFSTRING) {
+ theUnparsedNumber = theStudioVersion.Extract(0, theIndex);
+ theIterationNumber = atoi(theUnparsedNumber.GetCharStar());
+ theStudioVersion = theStudioVersion.Extract(theIndex + 1);
+ }
+
+ theBuildNumber = atoi(theStudioVersion.GetCharStar());
+
+ theRetVal->AddProduct("Studio.exe", theMajorVersion, theMinorVersion, theIterationNumber,
+ theBuildNumber);
+ return theRetVal;
+}
diff --git a/src/Authoring/Studio/Application/StudioInstance.h b/src/Authoring/Studio/Application/StudioInstance.h
new file mode 100644
index 00000000..d2df3e24
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioInstance.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STUDIO_INSTANCE_H
+#define INCLUDED_STUDIO_INSTANCE_H 1
+
+#pragma once
+#include "ProductInstance.h"
+
+//==============================================================================
+/**
+ * @class CStudioInstance
+ * @brief Generates a product instance for studio.
+ * The product instance is essentially a set of modules and a name, and could have
+ * one or more product instances inside of it.
+ */
+class CStudioInstance
+{
+public:
+ static CProductInstance *GenerateProductInstance();
+};
+
+#endif // INCLUDED_STUDIO_INSTANCE_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Application/StudioTutorialWidget.cpp b/src/Authoring/Studio/Application/StudioTutorialWidget.cpp
new file mode 100644
index 00000000..d354d731
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioTutorialWidget.cpp
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "StudioTutorialWidget.h"
+#include "ui_StudioTutorialWidget.h"
+#include "StudioUtils.h"
+
+StudioTutorialWidget::StudioTutorialWidget(bool goToFileDialog) :
+ m_ui(new Ui::StudioTutorialWidget),
+ m_welcomeImages(0),
+ m_imgIter(0),
+ m_palette(0),
+ m_displayScale(1.0)
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->studioTutorialBack, &QPushButton::released, this,
+ &StudioTutorialWidget::handleBack);
+ connect(m_ui->studioTutorialForward, &QPushButton::released, this,
+ &StudioTutorialWidget::handleFwd);
+ connect(m_ui->studioTutorialShowAgain, &QCheckBox::stateChanged, this,
+ &StudioTutorialWidget::handleDoNotShowAgainChange);
+ connect(m_ui->studioTutorialNew, &QPushButton::released, this,
+ &StudioTutorialWidget::handleCreateNew);
+ connect(m_ui->studioTutorialOpen, &QPushButton::released, this,
+ &StudioTutorialWidget::handleOpenSample);
+
+ OnInitDialog(goToFileDialog);
+}
+
+StudioTutorialWidget::~StudioTutorialWidget()
+{
+ delete m_ui;
+ delete m_welcomeImages;
+ delete m_palette;
+}
+
+void StudioTutorialWidget::OnInitDialog(bool goToFileDialog)
+{
+ m_welcomeImages = new QList<QString>();
+
+ // populate welcome screen images
+ getImageList();
+ m_imgIter = m_welcomeImages->begin();
+
+ // do we go straight to last page with file dialog buttons?
+ int page = goToFileDialog ? m_welcomeImages->size() - 1 : 0;
+ // based on first PNG, get the scale that we need to fit welcome
+ // screen and buttons comfortably on display
+ m_displayScale = getDisplayScalingForImage(m_imgIter);
+
+ if (!m_welcomeImages->isEmpty()) {
+ for (int i = 0; i < page && m_imgIter != m_welcomeImages->end(); ++i)
+ m_imgIter++;
+
+ m_ui->studioTutorialShowAgain->setVisible(false);
+ if (*m_imgIter == m_welcomeImages->last() || m_imgIter == m_welcomeImages->end()) {
+ if (m_imgIter == m_welcomeImages->end())
+ m_imgIter--;
+ m_ui->studioTutorialForward->setVisible(false);
+ m_ui->studioTutorialOpen->setVisible(true);
+ m_ui->studioTutorialNew->setVisible(true);
+ } else {
+ if (m_imgIter == m_welcomeImages->begin()) {
+ m_ui->studioTutorialBack->setVisible(false);
+ m_ui->studioTutorialShowAgain->setVisible(true);
+ }
+ m_ui->studioTutorialOpen->setVisible(false);
+ m_ui->studioTutorialNew->setVisible(false);
+ }
+ }
+
+ QSettings settings;
+ m_ui->studioTutorialShowAgain->setChecked(!settings.value("showWelcomeScreen").toBool());
+
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+}
+
+void StudioTutorialWidget::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+ if (m_palette)
+ return;
+
+ m_palette = new QPalette;
+ QPixmap pic = getScaledPic(m_imgIter);
+ m_palette->setBrush(QPalette::Window, pic);
+ setPalette(*m_palette);
+
+ // assume all welcome screen images are sized the same
+ resize(pic.size());
+ setFixedSize(size());
+}
+
+void StudioTutorialWidget::handleFwd()
+{
+ if (*m_imgIter != m_welcomeImages->last()) {
+ QPixmap pic = getNextScaledPic();
+ m_palette->setBrush(QPalette::Window, pic);
+ setPalette(*m_palette);
+
+ m_ui->studioTutorialBack->setVisible(true);
+ m_ui->studioTutorialShowAgain->setVisible(false);
+ }
+
+ if (*m_imgIter == m_welcomeImages->last()) {
+ m_ui->studioTutorialForward->setVisible(false);
+ m_ui->studioTutorialOpen->setVisible(true);
+ m_ui->studioTutorialNew->setVisible(true);
+ }
+}
+
+void StudioTutorialWidget::handleBack()
+{
+ if (*m_imgIter != m_welcomeImages->first()) {
+ QPixmap pic = getPrevScaledPic();
+ m_palette->setBrush(QPalette::Window, pic);
+ setPalette(*m_palette);
+
+ m_ui->studioTutorialForward->setVisible(true);
+ m_ui->studioTutorialShowAgain->setVisible(false);
+
+ m_ui->studioTutorialOpen->setVisible(false);
+ m_ui->studioTutorialNew->setVisible(false);
+ }
+
+ if (*m_imgIter == m_welcomeImages->first()) {
+ m_ui->studioTutorialBack->setVisible(false);
+ m_ui->studioTutorialShowAgain->setVisible(true);
+ }
+}
+
+void StudioTutorialWidget::handleDoNotShowAgainChange(int state)
+{
+ QSettings settings;
+ const bool show = !(state == Qt::Checked);
+ settings.setValue("showWelcomeScreen", show);
+}
+
+void StudioTutorialWidget::handleOpenSample()
+{
+ this->done(StudioTutorialWidget::openSampleResult);
+}
+
+void StudioTutorialWidget::handleCreateNew()
+{
+ this->done(StudioTutorialWidget::createNewResult);
+}
+
+void StudioTutorialWidget::getImageList()
+{
+ QDirIterator *it = new QDirIterator(":/res/Tutorial/screens/",
+ QDirIterator::NoIteratorFlags);
+
+ while (it->hasNext())
+ m_welcomeImages->append(it->next());
+}
+
+int StudioTutorialWidget::page() const
+{
+ int i = 0;
+ QList<QString>::iterator iter = m_welcomeImages->begin();
+ while (iter != m_imgIter) { ++i; iter++; }
+ return i;
+}
+
+QPixmap StudioTutorialWidget::getNextScaledPic()
+{
+ return getScaledPic(++m_imgIter);
+}
+
+QPixmap StudioTutorialWidget::getPrevScaledPic()
+{
+ return getScaledPic(--m_imgIter);
+}
+
+QPixmap StudioTutorialWidget::getScaledPic(QList<QString>::iterator iter)
+{
+ QPixmap picOrig = QPixmap(*iter);
+ QPixmap pic = picOrig.scaledToHeight(m_displayScale * picOrig.height(),
+ Qt::SmoothTransformation);
+
+ pic.setDevicePixelRatio(devicePixelRatio());
+ return pic;
+}
+
+qreal StudioTutorialWidget::getDisplayScalingForImage(QList<QString>::iterator iter)
+{
+ QPixmap picOrig = QPixmap(*iter);
+
+ // Set the splash screen to 80% of display size.
+ // Note that high DPI scaling has an effect on the display
+ // resolution returned by GetAvailableDisplaySize().
+ // DPI scaling factor is integer and taken from the primary screen.
+ // When running studio on secondary monitor with different DPI,
+ // or running it on primary with non-integer scaling, we might
+ // get different dialog size than intended.
+ QSize displaySize = GetAvailableDisplaySize(getWidgetScreen(this)) * 0.8;
+
+ // scale down if images do not fit on screen, otherwise use
+ // 1:1 PNGs to avoid scaling artifacts
+ if (picOrig.height() > displaySize.height() ||
+ picOrig.width() > displaySize.width()) {
+ QSize picScaledSize = picOrig.size();
+ picScaledSize.scale(displaySize, Qt::KeepAspectRatio);
+ m_displayScale = qMin((qreal)picScaledSize.height() / (qreal)picOrig.height(),
+ (qreal)picScaledSize.width() / (qreal)picOrig.width());
+ return m_displayScale;
+ } else {
+ return 1.0;
+ }
+}
diff --git a/src/Authoring/Studio/Application/StudioTutorialWidget.h b/src/Authoring/Studio/Application/StudioTutorialWidget.h
new file mode 100644
index 00000000..3fa69489
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioTutorialWidget.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef STUDIOTUTORIALWIDGET_H
+#define STUDIOTUTORIALWIDGET_H
+
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qsettings.h>
+#include <QtGui/qpixmap.h>
+#include <QtGui/qpalette.h>
+#include <QtWidgets/qdialog.h>
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class StudioTutorialWidget;
+}
+QT_END_NAMESPACE
+
+class StudioTutorialWidget : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit StudioTutorialWidget(bool goToFileDialog);
+
+ ~StudioTutorialWidget();
+
+ enum result {
+ acceptResult = QDialog::Accepted,
+ rejectResult = QDialog::Rejected,
+ openSampleResult,
+ createNewResult
+ };
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ void OnInitDialog(bool goToFileDialog);
+
+public:
+ void handleFwd();
+ void handleBack();
+ void handleDoNotShowAgainChange(int state);
+ void handleOpenSample();
+ void handleCreateNew();
+ int page() const;
+
+private:
+ Ui::StudioTutorialWidget *m_ui;
+
+ QList<QString> *m_welcomeImages;
+ QList<QString>::iterator m_imgIter;
+
+ QPalette *m_palette;
+
+ qreal m_displayScale;
+
+ void getImageList();
+
+ QPixmap getScaledPic(QList<QString>::iterator iter);
+ QPixmap getPrevScaledPic();
+ QPixmap getNextScaledPic();
+ qreal getDisplayScalingForImage(QList<QString>::iterator iter);
+};
+
+#endif // STUDIOTUTORIALWIDGET_H
diff --git a/src/Authoring/Studio/Application/StudioTutorialWidget.ui b/src/Authoring/Studio/Application/StudioTutorialWidget.ui
new file mode 100644
index 00000000..cb83a5ce
--- /dev/null
+++ b/src/Authoring/Studio/Application/StudioTutorialWidget.ui
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StudioTutorialWidget</class>
+ <widget class="QDialog" name="StudioTutorialWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1440</width>
+ <height>900</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Welcome to Qt 3D Studio</string>
+ </property>
+ <property name="accessibleName">
+ <string>StudioTutorialWidget</string>
+ </property>
+ <widget class="QCheckBox" name="studioTutorialShowAgain">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>701</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="accessibleName">
+ <string>studioTutorialShowAgain</string>
+ </property>
+ <property name="text">
+ <string>Do Not Show This Again</string>
+ </property>
+ </widget>
+ <widget class="QWidget" name="horizontalLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>820</y>
+ <width>1421</width>
+ <height>80</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="bottomBar">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="buttons">
+ <item>
+ <widget class="QPushButton" name="studioTutorialOpen">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="accessibleName">
+ <string>studioTutorialOpen</string>
+ </property>
+ <property name="text">
+ <string>Open Sample Project</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="studioTutorialNew">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="accessibleName">
+ <string>studioTutorialNew</string>
+ </property>
+ <property name="text">
+ <string>Create New</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="horizontalLayoutWidget_2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>40</y>
+ <width>1441</width>
+ <height>51</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="topBar">
+ <item>
+ <widget class="QPushButton" name="studioTutorialBack">
+ <property name="maximumSize">
+ <size>
+ <width>90</width>
+ <height>90</height>
+ </size>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="accessibleName">
+ <string>studioTutorialBack</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../qt3dstudio.qrc">
+ <normaloff>:/res/Tutorial/button_back.png</normaloff>:/res/Tutorial/button_back.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>39</width>
+ <height>39</height>
+ </size>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="studioTutorialForward">
+ <property name="maximumSize">
+ <size>
+ <width>90</width>
+ <height>90</height>
+ </size>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="accessibleName">
+ <string>StudioTutorialWidget</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../qt3dstudio.qrc">
+ <normaloff>:/res/Tutorial/button_next.png</normaloff>:/res/Tutorial/button_next.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>39</width>
+ <height>39</height>
+ </size>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources>
+ <include location="../qt3dstudio.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/Controls/BaseMeasure.cpp b/src/Authoring/Studio/Controls/BaseMeasure.cpp
new file mode 100644
index 00000000..25d5c204
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BaseMeasure.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "BaseMeasure.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Create a new measure.
+ * @param inRatio the current ratio.
+ * @param inFillBackground true if the background of this control is to be drawn.
+ */
+CBaseMeasure::CBaseMeasure(double inRatio, bool inFillBackground /*= true */)
+ : m_SmallHashInterval(0)
+ , m_MediumHashInterval(0)
+ , m_LargeHashInterval(0)
+ , m_Ratio(inRatio)
+ , m_Offset(0)
+ , m_EdgeMargin(0)
+ , m_LargeHashOffset(0)
+ , m_MediumHashOffset(6)
+ , m_SmallHashOffset(3)
+ , m_FillBackground(inFillBackground)
+{
+ m_BackgroundColor = CStudioPreferences::GetBaseColor();
+}
+
+CBaseMeasure::~CBaseMeasure()
+{
+}
+
+//=============================================================================
+/**
+ * Overrides the control to set up clipping rects.
+ * @param inRenderer the renderer to be rendered to.
+ * @param inDirtyRect the resulting dirty rect.
+ * @param inIgnoreValidation true if everything needs to be drawn.
+ */
+void CBaseMeasure::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ inRenderer->PushClippingRect(CRct(GetSize()));
+
+ CControl::OnDraw(inRenderer, inDirtyRect, inIgnoreValidation);
+
+ inRenderer->PopClippingRect();
+}
+
+//=============================================================================
+/**
+ * Draw the measure.
+ * @param inRenderer the renderer to draw to.
+ */
+void CBaseMeasure::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ if (m_FillBackground)
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+
+ long theLength = GetDisplayLength();
+ long theHeight = GetDisplayHeight();
+
+ double theTotalMeasure = theLength / m_Ratio;
+ long thePos;
+
+ long theNumLargeHashes = (long)(theTotalMeasure / m_LargeHashInterval) + 1;
+
+ inRenderer->PushPen(CStudioPreferences::GetRulerTickColor());
+
+ long theOffset = m_Offset - (m_Offset % ::dtol(m_LargeHashInterval));
+
+ for (long i = 0; i < theNumLargeHashes + 1; ++i) {
+ double theMeasure = m_LargeHashInterval * i + theOffset;
+
+ thePos = CalculatePos(theMeasure - m_Offset);
+
+ if (thePos > 0)
+ DrawLine(inRenderer, thePos, theHeight, theHeight - m_LargeHashOffset);
+
+ DrawMeasureText(inRenderer, thePos, long(theMeasure));
+
+ // sanity check
+ if (m_MediumHashInterval > 0) {
+ // in cases where the medium and small hashes must be filled up to the right of the
+ // first Large hash
+ if (m_Offset < 0 && i == 0) {
+ thePos = CalculatePos(theMeasure - m_Offset - m_MediumHashInterval);
+ if (thePos > 0)
+ DrawLine(inRenderer, thePos, theHeight, theHeight - m_MediumHashOffset);
+
+ for (double theSmallInterval = 0; theSmallInterval < m_LargeHashInterval;
+ theSmallInterval += m_SmallHashInterval) {
+ thePos = CalculatePos(theMeasure - m_Offset - theSmallInterval);
+ if (thePos > 0)
+ DrawLine(inRenderer, thePos, theHeight, theHeight - m_SmallHashOffset);
+ }
+ }
+
+ thePos = CalculatePos(theMeasure - m_Offset + m_MediumHashInterval);
+ if (thePos > 0)
+ DrawLine(inRenderer, thePos, theHeight, theHeight - m_MediumHashOffset);
+
+ for (double theSmallInterval = 0; theSmallInterval < m_LargeHashInterval;
+ theSmallInterval += m_SmallHashInterval) {
+ thePos = CalculatePos(theMeasure - m_Offset + theSmallInterval);
+
+ if (thePos > 0)
+ DrawLine(inRenderer, thePos, theHeight, theHeight - m_SmallHashOffset);
+ }
+ } // if medium is valid
+ }
+ // Draws the top outline
+ DrawOutline(inRenderer, theHeight, 0, theLength);
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ // Draws the bottom outline
+ DrawOutline(inRenderer, theHeight + 1, 0, theLength);
+ inRenderer->PopPen();
+
+ // Dark line at left and right ends of the measure
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ DrawLine(inRenderer, theRect.size.x - 1, 0, theRect.size.y - 1);
+
+ // Dark line across the top of the measure
+ DrawOutline(inRenderer, 0, 0, theRect.size.x - 1);
+ inRenderer->PopPen();
+
+ // pop for the first PushPen
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Return the display length, by default, this is horizontal and returns the x
+ */
+long CBaseMeasure::GetDisplayLength()
+{
+ return GetSize().x;
+}
+
+//=============================================================================
+/**
+ * Return the display height, by default, this is horizontal and returns the y
+ */
+long CBaseMeasure::GetDisplayHeight()
+{
+ return GetSize().y - m_EdgeMargin;
+}
+
+//=============================================================================
+/**
+ * By default, this draws a vertical line.
+ * A subclass can override this and draw a horizontal one, if its orientation requires so.
+ * @parma inRenderer the renderer
+ * @param inPos the position that in the x-coordinate
+ * @param inStart start position of the line
+ * @param inEnd end position of the line
+ */
+void CBaseMeasure::DrawLine(CRenderer *inRenderer, long inPos, long inStart, long inEnd)
+{
+ inRenderer->MoveTo(CPt(inPos, inStart));
+ inRenderer->LineTo(CPt(inPos, inEnd));
+}
+
+//=============================================================================
+/**
+ * This draws a horizontal outline
+ * A subclass can override this and draw a vertical one, if its orientation requires so.
+ * @parma inRenderer the renderer
+ * @param inPos the position that in the x-coordinate
+ * @param inStart start position of the line
+ * @param inEnd end position of the line
+ */
+void CBaseMeasure::DrawOutline(CRenderer *inRenderer, long inPos, long inStart, long inEnd)
+{
+ inRenderer->MoveTo(CPt(inStart, inPos));
+ inRenderer->LineTo(CPt(inEnd, inPos));
+}
diff --git a/src/Authoring/Studio/Controls/BaseMeasure.h b/src/Authoring/Studio/Controls/BaseMeasure.h
new file mode 100644
index 00000000..25916198
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BaseMeasure.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BASE_MEASURE_H
+#define INCLUDED_BASE_MEASURE_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+#include "CoreUtils.h"
+#include <vector>
+
+class CBaseMarker;
+class CSnapper;
+
+class CBaseMeasure : public CControl
+{
+public:
+ CBaseMeasure(double inRatio, bool inFillBackground = true);
+ virtual ~CBaseMeasure();
+
+ virtual void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false);
+
+ virtual void Draw(CRenderer *inRenderer);
+
+ // inline these
+ long GetLargeHashInterval() const { return ::dtol(m_LargeHashInterval); }
+ long GetMediumHashInterval() const { return ::dtol(m_MediumHashInterval); }
+ long GetSmallHashInterval() const { return ::dtol(m_SmallHashInterval); }
+
+protected:
+ virtual void DrawMeasureText(CRenderer *inRenderer, long inPosition, long inMeasure) = 0;
+ virtual long CalculatePos(double inNewValue) = 0;
+ virtual long GetDisplayLength();
+ virtual long GetDisplayHeight();
+ virtual void DrawLine(CRenderer *inRenderer, long inPos, long inStart, long inEnd);
+ virtual void DrawOutline(CRenderer *inRenderer, long inPos, long inStart, long inEnd);
+
+ double m_SmallHashInterval; ///< the measurement represented by a small hash
+ double m_MediumHashInterval; ///< the measurement represented by a medium hash
+ double m_LargeHashInterval; ///< the measurement represented by a large hash
+ double m_Ratio; ///< Ratio that is used to calculate the size of the large hash
+ long m_Offset; ///< Offset of the point 0 from left or top
+ ::CColor m_BackgroundColor; ///< Background of this control
+
+ // Tickmarks
+ long m_EdgeMargin; ///< Margin from the edge, so that no line draws all the way through
+ long m_LargeHashOffset; ///< Offset from the margin for the large hash
+ long m_MediumHashOffset; ///< Offset from the margin for the medium hash
+ long m_SmallHashOffset; ///< Offset from the margin for the small hash
+
+ bool m_FillBackground; ///< true to fill the background (i.e. its not transparent)
+};
+#endif // INCLUDED_BASE_MEASURE_H
diff --git a/src/Authoring/Studio/Controls/BlankControl.cpp b/src/Authoring/Studio/Controls/BlankControl.cpp
new file mode 100644
index 00000000..492bc630
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BlankControl.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+#include "Renderer.h"
+#include "SystemPreferences.h"
+
+//=============================================================================
+/**
+ * constructor
+ */
+CBlankControl::CBlankControl(CColor inColor)
+ : m_DrawBorder(false)
+ , m_FillBackground(true)
+{
+ SetColor(inColor);
+}
+
+//=============================================================================
+/**
+ * destructor
+ */
+CBlankControl::~CBlankControl()
+{
+}
+
+//=============================================================================
+/**
+ * Draws this control
+ */
+void CBlankControl::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ if (m_FillBackground)
+ inRenderer->FillSolidRect(theRect, m_Color);
+
+ if (m_DrawBorder) {
+ // Get the normal colors for the 3D border
+ CColor theShadowColor = CStudioPreferences::GetButtonShadowColor();
+ CColor theHiliteColor = CStudioPreferences::GetButtonHighlightColor();
+
+ // Draw a frame around the button
+ inRenderer->Draw3dRect(
+ CRct(theRect.position.x + 1, theRect.position.y, theRect.size.x, theRect.size.y),
+ theHiliteColor, theShadowColor);
+ inRenderer->PushPen(theShadowColor);
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theRect.size.y));
+ inRenderer->PopPen();
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the color for this control
+ */
+void CBlankControl::SetColor(CColor inColor)
+{
+ if (m_Color == inColor)
+ return;
+
+ m_Color = inColor;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * @param inDrawBorder true if we should draw the border
+ */
+void CBlankControl::SetDrawBorder(bool inDrawBorder)
+{
+ m_DrawBorder = inDrawBorder;
+}
+
+//=============================================================================
+/**
+ * @param inFillBackground true if we should draw the background
+ */
+void CBlankControl::SetFillBackground(bool inFillBackground)
+{
+ m_FillBackground = inFillBackground;
+}
diff --git a/src/Authoring/Studio/Controls/BlankControl.h b/src/Authoring/Studio/Controls/BlankControl.h
new file mode 100644
index 00000000..b04337b0
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BlankControl.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BLANKCONTROL_H
+#define INCLUDED_BLANKCONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "StudioPreferences.h"
+
+class CBlankControl : public CControl
+{
+public:
+ CBlankControl(::CColor inColor = CStudioPreferences::GetBaseColor());
+ virtual ~CBlankControl();
+ void Draw(CRenderer *inRenderer) override;
+ void SetColor(::CColor inColor);
+ void SetDrawBorder(bool inDrawBorder);
+ void SetFillBackground(bool inFillBackground);
+
+protected:
+ ::CColor m_Color;
+ bool m_DrawBorder;
+ bool m_FillBackground;
+};
+
+#endif // INCLUDED_BLANKCONTROL_H
diff --git a/src/Authoring/Studio/Controls/BreadCrumbControl.cpp b/src/Authoring/Studio/Controls/BreadCrumbControl.cpp
new file mode 100644
index 00000000..5ccae438
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BreadCrumbControl.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2004 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BreadCrumbControl.h"
+#include "StudioPreferences.h"
+#include "IBreadCrumbProvider.h"
+//#include "Cmd.h"
+#include <boost/bind.hpp>
+
+#include <QPixmap>
+
+IMPLEMENT_OBJECT_COUNTER(CBreadCrumbControl)
+
+//==============================================================================
+// Internal Classes
+//==============================================================================
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CBreadCrumbControl::CBreadCrumbControl()
+ : m_BreadCrumbProvider(nullptr)
+{
+ ADDTO_OBJECT_COUNTER(CBreadCrumbControl)
+
+ SetName("BreadCrumbControl");
+ SetAutoMin(false);
+ SetFlowDirection(FLOW_HORIZONTAL);
+ SetAlignment(ALIGN_TOP, ALIGN_LEFT);
+ SetTopMargin(0);
+ SetBottomMargin(0);
+ SetLeftMargin(0);
+ SetRightMargin(0);
+ SetGapBetweenChildren(0);
+
+ long theHeaderHeight = CStudioPreferences::GetHeaderHeight();
+ SetMaximumSize(CPt(LONG_MAX, theHeaderHeight));
+ SetMinimumSize(CPt(0, theHeaderHeight));
+ SetSize(CPt(LONG_MAX, theHeaderHeight));
+ SetPosition(CPt(0, 0));
+}
+
+//=============================================================================
+/**
+ * destructor
+ */
+CBreadCrumbControl::~CBreadCrumbControl()
+{
+ RemoveAllButtons();
+ REMOVEFROM_OBJECT_COUNTER(CBreadCrumbControl)
+}
+
+//=============================================================================
+/**
+ * Helper class to create the colon separator button
+ */
+static inline CBreadCrumbControl::TSeparatorButtonType *CreateSeparatorButton(const QPixmap &inImage)
+{
+ CBreadCrumbControl::TSeparatorButtonType *theButton =
+ new CBreadCrumbControl::TSeparatorButtonType;
+ theButton->SetAutoSize(true);
+ theButton->SetUpImage(inImage);
+ theButton->SetSize(CPt(12, 21));
+ theButton->SetMinimumSize(CPt(12, 21));
+ theButton->SetMaximumSize(CPt(12, 21));
+ return theButton;
+}
+
+//=============================================================================
+/**
+ * Helper function to create a new button to be placed on the breadcrumb bar
+ * @param inButtonName Name of the button to be displayed and stored
+ * inImageName The specific .png file to load in for the button
+ * @return TButtonType returns the newly created button
+ */
+static inline CBreadCrumbControl::TButtonType *CreateButton(const QPixmap &inImage)
+{
+ CBreadCrumbControl::TButtonType *theButton = new CBreadCrumbControl::TButtonType();
+ theButton->SetAutoSize(true);
+ theButton->SetUpImage(inImage);
+ CBreadCrumbControl::TButtonType::SBorderOptions theBorderOptions(false, false, false, false);
+ theButton->SetBorderVisibilityAll(theBorderOptions);
+ theButton->SetFillStyleUp(CBreadCrumbControl::TButtonType::EFILLSTYLE_FLOOD);
+ theButton->SetFillStyleOver(CBreadCrumbControl::TButtonType::EFILLSTYLE_FLOOD);
+ theButton->SetFillColorUp(CStudioPreferences::GetBaseColor());
+ theButton->SetFillColorDown(CStudioPreferences::GetBaseColor());
+ theButton->SetFillColorOver(CStudioPreferences::GetMouseOverHighlightColor());
+ theButton->SetMinimumSize(CPt(16, 21));
+ theButton->SetMaximumSize(CPt(125, 21));
+ return theButton;
+}
+
+//=============================================================================
+/**
+ * Generate/Reset the text appearing on the breadcrumb which is currently
+ * appending the slide name as well.
+ * @param inBreadCrumb containing color/text info for display
+ * @param inButton The button to be modified
+ */
+void CBreadCrumbControl::GenerateButtonText(const SBreadCrumb &inBreadCrumb, TButtonType *inButton)
+{
+ Q3DStudio::CString theDisplayName(Q3DStudio::CString::fromQString(inBreadCrumb.m_String));
+ inButton->SetTextColorUp(CStudioPreferences::GetNormalColor());
+ inButton->SetTextColorDown(CStudioPreferences::GetNormalColor());
+ inButton->SetText(theDisplayName);
+ inButton->SetTooltipText(theDisplayName);
+ inButton->SetName(theDisplayName);
+}
+
+//=============================================================================
+/**
+ * Draws this control
+ */
+void CBreadCrumbControl::Draw(CRenderer *inRenderer)
+{
+ const auto size = GetSize();
+
+ // Fill in the background
+ inRenderer->FillSolidRect(QRect(0, 0, size.x, size.y), CStudioPreferences::GetBaseColor());
+
+ // Outline the control
+ CColor theOutlineColor = CStudioPreferences::GetButtonShadowColor();
+ inRenderer->DrawRectOutline(QRect(0, 0, size.x, size.y -1), theOutlineColor, theOutlineColor,
+ theOutlineColor, theOutlineColor);
+
+ // Draw the highlight at the bottom
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(QPoint(0, size.y - 1));
+ inRenderer->LineTo(QPoint(size.x - 1, size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Update the entire bread crumb trail. This will 'reuse' created controls, creating more if
+ *trail is longer than previous, and deleting extra if shorter.
+ */
+void CBreadCrumbControl::RefreshTrail(IBreadCrumbProvider *inBreadCrumbProvider)
+{
+ bool theChangedProvider = m_BreadCrumbProvider != inBreadCrumbProvider;
+ if (theChangedProvider && m_BreadCrumbProvider)
+ m_BreadCrumbProvider->SigBreadCrumbUpdate.disconnect(boost::bind(&CBreadCrumbControl::OnUpdateBreadCrumb, this));
+
+ m_BreadCrumbProvider = inBreadCrumbProvider;
+
+ SuspendRecalcLayout(true);
+ size_t theIndex = 0;
+
+ if (m_BreadCrumbProvider) {
+ // listen for single breadcrumb update (i.e. name changes)
+ if (theChangedProvider)
+ m_BreadCrumbProvider->SigBreadCrumbUpdate.connect(boost::bind(&CBreadCrumbControl::OnUpdateBreadCrumb, this));
+
+ const IBreadCrumbProvider::TTrailList &theList = m_BreadCrumbProvider->GetTrail();
+ // By design, if this is only 1 item in the list, nothing is shown.
+ if (theList.size() > 1) {
+ for (; theIndex < theList.size(); ++theIndex) {
+ TButtonType *theButton = nullptr;
+ if (theIndex < m_BreadCrumbList.size())
+ theButton = m_BreadCrumbList[theIndex].m_BreadCrumb;
+ else // create new
+ {
+ theButton =
+ CreateButton((theIndex == 0) ? m_BreadCrumbProvider->GetRootImage()
+ : m_BreadCrumbProvider->GetBreadCrumbImage());
+ theButton->SigToggle.connect(boost::bind(&CBreadCrumbControl::OnButtonToggled, this, _1, _2));
+
+ TSeparatorButtonType *theSeparator =
+ CreateSeparatorButton(m_BreadCrumbProvider->GetSeparatorImage());
+
+ // the ":" is always added as a pair
+ m_BreadCrumbList.push_back(SBreadCrumbItem(theButton, theSeparator));
+ AddChild(theButton);
+ AddChild(theSeparator);
+ }
+ // Update text and color
+ GenerateButtonText(theList[theIndex], theButton);
+
+ // Refresh all properties that might be outdated since buttons are reused
+ QPixmap theImage;
+ bool theEnabled = true;
+ if (theIndex
+ == theList.size()
+ - 1) { // The last button is displayed differently (grayed-out)
+ theButton->SetTextColorUp(CColor(94, 91, 85));
+ theButton->SetTextColorDown(CColor(94, 91, 85));
+ theImage = m_BreadCrumbProvider->GetActiveBreadCrumbImage();
+
+ theEnabled = false; // the last item being the 'active' is disabled.
+ } else if (theIndex > 0)
+ theImage = m_BreadCrumbProvider->GetBreadCrumbImage();
+
+ if (!theImage.isNull())
+ theButton->SetUpImage(theImage);
+
+ theButton->SetToggleState(!theEnabled);
+ theButton->SetEnabled(theEnabled);
+ // the separator is hidden for the last one
+ m_BreadCrumbList[theIndex].m_Separator->SetVisible(theEnabled);
+
+ theButton->Invalidate();
+ }
+ }
+ }
+ // Discard 'extras' OR if this is no provider, this will remove all breadcrumbs
+ while (m_BreadCrumbList.size() > theIndex) {
+ RemoveButton(m_BreadCrumbList.back());
+ m_BreadCrumbList.pop_back();
+ }
+ SuspendRecalcLayout(false);
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Check for changes in the existing list and update.
+ */
+void CBreadCrumbControl::OnUpdateBreadCrumb()
+{
+ ASSERT(m_BreadCrumbProvider);
+ bool theUpdated = false;
+
+ const IBreadCrumbProvider::TTrailList &theList = m_BreadCrumbProvider->GetTrail(false);
+ ASSERT(m_BreadCrumbList.size() == theList.size()
+ || (m_BreadCrumbList.size() == 0
+ && theList.size()
+ == 1)); // By design, if this is only 1 item in the list, nothing is shown.
+
+ for (size_t theIndex = 0; theIndex < m_BreadCrumbList.size(); ++theIndex) {
+ TButtonType *theButton = m_BreadCrumbList[theIndex].m_BreadCrumb;
+ if (theButton->GetText().toQString() != theList[theIndex].m_String
+ || theButton->GetTextColorUp().getQColor() != theList[theIndex].m_Color) {
+ GenerateButtonText(theList[theIndex], theButton);
+ theUpdated = true;
+ }
+ }
+
+ if (theUpdated) // Make sure that this entire BreadCrumbControl has room for extraneous length
+ // buttons
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Removes and clean up button
+ */
+void CBreadCrumbControl::RemoveButton(SBreadCrumbItem &inBreadCrumb)
+{
+ inBreadCrumb.m_BreadCrumb->SigToggle.disconnect(boost::bind(&CBreadCrumbControl::OnButtonToggled, this, _1, _2));
+ RemoveChild(inBreadCrumb.m_BreadCrumb);
+ RemoveChild(inBreadCrumb.m_Separator);
+ delete inBreadCrumb.m_BreadCrumb;
+ delete inBreadCrumb.m_Separator;
+}
+
+//=============================================================================
+/**
+ * Removes all buttons (trail and separators)
+ */
+void CBreadCrumbControl::RemoveAllButtons()
+{
+ while (!m_BreadCrumbList.empty()) {
+ RemoveButton(m_BreadCrumbList.back());
+ m_BreadCrumbList.pop_back();
+ }
+}
+
+//=============================================================================
+/**
+ * Handles turning a filter on or off in response to a button being pressed.
+ * @param inButton button that generated the event
+ * @param inState new state of the button after being toggled
+ */
+void CBreadCrumbControl::OnButtonToggled(CToggleButton *inButton,
+ CButtonControl::EButtonState inState)
+{
+ Q_UNUSED(inState);
+
+ ASSERT(m_BreadCrumbProvider);
+
+ // Find the index into the trail list
+ size_t theIndex = 0;
+ for (; theIndex < m_BreadCrumbList.size(); ++theIndex) {
+ if (m_BreadCrumbList[theIndex].m_BreadCrumb == inButton)
+ break;
+ }
+ ASSERT(theIndex < m_BreadCrumbList.size()); // sanity check
+
+ m_BreadCrumbProvider->OnBreadCrumbClicked((long)theIndex);
+}
diff --git a/src/Authoring/Studio/Controls/BreadCrumbControl.h b/src/Authoring/Studio/Controls/BreadCrumbControl.h
new file mode 100644
index 00000000..c8fc519a
--- /dev/null
+++ b/src/Authoring/Studio/Controls/BreadCrumbControl.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2004 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BREADCRUMBCONTROL_H
+#define INCLUDED_BREADCRUMBCONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FlowLayout.h"
+#include "ProceduralButton.h"
+#include "ToggleButton.h"
+#include "TextButton.h"
+#include "IBreadCrumbProvider.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class IDoc;
+class CRenderer;
+
+class CBreadCrumbControl : public CFlowLayout
+{
+public:
+ typedef CProceduralButton<CTextButton<CToggleButton>> TButtonType;
+ typedef CTextButton<CButtonControl> TSeparatorButtonType;
+ struct SBreadCrumbItem
+ {
+ TButtonType *m_BreadCrumb;
+ TSeparatorButtonType *m_Separator;
+
+ SBreadCrumbItem(TButtonType *inBreadCrumb, TSeparatorButtonType *inSeparator)
+ {
+ m_BreadCrumb = inBreadCrumb;
+ m_Separator = inSeparator;
+ }
+ };
+ typedef std::vector<SBreadCrumbItem> TBreadCrumbList;
+
+ // Construction
+public:
+ CBreadCrumbControl();
+ virtual ~CBreadCrumbControl();
+
+ DEFINE_OBJECT_COUNTER(CBreadCrumbControl)
+
+ // CControl
+public:
+ virtual void Draw(CRenderer *inRenderer);
+
+ void RefreshTrail(IBreadCrumbProvider *inBreadCrumbProvider);
+ void OnUpdateBreadCrumb();
+
+ // Implementation
+protected:
+ void GenerateButtonText(const SBreadCrumb &inBreadCrumb, TButtonType *inButton);
+ void OnButtonToggled(CToggleButton *inButton, CButtonControl::EButtonState inState);
+ void RemoveButton(SBreadCrumbItem &inBreadCrumb);
+ void RemoveAllButtons();
+
+protected:
+ IBreadCrumbProvider *m_BreadCrumbProvider;
+ TBreadCrumbList m_BreadCrumbList;
+};
+
+#endif // INCLUDED_BREADCRUMBCONTROL_H
diff --git a/src/Authoring/Studio/Controls/ButtonControl.cpp b/src/Authoring/Studio/Controls/ButtonControl.cpp
new file mode 100644
index 00000000..c242915a
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ButtonControl.cpp
@@ -0,0 +1,425 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ButtonControl.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+
+//=============================================================================
+/**
+ * Constructor
+ *
+ * @param inToggleButton true if this is suppose to be a toggle button. Toggle
+ * buttons stay pressed when clicked on and have to be clicked again in order
+ * to release. Normal buttons return to the up state when the button is released.
+ */
+CButtonControl::CButtonControl()
+ : m_State(EBUTTONSTATE_UP)
+ , m_AutoSize(true)
+ , m_MouseEnter(true)
+ , m_IsMouseDown(false)
+ , m_VCenterImage(false)
+ , m_HCenterImage(false)
+ , m_Margin(0)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CButtonControl::~CButtonControl()
+{
+}
+
+//=============================================================================
+/**
+ * Returns the current state of the button. A button can be "up", "down", or
+ * "indeterminate". The up state is the normal state of the button. The down
+ * state is when the mouse is pressing the button. The indeterminate state is
+ * provided for subclasses and not used here.
+ * @return the button's current state
+ */
+CButtonControl::EButtonState CButtonControl::GetButtonState() const
+{
+ return m_State;
+}
+
+//=============================================================================
+/**
+ * Sets the current state of the button. No listeners are notified.
+ * @param inState new state of the button
+ */
+void CButtonControl::SetButtonState(EButtonState inState)
+{
+ if (m_State == inState)
+ return;
+
+ m_State = inState;
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is in the up state. A button is "up"
+ * if it is not disabled, not depressed, and the mouse is not over it.
+ * @param inImage image to be loaded when this button is up
+ */
+void CButtonControl::SetUpImage(const QPixmap &inImage)
+{
+ m_ImageUp = inImage;
+ Resize();
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is in the up state. A button is "up"
+ * if it is not disabled, not depressed, and the mouse is not over it.
+ * @param inImageName name of the image to be loaded when this button is up
+ */
+void CButtonControl::SetUpImage(const QString &inImageName)
+{
+ QPixmap theImage ;
+ if (!inImageName.isEmpty())
+ theImage = CResourceCache::GetInstance()->GetBitmap(inImageName);
+ SetUpImage(theImage);
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is in the down state.
+ * @param inImage image to be loaded when this button is down
+ */
+void CButtonControl::SetDownImage(const QPixmap &inImage)
+{
+ m_ImageDown = inImage;
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is in the down state.
+ * @param inImageName name of the image to be loaded when this button is down
+ */
+void CButtonControl::SetDownImage(const QString &inImageName)
+{
+ QPixmap theImage;
+ if (!inImageName.isEmpty())
+ theImage = CResourceCache::GetInstance()->GetBitmap(inImageName);
+ SetDownImage(theImage);
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button the mouse is over the button. To
+ * leave the button displaying the "up" state, pass NULL to this function.
+ * @param inImage image to be loaded when the mouse is over this button
+ */
+void CButtonControl::SetOverImage(const QPixmap &inImage)
+{
+ m_ImageOver = inImage;
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button the mouse is over the button. To
+ * leave the button displaying the "up" state, pass an empty string to this function.
+ * @param inImageName name of the image to be loaded when the mouse is over this button
+ */
+void CButtonControl::SetOverImage(const QString &inImageName)
+{
+ QPixmap theImage;
+ if (!inImageName.isEmpty())
+ theImage = CResourceCache::GetInstance()->GetBitmap(inImageName);
+ SetOverImage(theImage);
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is disabled, when the button is in a DOWN state
+ * @param inImage image to be loaded when this button is disabled
+ */
+void CButtonControl::SetDisabledImage(const QPixmap &inImage)
+{
+ m_ImageDownDisabled = inImage;
+}
+
+//=============================================================================
+/**
+ * Sets the image to use when this button is in the disabled state.
+ * @param inImageName name of the image to be loaded when this button is disabled
+ */
+void CButtonControl::SetDisabledImage(const QString &inImageName)
+{
+ QPixmap theImage;
+ if (!inImageName.isEmpty())
+ theImage = CResourceCache::GetInstance()->GetBitmap(inImageName);
+ SetDisabledImage(theImage);
+}
+
+//=============================================================================
+/**
+ * In cases, where there is a different (disabled) image for button UP state
+ */
+void CButtonControl::SetUpDisabledImage(const QString &inImageName)
+{
+ if (!inImageName.isEmpty())
+ m_ImageUpDisabled = CResourceCache::GetInstance()->GetBitmap(inImageName);
+}
+
+//=============================================================================
+/**
+ * Allows you to enable or disable auto sizing for this button. Auto sizing
+ * causes the button's size to change whenever the "Up" image is changed for
+ * this button. Button size does NOT change as the button state changes.
+ * @return true if this button is currently in Auto Size mode
+ */
+void CButtonControl::SetAutoSize(bool inEnableAutoSize)
+{
+ m_AutoSize = inEnableAutoSize;
+ Resize();
+}
+
+//=============================================================================
+/**
+ * Determines if this button will auto size or not. Auto sizing causes the
+ * button's size to change whenever the "Up" image is changed for this button.
+ * Button size does NOT change as the button state changes.
+ * @return true if this button is currently in Auto Size mode
+ */
+bool CButtonControl::GetAutoSize()
+{
+ return m_AutoSize;
+}
+
+void CButtonControl::SetCenterImage(bool inVCenter, bool inHCenter)
+{
+ if (m_VCenterImage == inVCenter && m_HCenterImage == inHCenter)
+ return;
+ m_VCenterImage = inVCenter;
+ m_HCenterImage = inHCenter;
+ Invalidate();
+}
+
+void CButtonControl::SetMarginImage(long inMargin)
+{
+ m_Margin = inMargin;
+}
+
+//=============================================================================
+/**
+ * Draws the button based on the current state
+ */
+void CButtonControl::Draw(CRenderer *inRenderer)
+{
+ Render(inRenderer);
+}
+
+//=============================================================================
+/**
+ * Handles mouse down on the button. Flags the button as down which results
+ * in some possible drawing changes.
+ */
+bool CButtonControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_IsMouseDown = true;
+ SetButtonState(EBUTTONSTATE_DOWN);
+ SigButtonDown(this);
+ Invalidate();
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles mouse up on the button. Sets the button to the "up" state and fires
+ * an event to interested listeners.
+ * @param inPoint location of the mouse when the event occurred
+ * @param inFlags state of the modifier keys when this event occurred
+ */
+void CButtonControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ SetButtonState(EBUTTONSTATE_UP);
+
+ if (IsMouseOver()) {
+ SigButtonUp(this);
+ if (m_IsMouseDown)
+ SigClicked(this);
+ }
+ m_IsMouseDown = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * This function is called when the mouse passes over this button. The button
+ * is invalidated so that any custom drawing for when the mouse is over the
+ * button can occurr.
+ * @param inPoint location of the mouse when the event occurred
+ * @param inFlags state of the modifier keys when this event occurred
+ */
+void CButtonControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ // Only invalidate the first time the mouse enters this control
+ if (m_MouseEnter) {
+ Invalidate();
+ m_MouseEnter = false;
+ }
+}
+
+//=============================================================================
+/**
+ * This function is called when the mouse exits the bounds of this button. The
+ * button is invalidated so that any custom drawing caused by the OnMouseOver
+ * event can be undone.
+ * @param inPoint location of the mouse when the event occurred
+ * @param inFlags state of the modifier keys when this event occurred
+ */
+void CButtonControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+ Invalidate();
+ // Change the flag so that next time we get a mouse over event, we know to invalidate this
+ // button
+ m_MouseEnter = true;
+}
+
+//=============================================================================
+/**
+ * Gets the current bitmap depending on the state of the button, postion of the
+ * mouse, etc. Returns the image for the up state by default.
+ * @return the currently displayed image or NULL if there is no image on this button
+ */
+QPixmap CButtonControl::GetCurrentImage() const
+{
+ // Default to the up state
+ auto theImage = m_ImageUp;
+
+ EButtonState theState = GetButtonState();
+
+ // If the mouse is over the button, switch to the mouse over image
+ if (IsMouseOver() && !m_ImageOver.isNull())
+ theImage = m_ImageOver;
+
+ // If the button is currently pressed, this cancels the up and over states, so return the down
+ // image
+ if (theState == EBUTTONSTATE_DOWN && !m_ImageDown.isNull())
+ theImage = m_ImageDown;
+
+ // If this button is disabled just return the disabled image (overrides any other states)
+ if (!IsEnabled()) { // use the specified butuon UP disabled if specified, otherwise fall back on
+ // the DOWN ( that's how the legacy system does it )
+ if (theState == EBUTTONSTATE_UP && !m_ImageUpDisabled.isNull())
+ theImage = m_ImageUpDisabled;
+ else if (!m_ImageDownDisabled.isNull())
+ theImage = m_ImageDownDisabled;
+ }
+ return theImage;
+}
+
+//=============================================================================
+/**
+ * Get the size of the image that this control is currently using.
+ * @return the size of the ImageUp image.
+ */
+QSize CButtonControl::GetImageSize() const
+{
+ // Get the image map.
+ QPixmap theImage = GetCurrentImage();
+
+ return theImage.size();
+}
+
+//=============================================================================
+/**
+ * Override of invalidate to invalidate the parent as well.
+ * @param inIsInvalidated true if this is to be invalidated.
+ */
+void CButtonControl::Invalidate(bool inIsInvalidated /* = true */)
+{
+ // Since the button is transparent the parent needs to be drawn as well.
+ if (inIsInvalidated && GetParent() != nullptr)
+ GetParent()->Invalidate();
+
+ CControl::Invalidate(inIsInvalidated);
+}
+
+//=============================================================================
+/**
+ * Draws the appropriate image for the current button state to the renderer.
+ * @param inRenderer the renderer to draw to
+ */
+void CButtonControl::Render(CRenderer *inRenderer)
+{
+ CPt thePos(0, 0);
+ const auto theImage = GetCurrentImage();
+
+ if (m_VCenterImage || m_HCenterImage) {
+ const auto theSize = theImage.size();
+
+ if (m_HCenterImage)
+ thePos.x = (GetSize().x / 2) - (theSize.width() / 2);
+
+ if (m_VCenterImage)
+ thePos.y = (GetSize().y / 2) - (theSize.height() / 2);
+ }
+
+ thePos.x += m_Margin;
+
+ if (!theImage.isNull())
+ inRenderer->DrawBitmap(thePos, theImage);
+}
+
+//=============================================================================
+/**
+ * If auto sizing is enabled, this function resizes the button to be the same
+ * size as the image that it contains. Note: it does not set the Min/Max sizes.
+ */
+void CButtonControl::Resize()
+{
+ if (m_AutoSize) {
+ auto theSize = m_ImageUp.size();
+
+ SetSize(CPt(theSize.width(), theSize.height()));
+ }
+}
diff --git a/src/Authoring/Studio/Controls/ButtonControl.h b/src/Authoring/Studio/Controls/ButtonControl.h
new file mode 100644
index 00000000..4baf71dc
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ButtonControl.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_BUTTON_CONTROL_H
+#define INCLUDED_BUTTON_CONTROL_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "Multicaster.h"
+
+#include <QPixmap>
+
+#include <boost/signals.hpp>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//=============================================================================
+/**
+ * Base class for creating button controls.
+ */
+class CButtonControl : public CControl
+{
+public:
+ /// States for procedural buttons that might result in drawing changes
+ enum EButtonState {
+ EBUTTONSTATE_UP, ///< Indicates that the button is currently in the up position
+ EBUTTONSTATE_DOWN, ///< Indicates that the button is currently in the up position
+ EBUTTONSTATE_INDETERMINATE ///< Not used yet; provided for sub-classes to implement a
+ ///tri-state functionality
+ };
+
+protected:
+ EButtonState m_State; ///< Specifies what state the button is currently in; state changes as the
+ ///button is clicked on
+ QPixmap m_ImageUp; ///< The image for the button in its normal state
+ QPixmap m_ImageDown; ///< The image for the button while it is being clicked
+ QPixmap m_ImageOver; ///< The image for the button while the mouse is over
+ QPixmap m_ImageUpDisabled; ///< The image for the button when it is disabled and unavailable
+ ///to the user
+ QPixmap m_ImageDownDisabled; ///< The image for the button when it is disabled and unavailable
+ ///to the user
+ bool m_AutoSize; ///< true if the button will automatically resize itself
+ bool m_MouseEnter; ///< Flag for determining mouse enter/exit status so that the number of
+ ///invalidations is minimized. See OnMouseOver and OnMouseOut.
+ bool m_IsMouseDown;
+ bool m_VCenterImage;
+ bool m_HCenterImage;
+ long m_Margin;
+
+public:
+ CButtonControl();
+ virtual ~CButtonControl();
+
+ EButtonState GetButtonState() const;
+ virtual void SetButtonState(EButtonState inState);
+
+ void SetUpImage(const QPixmap &inImage);
+ void SetUpImage(const QString &inImageName);
+ void SetDownImage(const QPixmap &inImage);
+ void SetDownImage(const QString &inImageName);
+ void SetOverImage(const QPixmap &inImage);
+ void SetOverImage(const QString &inImageName);
+ void SetDisabledImage(const QPixmap &inImage);
+ void SetDisabledImage(const QString &inImageName);
+ void SetUpDisabledImage(const QString &inImageName);
+ QSize GetImageSize() const;
+
+ void SetAutoSize(bool inEnableAutoSize);
+ bool GetAutoSize();
+
+ void SetCenterImage(bool inVCenter, bool inHCenter);
+ void SetMarginImage(long inMargin);
+
+ void Draw(CRenderer *inRenderer) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void Invalidate(bool inIsInvalidated = true) override;
+
+ boost::signal1<void, CControl *> SigButtonDown;
+ boost::signal1<void, CControl *> SigButtonUp;
+ boost::signal1<void, CControl *> SigClicked;
+
+protected:
+ virtual void Render(CRenderer *inRenderer);
+ virtual QPixmap GetCurrentImage() const;
+ virtual void Resize();
+};
+
+#endif // INCLUDED_BUTTON_CONTROL_H
diff --git a/src/Authoring/Studio/Controls/ClickableLabel.cpp b/src/Authoring/Studio/Controls/ClickableLabel.cpp
new file mode 100644
index 00000000..73995fcf
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ClickableLabel.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ClickableLabel.h"
+
+#include <QMouseEvent>
+
+ClickableLabel::ClickableLabel(QWidget *pr) :
+ QLabel(pr)
+{
+
+}
+
+void ClickableLabel::mousePressEvent(QMouseEvent *event)
+{
+ if (rect().contains(event->pos()))
+ event->accept();
+}
+
+void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (rect().contains(event->pos())) {
+ event->accept();
+ Q_EMIT clicked();
+ }
+}
diff --git a/src/Authoring/Studio/Controls/ClickableLabel.h b/src/Authoring/Studio/Controls/ClickableLabel.h
new file mode 100644
index 00000000..591baa9a
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ClickableLabel.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CLICKABLELABEL_H
+#define CLICKABLELABEL_H
+
+#include <QLabel>
+
+class ClickableLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ ClickableLabel(QWidget *pr = nullptr);
+
+Q_SIGNALS:
+ void clicked();
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+};
+
+#endif // CLICKABLELABEL_H
diff --git a/src/Authoring/Studio/Controls/ComboTextBox.h b/src/Authoring/Studio/Controls/ComboTextBox.h
new file mode 100644
index 00000000..dbb3f199
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ComboTextBox.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_COMBO_TEXT_BOX_H
+#define INCLUDED_COMBO_TEXT_BOX_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StringEdit.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//==============================================================================
+/**
+ * @class CComboTextBox extends CTextEdit to handle custom drawing, etc.
+ */
+class CComboTextBox : public CStringEdit
+{
+public:
+ CComboTextBox();
+ virtual ~CComboTextBox();
+ void Draw(CRenderer *inRenderer) override;
+ bool OnMouseDown(CPt inLocation, Qt::KeyboardModifiers inFlags) override;
+ bool CanAcceptChar(const Q3DStudio::CString &inCheckString, unsigned int inChar,
+ unsigned int inPosition) override;
+
+protected:
+};
+
+#endif // INCLUDED_COMBO_TEXT_BOX_H
diff --git a/src/Authoring/Studio/Controls/Control.cpp b/src/Authoring/Studio/Controls/Control.cpp
new file mode 100644
index 00000000..dbf06690
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Control.cpp
@@ -0,0 +1,1806 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "Control.h"
+#include "Renderer.h"
+#include "MasterP.h"
+#include "StudioUtils.h"
+#include "HotKeys.h"
+#include "ControlGraph.h"
+#include "ControlData.h"
+#include "ResourceCache.h"
+#include "MouseCursor.h"
+
+#include <QtWidgets/qapplication.h>
+
+using namespace Q3DStudio::Control;
+
+IMPLEMENT_OBJECT_COUNTER(CControl)
+
+//=============================================================================
+/**
+ * Constructor, creates a control with all default values.
+ */
+CControl::CControl()
+ : m_cursorSet(-1)
+{
+ m_ControlData = CControlData::CreateControlData(*this);
+ ADDTO_OBJECT_COUNTER(CControl)
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CControl::~CControl()
+{
+ REMOVEFROM_OBJECT_COUNTER(CControl)
+ m_ControlData->ReleaseControl();
+}
+
+//=============================================================================
+/**
+ * Draw this control and all children below it.
+ * This can be overriden by controls to draw themselves but should still be
+ * called if sub controls are to be drawn.
+ * This will call Draw if this control is actually supposed to be drawn. It will
+ * be drawn if it is invalidated or if anything above it in it's heirarchy has
+ * been invalidated.
+ * @param inRenderer the renderer to draw this control out to.
+ */
+void CControl::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect,
+ bool inIgnoreValidation /* = false */)
+{
+ EnsureLayout();
+ bool isInvalidated = IsInvalidated();
+
+ // inRenderer->PushClippingRect( GetSize( ) );
+
+ CRct theBoundingBox = inRenderer->GetClippingRect();
+
+ if (isInvalidated) {
+ CRct theRect(GetSize());
+ theRect.And(theBoundingBox);
+ theRect.Offset(inRenderer->GetTranslation());
+ inDirtyRect.Or(theRect);
+ }
+
+ if (isInvalidated || inIgnoreValidation) {
+ Draw(inRenderer);
+ }
+
+ // Notify the children in the reverse order that they are drawn.
+ ControlGraph::SReverseIterator theRPos = ControlGraph::GetRChildren(*this);
+ for (; !theRPos.IsDone(); ++theRPos) {
+ (*theRPos)->EnsureLayout();
+ (*theRPos)->BeginDrawChildren(inRenderer);
+ }
+
+ // Go through all the children and draw them in the correct order. By keeping
+ // this order there is a semblance of depth.
+ ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this);
+ for (; !thePos.IsDone(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) //&& ( theChild->IsChildInvalidated( ) || inIgnoreValidation ||
+ //isInvalidated ) )
+ {
+ CPt thePosition = theChild->GetPosition();
+ inRenderer->PushTranslation(thePosition);
+
+ // Clipping of non-visible objects.
+ if (theChild->IsInRect(theBoundingBox)) {
+ theChild->OnDraw(inRenderer, inDirtyRect, inIgnoreValidation || isInvalidated);
+ } else {
+ theChild->NotifyNotInClipRect();
+ }
+ inRenderer->PopTranslation();
+ }
+ }
+
+ // inRenderer->PopClippingRect( );
+
+ // Set this as not being invalidated.
+ Invalidate(false);
+}
+
+//=============================================================================
+/**
+ * Performs the drawing for this control.
+ * Does nothing by default.
+ * @param inRenderer the renderer to draw to.
+ */
+void CControl::Draw(CRenderer *inRenderer)
+{
+ Q_UNUSED(inRenderer);
+}
+
+//=============================================================================
+/**
+ * Notification that this control is not in the clipping rect and hence will
+ * not be drawn.
+ * By default, tell inform its children
+ */
+void CControl::NotifyNotInClipRect()
+{
+ ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this);
+ for (; !thePos.IsDone(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ theChild->NotifyNotInClipRect();
+ }
+}
+
+//=============================================================================
+/**
+ * Get the position of this control.
+ * The position of this control is relative to it's parent's 0,0 coordinate.
+ * @return the position relative to it's parent's position.
+ */
+CPt CControl::GetPosition() const
+{
+ return m_ControlData->m_Position;
+}
+
+//=============================================================================
+/**
+ * Set the position of this control.
+ * The position of this control is relative to it's parent's 0,0 coordinate.
+ * @return the position relative to it's parent's position.
+ */
+void CControl::SetPosition(CPt inPosition)
+{
+ if (inPosition != m_ControlData->m_Position) {
+ MarkChildrenNeedLayout();
+ m_ControlData->m_Position = inPosition;
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Overload of SetPosition to allow it to take 2 parameters.
+ * This will call SetPosition( CPt ) so there is no need to extend this
+ * method.
+ * @param inX the X position to set.
+ * @param inY the Y position to set.
+ */
+void CControl::SetPosition(long inX, long inY)
+{
+ SetPosition(CPt(inX, inY));
+}
+
+//=============================================================================
+/**
+ * Get the size of the control.
+ * The size is the width and height of the control from it's position.
+ * @return the size of this control.
+ */
+CPt CControl::GetSize() const
+{
+ return m_ControlData->m_Size;
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * The size is the width and height of the control from it's position.
+ * @param inSize the new size of the control.
+ */
+void CControl::SetSize(CPt inSize)
+{
+ if (GetSize() != inSize) {
+ m_ControlData->m_Size = inSize;
+ if (GetParent() != nullptr)
+ GetParent()->OnChildSizeChanged(this); // this callback needs to die
+ OnSizeChanged(inSize);
+ }
+}
+
+void CControl::OnSizeChanged(CPt /*inSize*/)
+{
+ MarkChildrenNeedLayout();
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Overload of SetSize to allow it to take 2 parameters.
+ * This will call SetSize( CPt ) so there is no need to extend this method.
+ * @param inWidth the new width of this control.
+ * @param inHeight the new height of this control.
+ */
+void CControl::SetSize(long inX, long inY)
+{
+ SetSize(CPt(inX, inY));
+}
+
+//=============================================================================
+/**
+ * Get the minimum allowable size of this control.
+ * This is used by layout managers to get the minimum size that a control is
+ * allowed to be when it is resizing the control. This defaults to 0,0.
+ * @return the minimum size that this control is allowed to be.
+ */
+CPt CControl::GetMinimumSize()
+{
+ return m_ControlData->m_MinSize;
+}
+
+//=============================================================================
+/**
+ * Set the minimum allowable size of this control.
+ * This is used by layout managers to get the minimum size that a control is
+ * allowed to be when it is resizing the control. This defaults to 0,0.
+ * param inSize the minimum size that this control is allowed to be.
+ */
+void CControl::SetMinimumSize(CPt inSize)
+{
+ if (inSize != m_ControlData->m_MinSize) {
+ NotifyParentNeedsLayout();
+ m_ControlData->m_MinSize = inSize;
+ if (inSize.x > m_ControlData->m_MaxSize.x)
+ m_ControlData->m_MaxSize.x = inSize.x;
+ if (inSize.y > m_ControlData->m_MaxSize.y)
+ m_ControlData->m_MaxSize.y = inSize.y;
+ }
+}
+
+//=============================================================================
+/**
+ * Get the maximum allowable size of this control.
+ * This is used by layout managers to get the maximum size that a control is
+ * allowed to be when it is resizing the control. This defaults to LONG_MAX,LONG_MAX.
+ * @return the maximum size that this control is allowed to be.
+ */
+CPt CControl::GetMaximumSize()
+{
+ return m_ControlData->m_MaxSize;
+}
+
+//=============================================================================
+/**
+ * Set the maximum allowable size of this control.
+ * This is used by layout managers to get the maximum size that a control is
+ * allowed to be when it is resizing the control. This defaults to LONG_MAX,LONG_MAX.
+ * @param inSize the maximum size that this control is allowed to be.
+ */
+void CControl::SetMaximumSize(CPt inSize)
+{
+ if (inSize != m_ControlData->m_MaxSize) {
+ NotifyParentNeedsLayout();
+ m_ControlData->m_MaxSize = inSize;
+ }
+}
+
+//=============================================================================
+/**
+ * Get the preferred size of this control.
+ * This is used by layout managers to get the preferred size of the control.
+ * The preferred size is often used for relative scaling of sibling controls.
+ * @return the preferred size of this control.
+ */
+CPt CControl::GetPreferredSize()
+{
+ return m_ControlData->m_PrefSize;
+}
+
+//=============================================================================
+/**
+ * Set the preferred size of this control.
+ * This is used by layout managers to get the preferred size of the control.
+ * The preferred size is often used for relative scaling of sibling controls.
+ * @param the preferred size of this control.
+ */
+void CControl::SetPreferredSize(CPt inSize)
+{
+ if (inSize != m_ControlData->m_PrefSize) {
+ NotifyParentNeedsLayout();
+ Invalidate();
+ m_ControlData->m_PrefSize = inSize;
+ }
+}
+
+void CControl::SetAbsoluteSize(CPt inSize)
+{
+ SetMinimumSize(inSize);
+ SetMaximumSize(inSize);
+ SetPreferredSize(inSize);
+ SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ * Event called when the mouse is moving.
+ * This event will be called when the mouse is over this control or when this
+ * control has the focus. This can be extended by controls but should be called
+ * if the event is to be propagated down to child controls.
+ * @param inPoint the location of the mouse relative to this control.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+void CControl::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theHitFlag = false;
+ // Go through all the children notifying them of mouse moves.
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If a child has not already gotten the move (higher level child covering a lower level
+ // one)
+ // then check the hit test.
+ if (!theHitFlag && theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ // This is the first child to be hit, do not allow the message to go onto another
+ // sibling under this one.
+ theHitFlag = true;
+
+ // Do not fire events to non-enabled children.
+ if (theChild->IsEnabled()) {
+ // If this is the first time the mouse is over this then fire a mouse over
+ if (!theChild->IsMouseOver()) {
+ theChild->OnMouseOver(theChildPoint, inFlags);
+ }
+ // Fire a mouse move as well, this will also propagate the mouse over to
+ // grand-children etc.
+ theChild->OnMouseMove(theChildPoint, inFlags);
+ }
+ }
+ // Check all the children and if they think the mouse is over them then notify them of a
+ // mouse out.
+ else if (theChild->IsMouseOver()) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ theChild->OnMouseOut(theChildPoint, inFlags);
+ }
+ }
+
+ // If there is a child with focus and the mouse is not over it then still notify it of the mouse
+ // move
+ // If the mouse is over it then it would have gotten the move from the above loop.
+ if (m_ControlData->m_MouseFocus && !m_ControlData->m_MouseFocus->IsMouseOver()) {
+ CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition();
+ m_ControlData->m_MouseFocus->OnMouseMove(theChildPoint, inFlags);
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse is over this control.
+ * This is only called once when the mouse enters the control, and does not get
+ * called until the mouse leaves then re-enters the control.
+ * @param inPoint where the mouse is, relative to this control.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+void CControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+ m_ControlData->m_IsMouseOver = true;
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse left this control.
+ * This is only called once when the mouse leaves the control and does not get
+ * called until the mouse enters then re-leaves the control.
+ * This also notifies all children that the mouse is over that it is no longer
+ * over.
+ * @param inPoint where the mouse is, relative to this control.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+void CControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_ControlData->m_IsMouseOver = false;
+
+ GetWindowListener()->HideTooltips();
+ // Go through all the children looking for ones that think the mouse is over.
+ // If it is then notify it of a mouse out.
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsMouseOver()) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ theChild->OnMouseOut(inPoint, inFlags);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the left mouse button was clicked on this control.
+ * This handles the mouse hits and sets the focus to the control that was
+ * clicked on.
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ * @return true if the mouse event is processed.
+ */
+bool CControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+ bool theChildGotHit = false;
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ theChildGotHit = true;
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled()) {
+ theRetVal = theChild->OnMouseDown(theChildPoint, inFlags);
+
+ if (m_ControlData->m_Focus != theChild) {
+ if (m_ControlData->m_Focus)
+ m_ControlData->m_Focus->OnLoseFocus();
+ if (theChild->CanGainFocus()) {
+ m_ControlData->m_Focus = theChild;
+ m_ControlData->m_Focus->OnGainFocus();
+ } else
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+ m_ControlData->m_MouseFocus = theChild;
+ } else {
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+
+ // only want OnMouseDown to be called on the first child under the point.
+ break;
+ }
+ }
+ if (!theChildGotHit && m_ControlData->m_Focus) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+ m_ControlData->SetMouseDown(!theRetVal);
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Notification that the right mouse button was clicked on this control.
+ * This handles the mouse hits and sets the focus to the control that was
+ * clicked on.
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ * @return true if the mouse event is processed.
+ */
+bool CControl::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled()) {
+ if (m_ControlData->m_Focus != theChild) {
+ if (m_ControlData->m_Focus)
+ m_ControlData->m_Focus->OnLoseFocus();
+ if (theChild->CanGainFocus()) {
+ m_ControlData->m_Focus = theChild;
+ m_ControlData->m_Focus->OnGainFocus();
+ } else
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+ m_ControlData->m_MouseFocus = theChild;
+ theRetVal = theChild->OnMouseRDown(theChildPoint, inFlags);
+ } else {
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+
+ // only want OnMouseDown to be called on the first child under the point.
+ break;
+ }
+ }
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Notification thatthe mouse was double clicked on this control.
+ * This handled the mouse hits and passes it down to all the children.
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ * @return true if the mouse event is processed.
+ */
+bool CControl::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled()) {
+ theRetVal = theChild->OnMouseDoubleClick(theChildPoint, inFlags);
+ }
+
+ // only want OnMouseDown to be called on the first child under the point.
+ break;
+ }
+ }
+
+ return theRetVal;
+}
+
+bool CControl::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+
+ // try letting the focus getting the wheel first
+ if (m_ControlData->m_Focus) {
+ CPt theChildPoint = inPoint - m_ControlData->m_Focus->GetPosition();
+ theRetVal = m_ControlData->m_Focus->OnMouseWheel(theChildPoint, inAmount, inFlags);
+ }
+
+ // if the focus does not want the wheel then let the mouse pos do it.
+ if (!theRetVal) {
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->GetMouseWheelEventState() == ControlEventState::Listening
+ && theChild->IsEnabled() && theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ theRetVal = theChild->OnMouseWheel(theChildPoint, inAmount, inFlags);
+ break;
+ }
+ }
+ }
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse is hovering over this control.
+ * This handles the mouse hover and passes it down to all the children.
+ * @param inPoint where the mouse is located, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ * @return true if the mouse event is processed.
+ */
+bool CControl::OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled()) {
+ theRetVal = theChild->OnMouseHover(theChildPoint, inFlags);
+ }
+
+ // only want OnMouseHover to be called on the first child under the point.
+ break;
+ }
+ }
+
+ // If the mouseover was not handled
+ if (!theRetVal) {
+ // If the tooltip text is not empty
+ Q3DStudio::CString theTooltipText = GetTooltipText();
+ if (!theTooltipText.IsEmpty()) {
+ // Show the tooltip, and return true so that parents don't cover this tooltip with their
+ // own
+ // Note that we are offsetting the point so that it appears below the mouse cursor
+ GetWindowListener()->ShowTooltips(GetGlobalPosition(CPt(inPoint.x, inPoint.y + 32)),
+ GetTooltipText());
+ theRetVal = true;
+ }
+ }
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Notification that the left mouse button was released.
+ * This is only called on the control that has focus, not on the control under
+ * the mouse.
+ * @param inPoint where the mouse was released, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+void CControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_ControlData->m_MouseFocus) {
+ CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition();
+ m_ControlData->m_MouseFocus->OnMouseUp(theChildPoint, inFlags);
+ }
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled() && theChild != m_ControlData->m_MouseFocus
+ && theChild != m_ControlData->m_Focus) {
+ theChild->OnMouseUp(theChildPoint, inFlags);
+ }
+
+ // only want OnMouseUp to be called on the first child under the point.
+ break;
+ }
+ }
+ m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>();
+
+ if (m_ControlData->m_IsMouseDown) {
+ OnMouseClick(inPoint, inFlags);
+ m_ControlData->SetMouseDown(false);
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the right mouse button was released.
+ * This is only called on the control that has focus, not on the control under
+ * the mouse.
+ * @param inPoint where the mouse was released, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+void CControl::OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_ControlData->m_MouseFocus) {
+ CPt theChildPoint = inPoint - m_ControlData->m_MouseFocus->GetPosition();
+ m_ControlData->m_MouseFocus->OnMouseRUp(theChildPoint, inFlags);
+ }
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ // If the child was hit then notify it of the mouse down, and set the focus to
+ // be it.
+ if (theChild->HitTest(inPoint)) {
+ CPt theChildPoint = inPoint - theChild->GetPosition();
+ // Only send the event if the child is enabled.
+ if (theChild->IsEnabled() && theChild != m_ControlData->m_MouseFocus
+ && theChild != m_ControlData->m_Focus) {
+ theChild->OnMouseRUp(theChildPoint, inFlags);
+ }
+
+ // only want OnMouseUp to be called on the first child under the point.
+ break;
+ }
+ }
+ m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>();
+}
+
+//=============================================================================
+/**
+ * Handles character input from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CControl::OnChar(const QString &inChar, Qt::KeyboardModifiers inModifiers)
+{
+ if (m_ControlData->m_Focus)
+ return m_ControlData->m_Focus->OnChar(inChar, inModifiers);
+ else
+ return false;
+}
+
+//=============================================================================
+/**
+ * Handles a key down message from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CControl::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inModifiers)
+{
+ bool theRetVal = false;
+
+ if (m_ControlData->m_Focus)
+ theRetVal = m_ControlData->m_Focus->OnKeyDown(inChar, inModifiers);
+
+ if (!theRetVal) {
+ if (inChar == Qt::Key_Tab) {
+ if (inModifiers & Qt::ShiftModifier) {
+ OnReverseTab();
+ } else {
+ OnTab();
+ }
+ theRetVal = true;
+ }
+ }
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Handles a key up from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CControl::OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers)
+{
+ Q_UNUSED(inChar);
+ return false;
+}
+
+//=============================================================================
+/**
+ * Find the first child (descendant) control that has a valid drop target.
+ */
+CDropTarget *CControl::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ CDropTarget *theDropTarget = NULL;
+
+ // Go through all the children looking for the first one that was clicked on
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsMouseOver() && theChild->IsEnabled()) {
+ // Put the point into this childs coords.
+ CPt theChildPoint = inMousePoint - theChild->GetPosition();
+
+ // Allow the child the opportunity to respond
+ theDropTarget = theChild->FindDropCandidate(theChildPoint, inFlags);
+
+ if (theDropTarget)
+ break;
+ }
+ }
+ return theDropTarget;
+}
+
+//=============================================================================
+/**
+ * Add a child control to this control.
+ * This will make the child behave as a child of this, get set up for drawing
+ * and events. The inInsertBefore control is used to determine Z-depth, or
+ * manually insert a control into a specific location.
+ * The child cannot already be a child of another control.
+ * @param inControl the control to be added.
+ * @param inInsertBefore the control to be inserted before, or std::shared_ptr<CControlData>() to
+ * be at the back.\
+ */
+void CControl::AddChild(CControl *inControl,
+ CControl *inInsertBefore /*=std::shared_ptr<CControlData>()*/)
+{
+ NotifyParentNeedsLayout();
+ ControlGraph::AddChild(*this, *inControl, inInsertBefore);
+}
+
+//=============================================================================
+/**
+ * Remove a child control from this control.
+ * This will remove it from drawing and getting any events.
+ * @param inControl the control to be removed.
+ */
+void CControl::RemoveChild(CControl *inControl)
+{
+ if (inControl) {
+ ControlGraph::RemoveChild(*this, *inControl);
+
+ if (m_ControlData->m_Focus == inControl->m_ControlData) {
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ }
+ if (m_ControlData->m_MouseFocus == inControl->m_ControlData) {
+ m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>();
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Remove all child controls from this control.
+ * This will remove it from drawing and getting any events.
+ *
+ * This is not recursive
+ */
+void CControl::RemoveAllChildren()
+{
+ NotifyParentNeedsLayout();
+ ControlGraph::RemoveAllChildren(*this);
+
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ m_ControlData->m_MouseFocus = std::shared_ptr<CControlData>();
+}
+
+//=============================================================================
+/**
+ * Retrieve the index of a child control. The index will return the zero based position.
+ * @param inChildControl the control that is a direct child of this control
+ * @return the zero-based index of this control we will return -1 if we don't find the control
+ */
+long CControl::GetChildIndex(CControl *inChildControl)
+{
+ return ControlGraph::GetChildIndex(*this, *inChildControl);
+}
+
+static inline CControl *ToControl(std::shared_ptr<CControlData> inPtr)
+{
+ if (inPtr)
+ return inPtr->GetControl();
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Finds a child control by its name
+ * @return the child if found, std::shared_ptr<CControlData>() otherwise
+ */
+CControl *CControl::FindChildByName(const Q3DStudio::CString &inName)
+{
+ std::shared_ptr<CControlData> theResult = std::shared_ptr<CControlData>();
+
+ ControlGraph::SIterator theChildIter = GetChildren();
+ for (; !theChildIter.IsDone(); ++theChildIter) {
+ if (theChildIter.GetCurrent()->GetName() == inName) {
+ theResult = theChildIter.GetCurrent();
+ break;
+ }
+ }
+
+ return ToControl(theResult);
+}
+
+CControl *CControl::FocusedChild()
+{
+ CControl *theResult = nullptr;
+
+ ControlGraph::SIterator theChildIter = GetChildren();
+ for (; !theChildIter.IsDone(); ++theChildIter) {
+ auto current = ToControl(theChildIter.GetCurrent());
+ auto hasFocus = HasFocus(current);
+ if (hasFocus) {
+ if (current->GetFirstChild())
+ theResult = current->FocusedChild();
+ else
+ theResult = current;
+ break;
+ }
+ }
+
+ return theResult;
+}
+
+//=============================================================================
+/**
+ * Check to see if the mouse is over this control or not.
+ * @return true if the mouse is over this control.
+ */
+bool CControl::IsMouseOver() const
+{
+ return m_ControlData->m_IsMouseOver;
+}
+
+//=============================================================================
+/**
+ * Check to see if inPoint is over this control or not.
+ * This is used for mouse hits and can be extended for non-standard control
+ * shapes. Non-visible controls always return false.
+ * @param inPoint the location of the mouse in local coordinates.
+ */
+bool CControl::HitTest(const CPt &inPoint) const
+{
+ CPt thePoint = inPoint - GetPosition();
+ CPt theSize = GetSize();
+ // Basic check to see if it's in the size.
+ if (IsVisible() && thePoint.x >= 0 && thePoint.y >= 0 && thePoint.x < theSize.x
+ && thePoint.y < theSize.y)
+ return true;
+ return false;
+}
+
+//=============================================================================
+/**
+ * Checks to see if any part of this control is in the rect.
+ * This is used for drawing and ignoring objects that do not need to be
+ * redrawn or need to be drawn.
+ * @param inRect the rect to check to see if this is in.
+ * @return true if this is in the rect.
+ */
+bool CControl::IsInRect(const CRct &inRect) const
+{
+ CRct myRect(GetPosition(), GetSize());
+
+ if (myRect.position <= inRect.size + inRect.position) {
+ if (myRect.size + myRect.position >= inRect.position) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Invalidate this control and cause it to be redrawn.
+ * @param inInvalidate true if this is to be invalidated.
+ */
+void CControl::Invalidate(bool inInvalidate /*= true*/)
+{
+ m_ControlData->m_IsInvalidated = inInvalidate;
+ if (inInvalidate && GetParent() != nullptr) {
+ GetParent()->OnChildInvalidated();
+ }
+ if (!inInvalidate) {
+ m_ControlData->m_IsChildInvalidated = false;
+ }
+}
+
+//=============================================================================
+/**
+ * Invalidate this object and all children within inRect.
+ * @param inRect the rect in which to invalidate all children.
+ */
+void CControl::InvalidateRect(const CRct &inRect)
+{
+ Invalidate();
+
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsInRect(inRect)) {
+ CRct theChildRect = inRect;
+ theChildRect.Offset(theChild->GetPosition());
+
+ theChild->InvalidateRect(inRect);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Check to see if this control is invalidated or not.
+ * @return true if this control is invalidated.
+ */
+bool CControl::IsInvalidated() const
+{
+ return m_ControlData->IsInvalidated();
+}
+
+//=============================================================================
+/**
+ * Notifies this control that a child of it has been invalidated.
+ */
+void CControl::OnChildInvalidated()
+{
+ // Only do it if we haven't already, avoid multiple traversals up the tree.
+ if (!m_ControlData->m_IsChildInvalidated) {
+ if (GetParent() != nullptr)
+ GetParent()->OnChildInvalidated();
+ else if (m_ControlData->m_WindowListener != nullptr)
+ m_ControlData->m_WindowListener->OnControlInvalidated();
+
+ m_ControlData->m_IsChildInvalidated = true;
+ }
+}
+
+//=============================================================================
+/**
+ * Checks to see if a child of this control is invalidated.
+ */
+bool CControl::IsChildInvalidated() const
+{
+ return m_ControlData->m_IsChildInvalidated || m_ControlData->m_IsInvalidated;
+}
+
+void CControl::SetWindowListener(CControlWindowListener *inListener)
+{
+ m_ControlData->m_WindowListener = inListener;
+}
+
+//=============================================================================
+/**
+ * Retrieves the topmost window of this control. The window listener is the
+ * bridge between the OS and the cross-platform custom control code below.
+ * This function was added specifically to support drag-and-drop by providing
+ * a way of setting the drag state on the outermost window.
+ * @return pointer to the control window listener or std::shared_ptr<CControlData>() if there is
+ * not one
+ */
+CControlWindowListener *CControl::GetWindowListener()
+{
+ CControlWindowListener *theWindowListener = nullptr;
+ if (GetParent() != nullptr)
+ theWindowListener = GetParent()->GetWindowListener();
+ else
+ theWindowListener = m_ControlData->m_WindowListener;
+ return theWindowListener;
+}
+
+//=============================================================================
+/**
+ * Set this control as being visible or not.
+ * If the control is not visible then it will not be drawn and will not
+ * get mouse clicks.
+ * @param inIsVisible true if this control is to be visible.
+ */
+void CControl::SetVisible(bool inIsVisible)
+{
+ if (inIsVisible != m_ControlData->m_IsVisible) {
+ m_ControlData->m_IsVisible = inIsVisible;
+ NotifyParentNeedsLayout();
+
+ if (GetParent() != nullptr)
+ GetParent()->OnChildSizeChanged(this);
+
+ OnVisibleStateChange(inIsVisible);
+ OnParentVisibleStateChanged(inIsVisible);
+
+ this->Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the visible state of a control has changed
+ */
+void CControl::OnVisibleStateChange(bool inIsVisible)
+{
+ Q_UNUSED(inIsVisible);
+}
+
+void CControl::OnParentVisibleStateChanged(bool inIsVisible)
+{
+ NotifyParentNeedsLayout();
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ theChild->OnParentVisibleStateChanged(inIsVisible);
+ }
+}
+
+//=============================================================================
+/**
+ * Checks to see if this control is visible or not.
+ * If the control is not visible then it will not be drawn and will not
+ * get mouse clicks.
+ * @return true if this control is visible.
+ */
+bool CControl::IsVisible() const
+{
+ return m_ControlData->m_IsVisible;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not this control is enabled.
+ * If the control is not enabled then it is still drawn and still intercepts
+ * mouse clicks, but it will not actually process them.
+ * @param inIsEnabled true if this control is to be enabled.
+ */
+void CControl::SetEnabled(bool inIsEnabled)
+{
+ if (inIsEnabled != m_ControlData->m_IsEnabled) {
+ NotifyParentNeedsLayout();
+ m_ControlData->m_IsEnabled = inIsEnabled;
+
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ theChild->SetParentEnabled(inIsEnabled);
+ }
+ Invalidate();
+ }
+}
+
+void CControl::SetParentEnabled(bool inParentEnabled)
+{
+ NotifyParentNeedsLayout();
+ m_ControlData->m_IsParentEnabled = inParentEnabled;
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ theChild->SetParentEnabled(inParentEnabled);
+ }
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Gets whether or not this control is enabled.
+ * If the control is not enabled then it is still drawn and still intercepts
+ * mouse clicks, but it will not actually process them.
+ * @param inIsEnabled true if this control is to be enabled.
+ */
+bool CControl::IsEnabled() const
+{
+ return m_ControlData->IsEnabled();
+}
+
+//=============================================================================
+/**
+ * Gets teh value of the enabled flag without its parent's flag
+ */
+bool CControl::GetEnabledFlag()
+{
+ return m_ControlData->GetEnabledFlag();
+}
+
+//=============================================================================
+/**
+ * Sets the enabled flag...this is used when a control wants to override the
+ * SetEnabled function and does not necessarily want to pass enabled messages
+ * to its children no matter what
+ */
+void CControl::SetEnabledFlag(bool inIsEnabled)
+{
+ m_ControlData->SetEnabledFlag(inIsEnabled);
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Notification that the size of a child has changed.
+ * @param inControl the control that has changed size.
+ */
+void CControl::OnChildSizeChanged(CControl *inControl)
+{
+ Q_UNUSED(inControl);
+}
+
+void CControl::SetName(const Q3DStudio::CString &inName)
+{
+ m_ControlData->SetName(inName);
+}
+
+Q3DStudio::CString CControl::GetName()
+{
+ return m_ControlData->GetName();
+}
+
+void CControl::BeginDrawChildren(CRenderer *inRenderer)
+{
+ Q_UNUSED(inRenderer);
+}
+
+long CControl::DoPopup(QMenu *inMenu, CPt inLocation)
+{
+ inLocation.Offset(GetPosition());
+ CControl *theParent(GetParent());
+ if (theParent) {
+ return theParent->DoPopup(inMenu, inLocation);
+ } else {
+ return m_ControlData->m_WindowListener->DoPopup(inMenu, inLocation);
+ }
+}
+
+//=============================================================================
+/**
+ * Called when a control acquires focus
+ */
+void CControl::OnGainFocus()
+{
+}
+
+//=============================================================================
+/**
+ * Causes focus to be lost.
+ */
+void CControl::OnLoseFocus()
+{
+ if (m_ControlData->m_Focus) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ }
+ FireFocusEvent(false);
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+}
+
+//=============================================================================
+/**
+ * @return the parent control of this class
+ */
+CControl *CControl::GetParent()
+{
+ return m_ControlData->GetParent();
+}
+
+const CControl *CControl::GetParent() const
+{
+ return m_ControlData->GetParent();
+}
+
+//=============================================================================
+/**
+ * Removes a control from the top level control
+ */
+void CControl::RemoveUberControl(CControl *inControl)
+{
+ if (GetParent() != nullptr) {
+ GetParent()->RemoveUberControl(inControl);
+ } else {
+ RemoveChild(inControl);
+ }
+ Invalidate();
+}
+
+long CControl::GetChildCount()
+{
+ return ControlGraph::GetNumChildren(*this);
+}
+
+Q3DStudio::Control::ControlGraph::SIterator CControl::GetChildren()
+{
+ return ControlGraph::GetChildren(*this);
+}
+
+Q3DStudio::Control::ControlGraph::SReverseIterator CControl::GetReverseChildren()
+{
+ return ControlGraph::GetRChildren(*this);
+}
+
+//=============================================================================
+/**
+ * Gets the global position of the point in regards to the top level control
+ */
+CPt CControl::GetGlobalPosition(CPt inChildPoint) const
+{
+ CPt thePosition(GetPosition());
+ CPt thePoint = CPt(inChildPoint.x + thePosition.x, inChildPoint.y + thePosition.y);
+ if (GetParent())
+ return GetParent()->GetGlobalPosition(thePoint);
+ else
+ return thePoint;
+}
+
+//=============================================================================
+/**
+ * Query the platform specific render device (window)
+ */
+UICRenderDevice CControl::GetPlatformDevice()
+{
+ if (GetParent())
+ return GetParent()->GetPlatformDevice();
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Does self or child use this render device?
+ * @see CWndControl::OnKillFocus
+ */
+bool CControl::IsChildPlatformDevice(UICRenderDevice inDevice)
+{
+ if (GetPlatformDevice() == inDevice)
+ return true;
+
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsChildPlatformDevice(inDevice))
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+/**
+ * Shows the window's moveable window with text
+ *
+ * @param inLocation the postion of hte center point of the window
+ * @param inText the text the window will display
+ */
+void CControl::ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, CRct inBoundingRct)
+{
+ CPt thePosition(GetPosition());
+ CPt thePoint = CPt(inLocation.x + thePosition.x, inLocation.y + thePosition.y);
+
+ if (GetParent())
+ GetParent()->ShowMoveableWindow(thePoint, inText, inBoundingRct);
+ else if (m_ControlData->m_WindowListener) {
+ m_ControlData->m_WindowListener->ShowMoveableWindow(thePoint, inText, inBoundingRct);
+ }
+}
+
+//=============================================================================
+/**
+ * Hides the window's moveable window
+ */
+void CControl::HideMoveableWindow()
+{
+ if (GetParent() != nullptr)
+ GetParent()->HideMoveableWindow();
+ else if (m_ControlData->m_WindowListener)
+ m_ControlData->m_WindowListener->HideMoveableWindow();
+}
+
+//=============================================================================
+/**
+ * Offsets the position of this control... this is useful if you don't want to calculate
+ * the global position every time
+ */
+void CControl::OffsetPosition(CPt inOffset)
+{
+ CPt thePosition(GetPosition());
+ thePosition.Offset(inOffset);
+ SetPosition(thePosition);
+}
+
+//=============================================================================
+/**
+ * Gets the first child of this control
+ */
+CControl *CControl::GetFirstChild()
+{
+ std::shared_ptr<CControlData> theChild = std::shared_ptr<CControlData>();
+ ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this);
+ if (!thePos.IsDone()) {
+ theChild = (*thePos);
+ }
+ return ToControl(theChild);
+}
+
+//=============================================================================
+/**
+ * @return true if this control has focus
+ */
+bool CControl::HasFocus(CControl *inControl)
+{
+ return (ToControl(m_ControlData->m_Focus) == inControl);
+}
+
+//=============================================================================
+/**
+ * default is true for controls... override if the control cannot have focus
+ */
+bool CControl::CanGainFocus()
+{
+ if (IsVisible())
+ for (ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this); !thePos.IsDone();
+ ++thePos)
+ if ((*thePos)->CanGainFocus())
+ return true;
+
+ return false;
+}
+
+//=============================================================================
+/**
+ * Returns true if this CControl is in focus.
+ * @return True if this CControl is in focus.
+ */
+bool CControl::IsInFocus()
+{
+ if (GetParent())
+ return GetParent()->HasFocus(this);
+
+ return false;
+}
+
+//=============================================================================
+/**
+ * Handles the tab button in controls
+ */
+void CControl::OnTab()
+{
+ // Go through the children... if there is a focus, then get the next control
+ ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this);
+ bool theFoundFlag = false;
+ if (m_ControlData->m_Focus) {
+ while (!thePos.IsDone() && !theFoundFlag) {
+ std::shared_ptr<CControlData> theCurrentControl = (*thePos);
+ if (theCurrentControl == m_ControlData->m_Focus) {
+ ++thePos;
+ while (!thePos.IsDone() && !theFoundFlag) {
+ std::shared_ptr<CControlData> theNextFocusCanidate = (*thePos);
+ if (theNextFocusCanidate->CanGainFocus()) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ theFoundFlag = true;
+ m_ControlData->m_Focus = theNextFocusCanidate;
+ m_ControlData->m_Focus->SetFocusToFirstAvailable();
+ } else {
+ ++thePos;
+ }
+ }
+ } else {
+ ++thePos;
+ }
+ }
+ // If we didn't find it and we have a parent, then allow the parent to decide
+ if (!theFoundFlag) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ if (GetParent())
+ GetParent()->OnTab();
+ else
+ SetFocusToFirstAvailable();
+ }
+ }
+ // If no focus, then go to first available control
+ else {
+ SetFocusToFirstAvailable();
+ }
+}
+
+//=============================================================================
+/**
+ * Handles the shift tab button in controls
+ */
+void CControl::OnReverseTab()
+{
+ // Go through the children in reverse order... if there is a focus, then get the next control
+ ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ bool theFoundFlag = false;
+ if (m_ControlData->m_Focus) {
+ while (!thePos.IsDone() && !theFoundFlag) {
+ std::shared_ptr<CControlData> theCurrentControl = (*thePos);
+ if (theCurrentControl == m_ControlData->m_Focus) {
+ ++thePos;
+ while (!thePos.IsDone() && !theFoundFlag) {
+ std::shared_ptr<CControlData> theNextFocusCanidate = (*thePos);
+ if (theNextFocusCanidate->CanGainFocus()) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ theFoundFlag = true;
+ m_ControlData->m_Focus = theNextFocusCanidate;
+ m_ControlData->m_Focus->SetFocusToLastAvailable();
+ } else {
+ ++thePos;
+ }
+ }
+ } else {
+ ++thePos;
+ }
+ }
+ // If we didn't find it and we have a parent, then allow the parent to decide
+ if (!theFoundFlag) {
+ m_ControlData->m_Focus->OnLoseFocus();
+ m_ControlData->m_Focus = std::shared_ptr<CControlData>();
+ if (GetParent())
+ GetParent()->OnReverseTab();
+ else
+ SetFocusToLastAvailable();
+ }
+ }
+
+ // If no focus, then go to last available control
+ else {
+ SetFocusToLastAvailable();
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the focus to the first available child control
+ */
+void CControl::SetFocusToFirstAvailable()
+{
+ bool theFlag = false;
+ OnGainFocus();
+
+ // if there are any child controls, then go through them
+ for (ControlGraph::SIterator thePos = ControlGraph::GetChildren(*this);
+ !thePos.IsDone() && !theFlag; ++thePos) {
+ std::shared_ptr<CControlData> theControl = (*thePos);
+ if (theControl->CanGainFocus()) {
+ m_ControlData->m_Focus = theControl;
+ m_ControlData->m_Focus->SetFocusToFirstAvailable();
+ theFlag = true;
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the focus to the last available child control
+ */
+void CControl::SetFocusToLastAvailable()
+{
+ bool theFlag = false;
+ OnGainFocus();
+
+ // if there are any child controls, then go through them
+ for (ControlGraph::SReverseIterator thePos = ControlGraph::GetRChildren(*this);
+ !thePos.IsDone() && !theFlag; ++thePos) {
+ std::shared_ptr<CControlData> theControl = (*thePos);
+ if (theControl->CanGainFocus()) {
+ m_ControlData->m_Focus = theControl;
+ m_ControlData->m_Focus->SetFocusToLastAvailable();
+ theFlag = true;
+ }
+ }
+}
+
+//===============================================================================
+/**
+* Coverts the given point from local coordinates into global coordinates.
+* @param inPoint the point in local coordinates to be converted
+* @return The point translated to global coordinates
+*/
+CPt CControl::ClientToScreen(CPt inPoint)
+{
+ CPt theFinalPt = inPoint + m_ControlData->m_Position;
+
+ if (GetParent())
+ theFinalPt = GetParent()->ClientToScreen(theFinalPt);
+ else if (m_ControlData->m_WindowListener)
+ theFinalPt = m_ControlData->m_WindowListener->ClientToScreen(theFinalPt);
+
+ return theFinalPt;
+}
+
+//===============================================================================
+/**
+* Coverts the given point from screen coordinates into local space.
+* @param inPoint the point in screen coordinates to be converted
+* @return The point translated to local, client coordinates
+*/
+CPt CControl::ScreenToClient(CPt inPoint)
+{
+ CPt theFinalPt = inPoint - m_ControlData->m_Position;
+
+ if (GetParent())
+ theFinalPt = GetParent()->ScreenToClient(theFinalPt);
+ else if (m_ControlData->m_WindowListener)
+ theFinalPt = m_ControlData->m_WindowListener->ScreenToClient(theFinalPt);
+
+ return theFinalPt;
+}
+
+//===============================================================================
+/**
+* Adds a focus listener to this control
+*/
+void CControl::AddFocusListener(CChildFocusListener *inListener)
+{
+ m_ControlData->m_FocusListeners.AddListener(inListener);
+}
+
+//===============================================================================
+/**
+* Removes a focus listener to this control
+*/
+void CControl::RemoveFocusListener(CChildFocusListener *inListener)
+{
+ m_ControlData->m_FocusListeners.RemoveListener(inListener);
+}
+
+//===============================================================================
+/**
+* tells anyone listeneing that the focus of this control has changed
+*/
+void CControl::FireFocusEvent(bool inStatus)
+{
+ m_ControlData->m_FocusListeners.FireEvent(&CChildFocusListener::OnChildFocusChange, inStatus);
+}
+
+//===============================================================================
+/**
+ * Get the platform specific view that this is embedded into.
+ * Used for when platform dependent controls have to be embedded or used.
+ */
+TPlatformView CControl::GetPlatformView()
+{
+ if (GetParent())
+ return GetParent()->GetPlatformView();
+ else if (m_ControlData->m_WindowListener)
+ return m_ControlData->m_WindowListener->GetPlatformView();
+ return nullptr;
+}
+
+bool CControl::IsMouseDown()
+{
+ return m_ControlData->m_IsMouseDown;
+}
+
+void CControl::OnMouseClick(CPt, Qt::KeyboardModifiers)
+{
+}
+
+//===============================================================================
+/**
+ * Sets the text of the tooltip for this control. If the string is empty, no
+ * tooltip will be shown.
+ * @param inText text of the tooltip
+ */
+void CControl::SetTooltipText(const Q3DStudio::CString &inText)
+{
+ m_ControlData->SetTooltipText(inText);
+}
+
+//===============================================================================
+/**
+ * @return the current tooltip text for this control
+ */
+Q3DStudio::CString CControl::GetTooltipText()
+{
+ return m_ControlData->GetTooltipText();
+}
+
+void CControl::GrabFocus(CControl *inControl)
+{
+ if (GetParent())
+ GetParent()->GrabFocus(this);
+
+ std::shared_ptr<CControlData> theNewFocus;
+ if (inControl)
+ theNewFocus = inControl->m_ControlData;
+
+ if (m_ControlData->m_Focus != theNewFocus) {
+ if (m_ControlData->m_Focus)
+ m_ControlData->m_Focus->OnLoseFocus();
+ m_ControlData->m_Focus = theNewFocus;
+ if (m_ControlData->m_Focus)
+ m_ControlData->m_Focus->OnGainFocus();
+ }
+}
+
+//===============================================================================
+/**
+ * Used to notify scrolling views that something should be visible.
+ */
+void CControl::EnsureVisible(CRct inRect)
+{
+ if (GetParent()) {
+ inRect.Offset(GetPosition());
+ GetParent()->EnsureVisible(inRect);
+ }
+}
+
+//===============================================================================
+/**
+ * Call to make this control visible.
+ */
+void CControl::EnsureVisible()
+{
+ CRct theRect(CPt(0, 0), GetSize());
+ EnsureVisible(theRect);
+}
+
+void CControl::OnParentChanged(CControl * /*inNewParent*/)
+{
+}
+
+void CControl::MarkChildrenNeedLayout()
+{
+ if (m_ControlData->m_ChildrenNeedLayout == false) {
+ for (ControlGraph::SIterator theIter = GetChildren(); theIter.IsDone() == false; ++theIter)
+ (*theIter)->MarkNeedsLayout();
+ m_ControlData->m_ChildrenNeedLayout = true;
+ }
+}
+
+void CControl::NotifyParentNeedsLayout()
+{
+ CControl *theParent(GetParent());
+ if (theParent)
+ theParent->MarkChildrenNeedLayout();
+}
+
+void CControl::MarkNeedsLayout()
+{
+ m_ControlData->m_NeedsLayout = true;
+}
+// Tell this control that one of its children need to be layed out.
+void CControl::SetLayout(CPt inSize, CPt inPosition)
+{
+ SetSize(inSize);
+ SetPosition(inPosition);
+ m_ControlData->m_NeedsLayout = false;
+}
+
+void CControl::LayoutChildren()
+{
+ for (ControlGraph::SIterator theIter = GetChildren(); theIter.IsDone() == false; ++theIter) {
+ std::shared_ptr<CControlData> theChild(*theIter);
+ // By default the children get the exact same layout that I do.
+ theChild->SetLayout(theChild->m_Size, theChild->m_Position);
+ }
+ m_ControlData->m_ChildrenNeedLayout = false;
+}
+
+void CControl::EnsureLayout()
+{
+ if (m_ControlData->m_NeedsLayout) {
+ if (IsVisible()) {
+ CControl *parent = GetParent();
+ if (parent)
+ parent->LayoutChildren();
+ }
+ m_ControlData->m_NeedsLayout = false;
+ }
+ if (m_ControlData->m_ChildrenNeedLayout) {
+ LayoutChildren();
+ m_ControlData->m_ChildrenNeedLayout = false;
+ }
+}
+
+void CControl::ChildrenChanged()
+{
+ MarkChildrenNeedLayout();
+ Invalidate();
+ m_ControlData->OnHierarchyChanged();
+}
+
+void CControl::setCursorIfNotSet(long cursor)
+{
+ if (cursor != m_cursorSet) {
+ if (m_cursorSet != -1)
+ qApp->restoreOverrideCursor();
+ m_cursorSet = cursor;
+ qApp->setOverrideCursor(CResourceCache::GetInstance()->GetCursor(cursor));
+ }
+}
+
+void CControl::resetCursor()
+{
+ if (m_cursorSet != -1) {
+ qApp->restoreOverrideCursor();
+ m_cursorSet = -1;
+ }
+}
+
+QCursor CControl::getCursor() const
+{
+ if (m_cursorSet != -1)
+ return CResourceCache::GetInstance()->GetCursor(m_cursorSet);
+ return QCursor();
+}
diff --git a/src/Authoring/Studio/Controls/Control.h b/src/Authoring/Studio/Controls/Control.h
new file mode 100644
index 00000000..0d2ea79e
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Control.h
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INCLUDED_CONTROL_H
+#define INCLUDED_CONTROL_H 1
+
+#pragma once
+
+#include "Pt.h"
+#include "Rct.h"
+#include "SafeArray.h"
+#include "GenericFunctor.h"
+#include "PlatformTypes.h"
+
+#include "DropTarget.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/signals/trackable.hpp>
+#include "ControlGraphIterators.h"
+
+class CRenderer;
+class CContextMenu;
+class CUICFile;
+class IDragable;
+class CAsset;
+class CStudioApp;
+
+class QMenu;
+
+namespace Q3DStudio {
+namespace Control {
+ class CControlData;
+ using std::pair;
+ using std::make_pair;
+ using std::vector;
+}
+}
+
+// this functor is used to tell when a child loses focus the boolean should be true if the child
+// gains focus
+GENERIC_FUNCTOR_1(CChildFocusListener, OnChildFocusChange, bool)
+
+#ifdef _WIN32
+typedef HWND TPlatformView;
+#else
+typedef void *TPlatformView;
+#endif
+
+class CControlWindowListener
+{
+public:
+ virtual void OnControlInvalidated() = 0;
+ virtual long DoPopup(QMenu *inMenu, CPt inLocation) = 0;
+ virtual CPt ClientToScreen(CPt inPoint) = 0;
+ virtual CPt ScreenToClient(CPt inPoint) = 0;
+ virtual TPlatformView GetPlatformView() = 0;
+ virtual void SetIsDragging(bool inIsDragging) = 0;
+ virtual void ShowTooltips(CPt inLocation, const Q3DStudio::CString &inText) = 0;
+ virtual void HideTooltips() = 0;
+ virtual void DoStartDrag(IDragable *inDragable) = 0;
+ virtual void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileList) = 0;
+ virtual void ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText,
+ CRct inBoundingRct) = 0;
+ virtual void HideMoveableWindow() = 0;
+};
+
+namespace Q3DStudio {
+namespace Control {
+ class CControlData;
+}
+}
+
+class CControl : public boost::BOOST_SIGNALS_NAMESPACE::trackable
+{
+
+ CControl(CControl &inOther);
+ CControl &operator=(CControl &inOther);
+
+public:
+ CControl();
+ virtual ~CControl();
+
+ DEFINE_OBJECT_COUNTER(CControl)
+
+ CControl *GetParent();
+ const CControl *GetParent() const;
+
+ virtual void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false);
+ virtual void Draw(CRenderer *inRenderer);
+ virtual void NotifyNotInClipRect();
+
+ virtual CPt GetPosition() const;
+ virtual void SetPosition(CPt inPosition);
+ void SetPosition(long inX, long inY);
+
+ virtual CPt GetSize() const;
+ virtual void SetSize(CPt inSize);
+ void SetSize(long inWidth, long inHeight);
+ virtual void OnSizeChanged(CPt inSize);
+
+ virtual CPt GetMinimumSize();
+ virtual void SetMinimumSize(CPt inSize);
+
+ virtual CPt GetMaximumSize();
+ virtual void SetMaximumSize(CPt inSize);
+
+ virtual CPt GetPreferredSize();
+ virtual void SetPreferredSize(CPt inSize);
+
+ virtual void SetAbsoluteSize(CPt inSize);
+
+ virtual void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual bool OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ /**
+ Note that just overriding this isn't enough, you need to call
+ m_ControlData->SetMouseWheelEnabled( true )
+ in order to receive mouse wheel events
+ */
+ virtual bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags);
+ virtual bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags);
+ virtual bool OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags);
+ virtual bool OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags);
+ virtual void OnLoseFocus();
+ virtual void OnGainFocus();
+ virtual bool CanGainFocus();
+ virtual bool IsInFocus();
+ void OnTab();
+ void OnReverseTab();
+ void SetFocusToFirstAvailable();
+ void SetFocusToLastAvailable();
+
+ virtual CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags);
+
+ virtual void AddChild(CControl *inControl, CControl *inInsertBefore = NULL);
+ virtual void RemoveChild(CControl *inControl);
+ virtual void RemoveAllChildren();
+ virtual long GetChildIndex(CControl *inChildControl);
+ virtual CControl *FindChildByName(const Q3DStudio::CString &inName);
+ CControl *FocusedChild();
+
+ virtual bool IsMouseOver() const;
+
+ virtual bool HitTest(const CPt &inPoint) const;
+ virtual bool IsInRect(const CRct &inRect) const;
+
+ virtual void Invalidate(bool inInvalidate = true);
+ virtual void InvalidateRect(const CRct &inRect);
+ virtual bool IsInvalidated() const;
+
+ virtual void OnChildInvalidated();
+ virtual bool IsChildInvalidated() const;
+
+ virtual void SetVisible(bool inIsVisible);
+ virtual bool IsVisible() const;
+ virtual void OnVisibleStateChange(bool inIsVisible);
+ virtual void OnParentVisibleStateChanged(bool inIsVisible);
+
+ virtual void SetParentEnabled(bool inParentEnabled);
+ virtual void SetEnabled(bool inIsEnabled);
+ virtual bool IsEnabled() const;
+ bool GetEnabledFlag();
+ void SetEnabledFlag(bool inIsEnabled);
+
+ void SetWindowListener(CControlWindowListener *inListener);
+ CControlWindowListener *GetWindowListener();
+
+ virtual void OnChildSizeChanged(CControl *inChild);
+ virtual void ResetMinMaxPref(){}
+
+ void SetName(const Q3DStudio::CString &inName);
+ Q3DStudio::CString GetName();
+
+ virtual void BeginDrawChildren(CRenderer *inRenderer);
+
+ virtual long DoPopup(QMenu *inContextMenu, CPt inPoint);
+ virtual void RemoveUberControl(CControl *inControl);
+ virtual void OffsetPosition(CPt inOffset);
+
+ virtual CPt GetGlobalPosition(CPt inChildPoint) const;
+ virtual UICRenderDevice GetPlatformDevice();
+ bool IsChildPlatformDevice(UICRenderDevice inDevice);
+ virtual void ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, CRct inBoundingRct);
+ virtual void HideMoveableWindow();
+ CControl *GetFirstChild();
+ bool HasFocus(CControl *inControl);
+ virtual void GrabFocus(CControl *inControl);
+
+ virtual CPt ClientToScreen(CPt inPoint);
+ virtual CPt ScreenToClient(CPt inPoint);
+
+ void AddFocusListener(CChildFocusListener *inListener);
+ void RemoveFocusListener(CChildFocusListener *inListener);
+ void FireFocusEvent(bool inStatus);
+
+ void SetTooltipText(const Q3DStudio::CString &inText);
+ Q3DStudio::CString GetTooltipText();
+
+ virtual void EnsureVisible(CRct inRect);
+ void EnsureVisible();
+
+ virtual void OnParentChanged(CControl *inNewParent);
+
+ virtual void MarkChildrenNeedLayout();
+ virtual void MarkNeedsLayout();
+ // Tell our parent that we (and all our siblings) need to be
+ // laid out.
+ virtual void NotifyParentNeedsLayout();
+ // Tell this control that one of its children need to be layed out.
+ virtual void SetLayout(CPt inSize, CPt inPosition);
+ virtual void LayoutChildren();
+ virtual void EnsureLayout();
+
+ virtual void ChildrenChanged();
+
+ Q3DStudio::Control::ControlGraph::SIterator GetChildren();
+ Q3DStudio::Control::ControlGraph::SReverseIterator GetReverseChildren();
+
+protected: ///< Current size of this control
+ // Member Functions
+
+ long GetChildCount();
+ TPlatformView GetPlatformView();
+ bool IsMouseDown();
+ void setCursorIfNotSet(long cursor);
+ void resetCursor();
+ QCursor getCursor() const;
+
+ std::shared_ptr<Q3DStudio::Control::CControlData> m_ControlData;
+ long m_cursorSet;
+};
+#endif // INCLUDED_CONTROL_H
diff --git a/src/Authoring/Studio/Controls/ControlData.cpp b/src/Authoring/Studio/Controls/ControlData.cpp
new file mode 100644
index 00000000..65122537
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ControlData.cpp
@@ -0,0 +1,763 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "ControlData.h"
+#include "ControlGraph.h"
+#include "Control.h"
+
+#include <QMenu>
+
+using namespace Q3DStudio::Control;
+using Q3DStudio::CString;
+
+CControlData::CControlData(CControl &inControl)
+ : m_Control(&inControl)
+ , m_MinSize(0, 0)
+ , m_MaxSize(LONG_MAX, LONG_MAX)
+ , m_PrefSize(10, 10)
+ , m_IsMouseOver(false)
+ , m_IsInvalidated(true)
+ , m_IsChildInvalidated(false)
+ , m_IsVisible(true)
+ , m_IsEnabled(true)
+ , m_IsParentEnabled(true)
+ , m_HasFocus(false)
+ , m_IsMouseDown(false)
+ , m_ShowTooltips(false)
+ , m_NeedsLayout(true)
+ , m_ChildrenNeedLayout(true)
+ , m_WindowListener(NULL)
+{
+}
+
+CControlData::~CControlData()
+{
+}
+
+void CControlData::ReleaseControl()
+{
+ if (m_Control) {
+ ControlGraph::RemoveNode(*m_Control);
+ m_Control = nullptr;
+ }
+}
+
+std::shared_ptr<CControlData> CControlData::CreateControlData(CControl &inControl)
+{
+ std::shared_ptr<CControlData> retval =
+ std::make_shared<CControlData>(std::ref(inControl));
+ ControlGraph::AddNode(retval);
+ return retval;
+}
+
+CControl *CControlData::GetControl()
+{
+ return m_Control;
+}
+
+CControl *CControlData::GetParent()
+{
+
+ if (m_Control) {
+ std::shared_ptr<CControlData> theParent(ControlGraph::GetParent(*m_Control));
+ if (theParent)
+ return theParent->GetControl();
+ }
+ return nullptr;
+}
+
+void CControlData::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ if (m_Control)
+ m_Control->OnDraw(inRenderer, inDirtyRect, inIgnoreValidation);
+}
+
+void CControlData::Draw(CRenderer *inRenderer)
+{
+ if (m_Control)
+ m_Control->Draw(inRenderer);
+}
+
+void CControlData::NotifyNotInClipRect()
+{
+ if (m_Control)
+ m_Control->NotifyNotInClipRect();
+}
+
+CPt CControlData::GetPosition() const
+{
+ if (m_Control)
+ return m_Control->GetPosition();
+ return CPt();
+}
+void CControlData::SetPosition(CPt inPosition)
+{
+ if (m_Control)
+ m_Control->SetPosition(inPosition);
+}
+void CControlData::SetPosition(long inX, long inY)
+{
+ SetPosition(CPt(inX, inY));
+}
+
+CPt CControlData::GetSize() const
+{
+ if (m_Control)
+ return m_Control->GetSize();
+ return CPt();
+}
+void CControlData::SetSize(CPt inSize)
+{
+ if (m_Control)
+ m_Control->SetSize(inSize);
+}
+void CControlData::SetSize(long inWidth, long inHeight)
+{
+ SetSize(CPt(inWidth, inHeight));
+}
+
+CPt CControlData::GetMinimumSize()
+{
+ if (m_Control)
+ return m_Control->GetMinimumSize();
+ return CPt();
+}
+void CControlData::SetMinimumSize(CPt inSize)
+{
+ if (m_Control)
+ m_Control->SetMinimumSize(inSize);
+}
+
+CPt CControlData::GetMaximumSize()
+{
+ if (m_Control)
+ return m_Control->GetMaximumSize();
+ return CPt();
+}
+void CControlData::SetMaximumSize(CPt inSize)
+{
+ if (m_Control)
+ m_Control->SetMaximumSize(inSize);
+}
+
+CPt CControlData::GetPreferredSize()
+{
+ if (m_Control)
+ return m_Control->GetPreferredSize();
+ return CPt();
+}
+void CControlData::SetPreferredSize(CPt inSize)
+{
+ if (m_Control)
+ return m_Control->SetPreferredSize(inSize);
+}
+
+void CControlData::SetAbsoluteSize(CPt inSize)
+{
+ if (m_Control)
+ m_Control->SetAbsoluteSize(inSize);
+}
+void CControlData::SetMouseDown(bool inMouseDown)
+{
+ m_IsMouseDown = inMouseDown;
+}
+
+void CControlData::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseMove(inPoint, inFlags);
+}
+void CControlData::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseOver(inPoint, inFlags);
+}
+void CControlData::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseOut(inPoint, inFlags);
+}
+bool CControlData::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnMouseDown(inPoint, inFlags);
+ return false;
+}
+bool CControlData::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnMouseRDown(inPoint, inFlags);
+ return false;
+}
+void CControlData::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseUp(inPoint, inFlags);
+}
+void CControlData::OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseRUp(inPoint, inFlags);
+}
+void CControlData::OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ m_Control->OnMouseClick(inPoint, inFlags);
+}
+bool CControlData::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnMouseDoubleClick(inPoint, inFlags);
+ return false;
+}
+bool CControlData::OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnMouseHover(inPoint, inFlags);
+ return false;
+}
+bool CControlData::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnMouseWheel(inPoint, inAmount, inFlags);
+ return false;
+}
+bool CControlData::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnKeyDown(inChar, inFlags);
+ return false;
+}
+bool CControlData::OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnKeyUp(inChar, inFlags);
+ return false;
+}
+bool CControlData::OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control)
+ return m_Control->OnChar(inChar, inFlags);
+ return false;
+}
+void CControlData::OnLoseFocus()
+{
+ if (m_Control) {
+ m_Control->OnLoseFocus();
+ }
+}
+void CControlData::OnGainFocus()
+{
+ if (m_Control) {
+ m_Control->OnGainFocus();
+ }
+}
+bool CControlData::CanGainFocus()
+{
+ if (m_Control) {
+ return m_Control->CanGainFocus();
+ }
+ return false;
+}
+bool CControlData::IsInFocus()
+{
+ if (m_Control) {
+ return m_Control->IsInFocus();
+ }
+ return false;
+}
+void CControlData::OnTab()
+{
+ if (m_Control) {
+ m_Control->OnTab();
+ }
+}
+void CControlData::OnReverseTab()
+{
+ if (m_Control) {
+ m_Control->OnReverseTab();
+ }
+}
+void CControlData::SetFocusToFirstAvailable()
+{
+ if (m_Control) {
+ m_Control->SetFocusToFirstAvailable();
+ }
+}
+void CControlData::SetFocusToLastAvailable()
+{
+ if (m_Control) {
+ m_Control->SetFocusToLastAvailable();
+ }
+}
+void CControlData::ChildrenChanged()
+{
+ if (m_Control)
+ m_Control->ChildrenChanged();
+}
+
+void CControlData::SetMouseWheelEnabled(bool inEnabled)
+{
+ if (m_Control == nullptr)
+ return;
+
+ if (m_MouseWheelState.m_Enabled != inEnabled) {
+ m_MouseWheelState.m_Enabled = inEnabled;
+ ControlEventState::Enum theNewState;
+ if (m_MouseWheelState.m_Enabled == false)
+ theNewState = ControlEventState::Unknown;
+ else
+ theNewState = ControlEventState::Listening;
+ SetMouseWheelEventState(theNewState);
+ }
+}
+
+void CControlData::OnHierarchyChanged()
+{
+ ControlEventState::Enum theNewState;
+ if (m_MouseWheelState.m_Enabled)
+ theNewState = ControlEventState::Listening;
+ else
+ theNewState = ControlEventState::Unknown;
+ SetMouseWheelEventState(theNewState);
+}
+
+/**
+ Look at hierarchy information to figure out which events should
+ trickle down the tree to here (or deeper). Recursive call that
+ may take some time to complete.
+*/
+void CControlData::UpdateMouseWheelEventState()
+{
+ if (m_Control == nullptr)
+ return;
+ ControlEventState::Enum theNewState(ControlEventState::Ignoring);
+ if (m_MouseWheelState.m_Enabled == true)
+ theNewState = ControlEventState::Listening;
+ else if (m_MouseWheelState.m_EventState == ControlEventState::Unknown && m_Control != nullptr) {
+ for (ControlGraph::SIterator theIter(ControlGraph::GetChildren(*m_Control));
+ theIter.IsDone() == false; ++theIter) {
+ if (theIter->GetMouseWheelEventState() == ControlEventState::Listening) {
+ theNewState = ControlEventState::Listening;
+ break;
+ }
+ }
+ }
+ SetMouseWheelEventState(theNewState);
+}
+
+/**
+ Update our mouse wheel state and notify our parent if necessary. Note that we
+ can't set our event state to ignoring without checking all of our children
+ so the result of this function is that our event state is either listening
+ or unknown.
+*/
+void CControlData::SetMouseWheelEventState(ControlEventState::Enum inNewState)
+{
+ if (m_MouseWheelState.m_EventState != inNewState) {
+ m_MouseWheelState.m_EventState = inNewState;
+ std::shared_ptr<CControlData> theParent = ControlGraph::GetParent(*m_Control);
+ if (theParent)
+ theParent->ChildMouseWheelEventStateChanged(m_MouseWheelState.m_EventState);
+ }
+}
+
+/**
+ When a given child notifies us that its event state has changed then we can update
+ our state. We can't set the state to ignoring unless we *know* all of our
+ children are ignoring mouse wheel.
+ */
+void CControlData::ChildMouseWheelEventStateChanged(ControlEventState::Enum inNewState)
+{
+ ControlEventState::Enum theNewState;
+ if (inNewState == ControlEventState::Listening || m_MouseWheelState.m_Enabled == true)
+ theNewState = ControlEventState::Listening;
+ else
+ theNewState = ControlEventState::Unknown;
+
+ SetMouseWheelEventState(theNewState);
+}
+
+ControlEventState::Enum CControlData::GetMouseWheelEventState()
+{
+ if (m_MouseWheelState.m_EventState == ControlEventState::Unknown)
+ UpdateMouseWheelEventState();
+ assert(m_MouseWheelState.m_EventState != ControlEventState::Unknown);
+ return m_MouseWheelState.m_EventState;
+}
+
+CDropTarget *CControlData::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_Control) {
+ return m_Control->FindDropCandidate(inMousePoint, inFlags);
+ }
+ return nullptr;
+}
+
+void CControlData::AddChild(CControl *inControl, CControl *inInsertBefore)
+{
+ if (m_Control)
+ m_Control->AddChild(inControl, inInsertBefore);
+}
+
+void CControlData::RemoveChild(CControl *inControl)
+{
+ if (m_Control)
+ m_Control->RemoveChild(inControl);
+}
+void CControlData::RemoveAllChildren()
+{
+ if (m_Control)
+ m_Control->RemoveAllChildren();
+}
+long CControlData::GetChildIndex(CControl *inChildControl)
+{
+ if (m_Control)
+ return m_Control->GetChildIndex(inChildControl);
+ return 0;
+}
+CControl *CControlData::FindChildByName(const Q3DStudio::CString &inName)
+{
+ if (m_Control) {
+ m_Control->FindChildByName(inName);
+ }
+ return nullptr;
+}
+
+bool CControlData::IsMouseOver() const
+{
+ if (m_Control) {
+ return m_Control->IsMouseOver();
+ }
+ return false;
+}
+
+bool CControlData::HitTest(const CPt &inPoint) const
+{
+ if (m_Control) {
+ return m_Control->HitTest(inPoint);
+ }
+ return false;
+}
+bool CControlData::IsInRect(const CRct &inRect) const
+{
+ if (m_Control) {
+ return m_Control->IsInRect(inRect);
+ }
+ return false;
+}
+
+void CControlData::Invalidate(bool inInvalidate)
+{
+ if (m_Control) {
+ m_Control->Invalidate(inInvalidate);
+ }
+}
+void CControlData::InvalidateRect(const CRct &inRect)
+{
+ if (m_Control) {
+ m_Control->InvalidateRect(inRect);
+ }
+}
+bool CControlData::IsInvalidated() const
+{
+ return m_IsInvalidated;
+}
+
+void CControlData::OnChildInvalidated()
+{
+ if (m_Control) {
+ m_Control->OnChildInvalidated();
+ }
+}
+bool CControlData::IsChildInvalidated() const
+{
+ if (m_Control) {
+ return m_Control->IsChildInvalidated();
+ }
+ return false;
+}
+
+void CControlData::SetVisible(bool inIsVisible)
+{
+ if (m_Control) {
+ m_Control->SetVisible(inIsVisible);
+ }
+}
+bool CControlData::IsVisible() const
+{
+ if (m_Control) {
+ return m_Control->IsVisible();
+ }
+ return false;
+}
+void CControlData::OnVisibleStateChange(bool inIsVisible)
+{
+ if (m_Control) {
+ m_Control->OnVisibleStateChange(inIsVisible);
+ }
+}
+void CControlData::OnParentVisibleStateChanged(bool inIsVisible)
+{
+ if (m_Control) {
+ m_Control->OnParentVisibleStateChanged(inIsVisible);
+ }
+}
+
+void CControlData::SetParentEnabled(bool inParentEnabled)
+{
+ if (m_Control) {
+ m_Control->SetParentEnabled(inParentEnabled);
+ }
+}
+void CControlData::SetEnabled(bool inIsEnabled)
+{
+ if (m_Control) {
+ m_Control->SetEnabled(inIsEnabled);
+ }
+}
+
+bool CControlData::IsEnabled() const
+{
+ return m_IsEnabled && m_IsParentEnabled;
+}
+bool CControlData::GetEnabledFlag()
+{
+ return m_IsEnabled;
+}
+void CControlData::SetEnabledFlag(bool inIsEnabled)
+{
+ m_IsEnabled = inIsEnabled;
+}
+
+void CControlData::SetWindowListener(CControlWindowListener *inListener)
+{
+ m_WindowListener = inListener;
+}
+CControlWindowListener *CControlData::GetWindowListener()
+{
+ return m_WindowListener;
+}
+
+void CControlData::OnChildSizeChanged(CControl *inChild)
+{
+ if (m_Control) {
+ m_Control->OnChildSizeChanged(inChild);
+ }
+}
+void CControlData::ResetMinMaxPref()
+{
+ if (m_Control) {
+ m_Control->ResetMinMaxPref();
+ }
+}
+
+void CControlData::SetName(CString inName)
+{
+ m_ControlName = inName;
+}
+CString CControlData::GetName()
+{
+ return m_ControlName;
+}
+
+void CControlData::BeginDrawChildren(CRenderer *inRenderer)
+{
+ if (m_Control) {
+ m_Control->BeginDrawChildren(inRenderer);
+ }
+}
+
+long CControlData::DoPopup(QMenu *inContextMenu, CPt inPoint)
+{
+ if (m_Control) {
+ return m_Control->DoPopup(inContextMenu, inPoint);
+ }
+ return 0;
+}
+void CControlData::RemoveUberControl(CControl *inControl)
+{
+ if (m_Control) {
+ m_Control->RemoveUberControl(inControl);
+ }
+}
+void CControlData::OffsetPosition(CPt inOffset)
+{
+ if (m_Control) {
+ m_Control->OffsetPosition(inOffset);
+ }
+}
+
+CPt CControlData::GetGlobalPosition(CPt inChildPoint) const
+{
+ if (m_Control) {
+ return m_Control->GetGlobalPosition(inChildPoint);
+ }
+ return CPt();
+}
+UICRenderDevice CControlData::GetPlatformDevice()
+{
+ if (m_Control) {
+ return m_Control->GetPlatformDevice();
+ }
+ return nullptr;
+}
+bool CControlData::IsChildPlatformDevice(UICRenderDevice inDevice)
+{
+ if (m_Control) {
+ return m_Control->IsChildPlatformDevice(inDevice);
+ }
+ return false;
+}
+void CControlData::ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct)
+{
+ if (m_Control) {
+ m_Control->ShowMoveableWindow(inLocation, inText, inBoundingRct);
+ }
+}
+void CControlData::HideMoveableWindow()
+{
+ if (m_Control) {
+ m_Control->HideMoveableWindow();
+ }
+}
+CControl *CControlData::GetFirstChild()
+{
+ if (m_Control) {
+ m_Control->GetFirstChild();
+ }
+ return nullptr;
+}
+bool CControlData::HasFocus(CControl *inControl)
+{
+ if (m_Control) {
+ return m_Control->HasFocus(inControl);
+ }
+ return false;
+}
+void CControlData::GrabFocus(CControl *inControl)
+{
+ if (m_Control) {
+ m_Control->GrabFocus(inControl);
+ }
+}
+
+CPt CControlData::ClientToScreen(CPt inPoint)
+{
+ if (m_Control) {
+ return m_Control->ClientToScreen(inPoint);
+ }
+ return CPt();
+}
+CPt CControlData::ScreenToClient(CPt inPoint)
+{
+ if (m_Control) {
+ return m_Control->ScreenToClient(inPoint);
+ }
+ return CPt();
+}
+
+void CControlData::AddFocusListener(CChildFocusListener *inListener)
+{
+ if (m_Control) {
+ m_Control->AddFocusListener(inListener);
+ }
+}
+void CControlData::RemoveFocusListener(CChildFocusListener *inListener)
+{
+ if (m_Control) {
+ m_Control->RemoveFocusListener(inListener);
+ }
+}
+void CControlData::FireFocusEvent(bool inStatus)
+{
+ if (m_Control) {
+ m_Control->FireFocusEvent(inStatus);
+ }
+}
+
+void CControlData::SetTooltipText(const Q3DStudio::CString &inText)
+{
+ m_TooltipText = inText;
+}
+CString CControlData::GetTooltipText()
+{
+ return m_TooltipText;
+}
+
+void CControlData::EnsureVisible(CRct inRect)
+{
+ if (m_Control) {
+ m_Control->EnsureVisible(inRect);
+ }
+}
+void CControlData::EnsureVisible()
+{
+ if (m_Control) {
+ m_Control->EnsureVisible();
+ }
+}
+
+void CControlData::OnParentChanged(CControl *inControl)
+{
+ if (m_Control)
+ m_Control->OnParentChanged(inControl);
+}
+
+void CControlData::NotifyParentNeedsLayout()
+{
+ if (m_Control)
+ m_Control->NotifyParentNeedsLayout();
+}
+void CControlData::MarkChildrenNeedLayout()
+{
+ if (m_Control)
+ m_Control->MarkChildrenNeedLayout();
+}
+void CControlData::MarkNeedsLayout()
+{
+ if (m_Control)
+ m_Control->MarkNeedsLayout();
+}
+// Tell this control that one of its children need to be layed out.
+void CControlData::SetLayout(CPt inSize, CPt inPosition)
+{
+ if (m_Control)
+ m_Control->SetLayout(inSize, inPosition);
+}
+void CControlData::LayoutChildren()
+{
+ if (m_Control)
+ m_Control->LayoutChildren();
+}
+void CControlData::EnsureLayout()
+{
+ if (m_Control)
+ m_Control->EnsureLayout();
+}
diff --git a/src/Authoring/Studio/Controls/ControlData.h b/src/Authoring/Studio/Controls/ControlData.h
new file mode 100644
index 00000000..83ebaf7d
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ControlData.h
@@ -0,0 +1,276 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef CONTROLDATAH
+#define CONTROLDATAH
+
+#include "DropTarget.h"
+#include "Pt.h"
+#include "Rct.h"
+#include "Multicaster.h"
+#include "UICString.h"
+
+class CControl;
+class CControlWindowListener;
+class CChildFocusListener;
+class CRenderer;
+
+class QMenu;
+
+namespace Q3DStudio {
+namespace Control {
+
+ using namespace std;
+
+ struct ControlEventState
+ {
+ enum Enum {
+ Unknown = 0,
+ Listening,
+ Ignoring,
+ };
+ };
+
+ struct SControlEventData
+ {
+ bool m_Enabled;
+ ControlEventState::Enum m_EventState;
+ SControlEventData()
+ : m_Enabled(false)
+ , m_EventState(ControlEventState::Ignoring)
+ {
+ }
+ };
+
+ // Smart pointer object that referees access to a control.
+ class CControlData
+ {
+ Q_DISABLE_COPY(CControlData)
+ public:
+ friend class ::CControl;
+ friend class std::shared_ptr<CControlData>;
+
+ private:
+ // The physical address of the control is the key in the graph
+ // that points to this data item.
+ CControl *m_Control;
+
+ CPt m_Size; ///< Current size of this control
+
+ CPt m_Position; ///< Position of this control, relative to the parent
+ CPt m_MinSize; ///< Minimum allowed size of this control
+ CPt m_MaxSize; ///< Maximum allowed size of this contrl
+ CPt m_PrefSize; ///< Preferred size of this control
+
+ bool m_IsMouseOver; ///< True if the mouse is over this control
+ bool m_IsInvalidated; ///< True if this control needs to be redrawn
+ bool m_IsChildInvalidated; ///< True if a child of this control is invalidated.
+ bool m_IsVisible; ///< True if this control is to be visible
+ bool m_IsEnabled; ///< True if this control is enabled.
+ bool m_IsParentEnabled;
+ bool m_HasFocus;
+ bool m_IsMouseDown;
+ bool m_ShowTooltips; ///< Specifies whether or not tooltips should be shown
+ bool m_NeedsLayout; ///< True if this control needs to be layed out.
+ bool m_ChildrenNeedLayout; ///< True if my children need layout
+ SControlEventData
+ m_MouseWheelState; ///< Tracks whether this object cares about mouse wheel.
+
+ Q3DStudio::CString m_TooltipText; ///< Text to be displayed for tooltips
+
+ std::shared_ptr<CControlData> m_Focus; ///< Child control that has the focus.
+ std::shared_ptr<CControlData> m_MouseFocus; ///< Child control that got the mouse down.
+ CControlWindowListener
+ *m_WindowListener; ///< External listener for when this control is invalidated.
+
+ Q3DStudio::CString m_ControlName;
+ CMulticaster<CChildFocusListener *> m_FocusListeners; ///< Used for focus changes
+
+ public:
+ CControlData(CControl &inControl);
+ ~CControlData();
+
+ CControl *GetControl();
+
+ CControl *GetParent();
+
+ void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false);
+ void Draw(CRenderer *inRenderer);
+ void NotifyNotInClipRect();
+
+ CPt GetPosition() const;
+ void SetPosition(CPt inPosition);
+ void SetPosition(long inX, long inY);
+
+ CPt GetSize() const;
+ void SetSize(CPt inSize);
+ void SetSize(long inWidth, long inHeight);
+
+ CPt GetMinimumSize();
+ void SetMinimumSize(CPt inSize);
+
+ CPt GetMaximumSize();
+ void SetMaximumSize(CPt inSize);
+
+ CPt GetPreferredSize();
+ void SetPreferredSize(CPt inSize);
+
+ void SetAbsoluteSize(CPt inSize);
+
+ void SetMouseDown(bool inMouseDown);
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ void OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ bool OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags);
+ bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags);
+ bool OnKeyUp(unsigned int inChar, Qt::KeyboardModifiers inFlags);
+ bool OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags);
+ void OnLoseFocus();
+ void OnGainFocus();
+ bool CanGainFocus();
+ bool IsInFocus();
+ void OnTab();
+ void OnReverseTab();
+ void SetFocusToFirstAvailable();
+ void SetFocusToLastAvailable();
+ void ChildrenChanged();
+
+ // Mouse wheel event state.
+ void SetMouseWheelEnabled(bool inEnabled);
+ /**
+ Callback so we can update any event states to unknown
+ from ignoring forcing a refresh of hierarchy event information
+ the next time the event state is queried.
+ */
+ void OnHierarchyChanged();
+ /**
+ If our event state is unknown, force resolution by asking children what their
+ event state is. Else return our event state.
+ */
+ ControlEventState::Enum GetMouseWheelEventState();
+
+ protected:
+ void UpdateMouseWheelEventState();
+ void ChildMouseWheelEventStateChanged(ControlEventState::Enum inNewState);
+ void SetMouseWheelEventState(ControlEventState::Enum inNewState);
+
+ public:
+ CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags);
+
+ void AddChild(CControl *inControl, CControl *inInsertBefore = NULL);
+ void RemoveChild(CControl *inControl);
+ void RemoveAllChildren();
+ long GetChildIndex(CControl *inChildControl);
+ CControl *FindChildByName(const Q3DStudio::CString &inName);
+
+ bool IsMouseOver() const;
+
+ bool HitTest(const CPt &inPoint) const;
+ bool IsInRect(const CRct &inRect) const;
+
+ void Invalidate(bool inInvalidate = true);
+ void InvalidateRect(const CRct &inRect);
+ bool IsInvalidated() const;
+
+ void OnChildInvalidated();
+ bool IsChildInvalidated() const;
+
+ void SetVisible(bool inIsVisible);
+ bool IsVisible() const;
+ void OnVisibleStateChange(bool inIsVisible);
+ void OnParentVisibleStateChanged(bool inIsVisible);
+
+ void SetParentEnabled(bool inParentEnabled);
+ void SetEnabled(bool inIsEnabled);
+ bool IsEnabled() const;
+ bool GetEnabledFlag();
+ void SetEnabledFlag(bool inIsEnabled);
+
+ void SetWindowListener(CControlWindowListener *inListener);
+ CControlWindowListener *GetWindowListener();
+
+ void OnChildSizeChanged(CControl *inChild);
+ void ResetMinMaxPref();
+
+ void SetName(Q3DStudio::CString inName);
+ Q3DStudio::CString GetName();
+
+ void BeginDrawChildren(CRenderer *inRenderer);
+
+ long DoPopup(QMenu *inContextMenu, CPt inPoint);
+ void RemoveUberControl(CControl *inControl);
+ void OffsetPosition(CPt inOffset);
+
+ CPt GetGlobalPosition(CPt inChildPoint) const;
+ UICRenderDevice GetPlatformDevice();
+ bool IsChildPlatformDevice(UICRenderDevice inDevice);
+ void ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct);
+ void HideMoveableWindow();
+ CControl *GetFirstChild();
+ bool HasFocus(CControl *inControl);
+ void GrabFocus(CControl *inControl);
+
+ CPt ClientToScreen(CPt inPoint);
+ CPt ScreenToClient(CPt inPoint);
+
+ void AddFocusListener(CChildFocusListener *inListener);
+ void RemoveFocusListener(CChildFocusListener *inListener);
+ void FireFocusEvent(bool inStatus);
+
+ void SetTooltipText(const Q3DStudio::CString &inText);
+ Q3DStudio::CString GetTooltipText();
+
+ void EnsureVisible(CRct inRect);
+ void EnsureVisible();
+
+ void OnParentChanged(CControl *inControl);
+
+ void NotifyParentNeedsLayout();
+ void MarkChildrenNeedLayout();
+ void MarkNeedsLayout();
+ // Tell this control that one of its children need to be layed out.
+ void SetLayout(CPt inSize, CPt inPosition);
+ void LayoutChildren();
+ void EnsureLayout();
+
+ private:
+ static std::shared_ptr<CControlData> CreateControlData(CControl &inControl);
+ void ReleaseControl(); // set the control to null, remove our graph representation
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Controls/ControlGraph.cpp b/src/Authoring/Studio/Controls/ControlGraph.cpp
new file mode 100644
index 00000000..912bd1a0
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ControlGraph.cpp
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "ControlGraph.h"
+#include "GraphImpl.h"
+#include "foundation/Qt3DSAssert.h"
+#include "Control.h"
+#include "ControlData.h"
+
+using namespace std;
+using namespace Q3DStudio;
+using namespace Q3DStudio::Graph;
+using namespace Q3DStudio::Control;
+using Q3DStudio::CString;
+
+namespace {
+typedef SGraphImpl<CControl *, std::shared_ptr<CControlData>> TGraphType;
+
+TGraphType g_ControlGraph;
+}
+
+namespace Q3DStudio {
+namespace Control {
+ namespace ControlGraph {
+
+ void AddNode(std::shared_ptr<CControlData> inData)
+ {
+ g_ControlGraph.AddRoot(inData->GetControl());
+ g_ControlGraph.SetData(inData->GetControl(), inData);
+ }
+
+ void RemoveNode(CControl &inData) { g_ControlGraph.RemoveChild(&inData, true); }
+
+ void AddChild(CControl &inParent, CControl &inChild, CControl *inNextSibling)
+ {
+ std::shared_ptr<CControlData> oldParent = GetParent(inChild);
+ TGraphType::TNodePtr theParent(g_ControlGraph.GetImpl(&inParent));
+
+ // It actually happens that sometimes inChild gets added with a sibling
+ // but the sibling hasn't actually been added yet.
+ if (inNextSibling != nullptr) {
+ TGraphType::TNodePtr theSibling(g_ControlGraph.GetImpl(inNextSibling));
+ if (theSibling->m_Parent == theParent)
+ g_ControlGraph.MoveBefore(&inChild, inNextSibling);
+ else
+ g_ControlGraph.AddChild(&inParent, &inChild, SGraphPosition::SEnd());
+ } else
+ g_ControlGraph.AddChild(&inParent, &inChild, SGraphPosition::SEnd());
+
+ if (!oldParent || oldParent->GetControl() != &inParent) {
+ inChild.OnParentChanged(&inParent);
+ if (oldParent)
+ oldParent->ChildrenChanged();
+ }
+ inChild.Invalidate();
+ inParent.ChildrenChanged();
+ }
+ // inParent is supplied for error checking purposes.
+ void RemoveChild(CControl &inParent, CControl &inChild)
+ {
+ inChild.OnParentChanged(nullptr);
+ g_ControlGraph.RemoveChild(&inParent, &inChild, false);
+ inChild.Invalidate();
+ inParent.ChildrenChanged();
+ }
+ void RemoveAllChildren(CControl &inParent)
+ {
+ TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inParent));
+ if (theNode == nullptr)
+ return;
+ while (theNode->m_Children.empty() == false) {
+ theNode->m_Children.back()->m_Data->OnParentChanged(nullptr);
+ theNode->m_Children.back()->m_Data->NotifyParentNeedsLayout();
+ g_ControlGraph.RemoveChild(&inParent, theNode->m_Children.back()->m_GraphableID,
+ false);
+ }
+ inParent.ChildrenChanged();
+ }
+ void MoveTo(CControl &inParent, CControl &inChild, const Graph::SGraphPosition &inPosition)
+ {
+ std::shared_ptr<CControlData> theOldParent(GetParent(inChild));
+ g_ControlGraph.MoveTo(&inParent, &inChild, inPosition);
+ if (!theOldParent || theOldParent->GetControl() != &inParent)
+ inChild.OnParentChanged(&inParent);
+ inChild.NotifyParentNeedsLayout();
+ inParent.MarkChildrenNeedLayout();
+ if (theOldParent) {
+ theOldParent->ChildrenChanged();
+ }
+ inParent.ChildrenChanged();
+ inChild.Invalidate();
+ }
+ long GetNumChildren(CControl &inControl)
+ {
+ return g_ControlGraph.GetChildCount(&inControl);
+ }
+
+ long GetChildIndex(CControl &inParent, CControl &inChild)
+ {
+ QT3DS_ASSERT(GetParent(inChild)->GetControl() == &inParent);
+
+ SGraphPosition thePos = g_ControlGraph.GetNodePosition(&inChild);
+ return thePos.GetIndex();
+ }
+
+ std::shared_ptr<CControlData> GetChild(CControl &inParent, long inIndex)
+ {
+ TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inParent));
+ if (theNode && inIndex < (long)theNode->m_Children.size() && inIndex > -1)
+ return theNode->m_Children[inIndex]->m_Data;
+ return std::shared_ptr<CControlData>();
+ }
+
+ std::shared_ptr<CControlData> GetParent(CControl &inControl)
+ {
+ TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl));
+ if (theNode && theNode->m_Parent)
+ return theNode->m_Parent->m_Data;
+ return std::shared_ptr<CControlData>();
+ }
+
+ // Dummy struct to hide graph implementation
+ struct SDummyGraphNode : public TGraphType::TNodeType
+ {
+ SDummyGraphNode(CControl *const &inIdentifier,
+ const std::shared_ptr<CControlData> &inNodeData =
+ std::shared_ptr<CControlData>())
+ : TGraphType::TNodeType(inIdentifier, inNodeData)
+ {
+ }
+ };
+
+ std::shared_ptr<CControlData> SIteratorBase::GetCurrent()
+ {
+ if (m_Children && m_Index < (long)m_Children->size())
+ return (*m_Children)[m_Index]->m_Data;
+
+ QT3DS_ASSERT(false);
+ return std::shared_ptr<CControlData>();
+ }
+
+ SReverseIterator GetRChildren(CControl &inControl)
+ {
+ TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl));
+ if (theNode)
+ return SReverseIterator(
+ theNode->m_Data,
+ reinterpret_cast<vector<SDummyGraphNode *> &>(theNode->m_Children));
+ return SReverseIterator();
+ }
+
+ SIterator GetChildren(CControl &inControl)
+ {
+ TGraphType::TNodePtr theNode(g_ControlGraph.GetImpl(&inControl));
+ if (theNode)
+ return SIterator(theNode->m_Data, reinterpret_cast<vector<SDummyGraphNode *> &>(
+ theNode->m_Children));
+ return SIterator();
+ }
+ }
+}
+}
diff --git a/src/Authoring/Studio/Controls/ControlGraph.h b/src/Authoring/Studio/Controls/ControlGraph.h
new file mode 100644
index 00000000..0dfe2b30
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ControlGraph.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef CONTROLGRAPHH
+#define CONTROLGRAPHH
+#include "Multicaster.h"
+#include "ControlGraphIterators.h"
+
+class CControl;
+class CControlWindowListener;
+class CChildFocusListener;
+class CRenderer;
+
+namespace Q3DStudio {
+namespace Graph {
+
+ struct SGraphPosition; // GraphPosition.h"
+}
+
+namespace Control {
+ class ControlData;
+
+ namespace ControlGraph {
+ void AddNode(std::shared_ptr<CControlData> inData);
+ void RemoveNode(CControl &inData);
+ void AddChild(CControl &inParent, CControl &inChild, CControl *inNextSibling);
+ // inParent is supplied for error checking purposes.
+ void RemoveChild(CControl &inParent, CControl &inChild);
+ void RemoveAllChildren(CControl &inParent);
+ void MoveTo(CControl &inParent, CControl &inChild, const Graph::SGraphPosition &inPosition);
+ long GetNumChildren(CControl &inControl);
+ long GetChildIndex(CControl &inParent, CControl &inChild);
+ std::shared_ptr<CControlData> GetChild(CControl &inParent, long inIndex);
+ std::shared_ptr<CControlData> GetParent(CControl &inControl);
+
+ SReverseIterator GetRChildren(CControl &inControl);
+ SIterator GetChildren(CControl &inControl);
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Controls/ControlGraphIterators.h b/src/Authoring/Studio/Controls/ControlGraphIterators.h
new file mode 100644
index 00000000..ae2ffcf6
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ControlGraphIterators.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef CONTROLGRAPHITERATORSH
+#define CONTROLGRAPHITERATORSH
+#include <vector>
+
+namespace Q3DStudio {
+namespace Control {
+ class CControlData;
+ using std::vector;
+
+ namespace ControlGraph {
+ struct SDummyGraphNode;
+
+ struct SIteratorBase
+ {
+ protected:
+ std::shared_ptr<CControlData> m_ControlData;
+ vector<SDummyGraphNode *> *m_Children;
+ long m_Index;
+
+ public:
+ SIteratorBase()
+ : m_Children(nullptr)
+ , m_Index(0)
+ {
+ }
+
+ SIteratorBase(std::shared_ptr<CControlData> data,
+ vector<SDummyGraphNode *> &inChildren)
+ : m_ControlData(data)
+ , m_Children(&inChildren)
+ , m_Index(0)
+ {
+ }
+
+ SIteratorBase(const SIteratorBase &inOther)
+ : m_ControlData(inOther.m_ControlData)
+ , m_Children(inOther.m_Children)
+ , m_Index(inOther.m_Index)
+ {
+ }
+
+ SIteratorBase &operator=(const SIteratorBase &inOther)
+ {
+ if (this != &inOther) {
+ m_ControlData = inOther.m_ControlData;
+ m_Children = inOther.m_Children;
+ m_Index = inOther.m_Index;
+ }
+ return *this;
+ }
+
+ bool operator==(const SIteratorBase &other) { return m_Index == other.m_Index; }
+ bool operator!=(const SIteratorBase &other) { return m_Index != other.m_Index; }
+
+ std::shared_ptr<CControlData> GetCurrent();
+
+ std::shared_ptr<CControlData> operator->() { return GetCurrent(); }
+ std::shared_ptr<CControlData> operator*() { return GetCurrent(); }
+ };
+
+ struct SReverseIterator : SIteratorBase
+ {
+ SReverseIterator() {}
+ SReverseIterator(std::shared_ptr<CControlData> data,
+ vector<SDummyGraphNode *> &inChildren)
+ : SIteratorBase(data, inChildren)
+ {
+ m_Index = (long)inChildren.size() - 1;
+ }
+ SReverseIterator(const SReverseIterator &inOther)
+ : SIteratorBase(inOther)
+ {
+ }
+ SReverseIterator &operator=(const SReverseIterator &inOther)
+ {
+ SIteratorBase::operator=(inOther);
+ return *this;
+ }
+
+ bool IsDone() { return m_Children == nullptr || m_Index < 0; }
+ bool HasNext() { return m_Children && m_Index > -1; }
+ SReverseIterator &operator++()
+ {
+ --m_Index;
+ return *this;
+ }
+ SReverseIterator &operator+=(long inAmount)
+ {
+ m_Index -= inAmount;
+ return *this;
+ }
+ };
+
+ struct SIterator : SIteratorBase
+ {
+ SIterator() {}
+ SIterator(std::shared_ptr<CControlData> data, vector<SDummyGraphNode *> &inChildren)
+ : SIteratorBase(data, inChildren)
+ {
+ }
+ SIterator(const SReverseIterator &inOther)
+ : SIteratorBase(inOther)
+ {
+ }
+ SIterator &operator=(const SIterator &inOther)
+ {
+ SIteratorBase::operator=(inOther);
+ return *this;
+ }
+
+ bool IsDone() { return m_Children == nullptr || m_Index >= (long)m_Children->size(); }
+ bool HasNext() { return m_Children && m_Index < (long)m_Children->size(); }
+ SIterator operator++()
+ {
+ ++m_Index;
+ return *this;
+ }
+ SIterator &operator+=(long inAmount)
+ {
+ m_Index += inAmount;
+ return *this;
+ }
+ };
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/EditInPlace.h b/src/Authoring/Studio/Controls/EditInPlace.h
new file mode 100644
index 00000000..f4497210
--- /dev/null
+++ b/src/Authoring/Studio/Controls/EditInPlace.h
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 Anark Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_EDIT_IN_PLACE_H
+#define INCLUDED_EDIT_IN_PLACE_H 1
+
+#pragma once
+
+#include "Renderer.h"
+#include "Pt.h"
+#include "StudioPreferences.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+template <class T>
+class CEditInPlace : public T
+{
+public:
+ CEditInPlace();
+ virtual ~CEditInPlace();
+
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnLoseFocus() override;
+ void OnGainFocus() override;
+ void Draw(CRenderer *inRenderer) override;
+ void SetEditable(bool inIsEditable);
+ void SetEditMode(bool inEditMode);
+ void EnterText(bool inHighlight) override;
+ void Invalidate(bool inInvalidate = true) override;
+ bool GetEditMode();
+ static long GetRightBuffer();
+
+protected:
+ bool m_IsInEditMode; ///< If in edit mode, the user is currently editing the control
+ bool m_IsEditable; ///< Can only enter edit mode when the control is editable
+
+private:
+ static const long s_RightBuffer = 6;
+};
+
+template <class T>
+CEditInPlace<T>::CEditInPlace()
+ : T()
+ , m_IsInEditMode(false)
+ , m_IsEditable(true)
+{
+ T::SetReadOnly(true);
+ T::SetFillBackground(false);
+}
+
+template <class T>
+CEditInPlace<T>::~CEditInPlace()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the mouse double-click event. Enters the text into edit mode so that
+ * the user can change its value.
+ * @param inPoint location of the mouse
+ * @param inFlags modifier flags for the mouse
+ * @return true if this message should not be passed to any other children, otherwise false
+ */
+template <class T>
+bool CEditInPlace<T>::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_IsEditable && CStudioPreferences::IsSudoMode()) {
+ SetEditMode(true);
+ this->SetSelection(0, T::GetString().Length());
+ return true;
+ }
+ return T::OnMouseDoubleClick(inPoint, inFlags);
+}
+
+template <class T>
+bool CEditInPlace<T>::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (T::GetParent()->HasFocus(this) && !(inFlags & CHotKeys::MOUSE_RBUTTON))
+ SetEditMode(true);
+ return T::OnMouseDown(inPoint, inFlags);
+}
+
+//==============================================================================
+/**
+ * Called when this control loses focus. Turns off edit mode and redraws the
+ * control.
+ */
+template <class T>
+void CEditInPlace<T>::OnLoseFocus()
+{
+ T::OnLoseFocus();
+ SetEditMode(false);
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Handles most of the drawing, with some help from the parent class.
+ * @param inRenderer Renderer to draw to
+ */
+template <class T>
+void CEditInPlace<T>::Draw(CRenderer *inRenderer)
+{
+ CRct theRect = CRct(T::GetSize()); // CRct( CPt( 0, 0 ), CPt( CalculateCharWidths( inRenderer ) +
+ // s_RightBuffer, GetSize( ).y ) );
+ CColor theOutlineColor = CColor(0, 0, 0);
+
+ T::SetAbsoluteSize(theRect.size);
+
+ bool theFillFlag = T::m_FillBackground;
+
+ if (theFillFlag && !m_IsInEditMode) {
+ T::SetFillBackground(false);
+ inRenderer->FillSolidRect(theRect, T::m_BackgroundColorNoFocus);
+ }
+
+ if (m_IsInEditMode)
+ T::SetTextColor(CColor(0, 0, 0));
+
+ inRenderer->PushClippingRect(theRect);
+ T::Draw(inRenderer);
+ inRenderer->PopClippingRect();
+
+ if (!m_IsInEditMode)
+ T::SetFillBackground(theFillFlag);
+
+ if (m_IsInEditMode)
+ inRenderer->DrawRectOutline(theRect, theOutlineColor, theOutlineColor, theOutlineColor,
+ theOutlineColor);
+}
+
+//==============================================================================
+/**
+ * Enables or disables this control from being able to enter "Edit Mode" when
+ * SetEditMode() is called.
+ * @param inIsEditable true if you want the control to be editable when double-clicked, otherwise
+ * false
+ */
+template <class T>
+void CEditInPlace<T>::SetEditable(bool inIsEditable)
+{
+ m_IsEditable = inIsEditable;
+
+ if (!m_IsEditable && m_IsInEditMode)
+ SetEditMode(false);
+}
+
+//==============================================================================
+/**
+ * Starts or stops "Edit Mode". While in Edit Mode, the user can change the
+ * text, move the caret, and highlight the text. An edit box is also drawn
+ * around the text.
+ * @param inEditMode true to turn on Edit Mode, false to turn off Edit Mode
+ */
+template <class T>
+void CEditInPlace<T>::SetEditMode(bool inEditMode)
+{
+ if (m_IsEditable) {
+ m_IsInEditMode = inEditMode;
+ T::SetFillBackground(!m_IsInEditMode);
+ T::SetReadOnly(!m_IsInEditMode);
+ if (!m_IsInEditMode)
+ T::FireCommitEvent();
+ }
+}
+
+//==============================================================================
+/**
+ * Overriden from the parent to cause this control to lose focus when the Enter
+ * button is pressed on the keyboard.
+ * @param inHighlight true to highlight all of the text, false to just change the string
+ */
+template <class T>
+void CEditInPlace<T>::EnterText(bool inHighlight)
+{
+ T::EnterText(inHighlight);
+ OnLoseFocus();
+}
+
+//==============================================================================
+/**
+ * Overriden to also invalidate/validate the parent.
+ * @param inInvalidate true to invalidate this control and the parent control
+ */
+/*
+template<class T>
+void CEditInPlace<T>::Invalidate( bool inInvalidate )
+{
+ if ( inInvalidate && GetParent() )
+ GetParent()->Invalidate( inInvalidate );
+
+ CControl::Invalidate( inInvalidate );
+}*/
+
+//==============================================================================
+/**
+ * Override to avoid selecting all the text unless in edit mode.
+ */
+template <class T>
+void CEditInPlace<T>::OnGainFocus()
+{
+ if (m_IsInEditMode && m_IsEditable)
+ T::OnGainFocus();
+}
+
+template <class T>
+bool CEditInPlace<T>::GetEditMode()
+{
+ return m_IsInEditMode;
+}
+
+template <class T>
+long CEditInPlace<T>::GetRightBuffer()
+{
+ return s_RightBuffer;
+}
+
+template <class T>
+bool CEditInPlace<T>::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers)
+{
+ bool theReturn = false;
+
+ if (m_IsInEditMode) {
+ T::DisplayContextMenu(inPoint);
+ theReturn = true;
+ }
+
+ return theReturn;
+}
+
+//==============================================================================
+/**
+ * Overriden to also invalidate/validate the parent.
+ * @param inInvalidate true to invalidate this control and the parent control
+ */
+template <class T>
+void CEditInPlace<T>::Invalidate(bool inInvalidate)
+{
+ if (inInvalidate && T::GetParent())
+ T::GetParent()->Invalidate(inInvalidate);
+
+ T::Invalidate(inInvalidate);
+}
+
+#endif // INCLUDED_EDIT_IN_PLACE_H
diff --git a/src/Authoring/Studio/Controls/FloatEdit.cpp b/src/Authoring/Studio/Controls/FloatEdit.cpp
new file mode 100644
index 00000000..e3dc9c2f
--- /dev/null
+++ b/src/Authoring/Studio/Controls/FloatEdit.cpp
@@ -0,0 +1,524 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+
+#include "StringTokenizer.h"
+#include "StringLoader.h"
+//==============================================================================
+// Include
+//==============================================================================
+#include "FloatEdit.h"
+#include "MouseCursor.h"
+#include "ResourceCache.h"
+#include "StudioClipboard.h"
+#include "ControlData.h"
+#include "UICMath.h"
+
+#include <QApplication>
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CFloatEdit::CFloatEdit()
+ : m_NumDecimalPlaces(3)
+ , m_FixedPlaces(-1)
+ , m_IsMouseDown(false)
+ , m_Min(-FLT_MAX)
+ , m_Max(FLT_MAX)
+ , m_EditMode(false)
+ , m_RevertListener(NULL)
+{
+ m_ControlData->SetMouseWheelEnabled(true);
+ // Set the default string
+ SetData(0.0f, false);
+
+ SetReadOnly(true);
+ SetName("FloatEdit");
+
+ // TODO: Used only for timeline palette timebar. It is not editable, we do not need
+ // to register any events. Registering minus here will prevent it from working for zooming
+ // out in the timeline.
+ // TODO: This whole class will most likely be removed when timeline is converted to Qt.
+// m_CommandHandler.RegisterKeyEvent(
+// new CDynHotKeyConsumer<CFloatEdit>(this, &CFloatEdit::AddCharNegative), 0, '-');
+// m_CommandHandler.RegisterKeyEvent(
+// new CDynHotKeyConsumer<CFloatEdit>(this, &CFloatEdit::AddCharPeriod), 0, '.');
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CFloatEdit::~CFloatEdit()
+{
+}
+
+void CFloatEdit::AddCharNegative()
+{
+ Q3DStudio::CString theMinusString = ::LoadResourceString(IDS_CONTROLS_FLOAT_MINUS);
+ unsigned int theMinus = theMinusString[0];
+
+ InsertChar(theMinus);
+}
+
+void CFloatEdit::AddCharPeriod()
+{
+ Q3DStudio::CString thePeriodString = ::LoadResourceString(IDS_CONTROLS_FLOAT_PERIOD);
+ unsigned int thePeriod = thePeriodString[0];
+
+ InsertChar(thePeriod);
+}
+
+void CFloatEdit::FormatString()
+{
+ Q3DStudio::CString theFormatString;
+ if (m_FixedPlaces == -1)
+ theFormatString.Format(_UIC("%%.%df"), m_NumDecimalPlaces);
+ else
+ theFormatString.Format(_UIC("%%0%d.%df"), m_FixedPlaces, m_NumDecimalPlaces);
+
+ m_ValueString.Format(theFormatString, m_Value);
+}
+
+//==============================================================================
+/**
+ * Converts the internal (float) data of this control into a string that can be
+ * displayed. The parent class (CTextEdit) calls this function when it needs
+ * to redraw the control and the text within it.
+ *
+ * @return the string to be displayed.
+ */
+Q3DStudio::CString CFloatEdit::GetString()
+{
+ return m_ValueString;
+}
+
+//==============================================================================
+/**
+ * Sets the internal data of this control the specified value and marks the
+ * control as dirty so that it gets redrawn during the next free cycle.
+ *
+ * @param inValue the new value for this control
+ * @param inFireEvent true to fire a property change event
+ */
+void CFloatEdit::SetData(float inValue, bool inFireEvent /* = true */)
+{
+ m_Value = inValue;
+
+ FormatString();
+ SetDisplayString(GetString(), inFireEvent);
+ if (inFireEvent)
+ SetDirty(true);
+}
+
+//==============================================================================
+/**
+ * Attempts to set the internal data of this control to the specified string.
+ *
+ * @param inData a string containing the new data
+ */
+void CFloatEdit::SetData(const Q3DStudio::CString &inData, bool inFireEvent /*= true*/)
+{
+ m_Value = static_cast<float>(::atof(inData.GetCharStar()));
+
+ FormatString();
+ // The string needs to be set precisely here, otherwise
+ // we end up truncating - and . from the floats
+ SetDisplayString(inData, inFireEvent);
+ SetDirty(true);
+}
+
+//==============================================================================
+/**
+ * Determines if the specified character can be inserted into the string. This
+ * control only accepts numbers, decimals, and minus signs. Further, a minus
+ * sign may only be inserted at the front of the string if there is not already
+ * one in the string and a decimal may only be inserted if there is not one in
+ * the string already.
+ *
+ * @param inCheckString the string that is to be checked (in case a lenght requirement is exceeded)
+ * @param inChar the character that was pressed
+ * @param inPosition character index where we are attempting to insert inChar into inCheckString
+ * @return true if this control can accept the character into the string, otherwise false
+ */
+bool CFloatEdit::CanAcceptChar(const Q3DStudio::CString &inCheckString, unsigned int inChar,
+ unsigned int inPosition)
+{
+ bool theRetVal = false;
+ Q3DStudio::CString thePeriodString = ::LoadResourceString(IDS_CONTROLS_FLOAT_PERIOD);
+ Q3DStudio::CString theMinusString = ::LoadResourceString(IDS_CONTROLS_FLOAT_MINUS);
+
+ unsigned int thePeriod = thePeriodString[0];
+ unsigned int theMinus = theMinusString[0];
+
+ if ((CTextEdit::CanAcceptChar(inCheckString, inChar, inPosition) && inCheckString.Length() < 10)
+ || inChar == thePeriod || inChar == theMinus) {
+ if (inChar >= '0' && inChar <= '9')
+ theRetVal = true;
+ if (!theRetVal) {
+ if (thePeriod == inChar) {
+ if (inCheckString.Find(char(thePeriod)) == Q3DStudio::CString::ENDOFSTRING)
+ theRetVal = true;
+ else {
+ theRetVal = false;
+ }
+ } else if (theMinus == inChar) {
+ if ((inCheckString.Find(char(theMinus)) == Q3DStudio::CString::ENDOFSTRING)
+ && (inPosition == 0))
+ theRetVal = true;
+ }
+ }
+ }
+ return theRetVal;
+}
+
+bool CFloatEdit::HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool isLargeStep = inFlags & CHotKeys::MODIFIER_SHIFT ? true : false;
+ bool isSmallStep = inFlags & CHotKeys::MODIFIER_CONTROL ? true : false;
+
+ bool wasHandled = false;
+ switch (inChar) {
+ case Qt::Key_Up:
+ if (isLargeStep)
+ SetFloatValue(m_Value + 10.0f);
+ else if (isSmallStep)
+ SetFloatValue(static_cast<float>(m_Value + 0.1f));
+ else
+ SetFloatValue(static_cast<float>(m_Value + 1.0f));
+ wasHandled = true;
+ break;
+
+ case Qt::Key_Down:
+ if (isLargeStep)
+ SetFloatValue(static_cast<float>(m_Value - 10.0f));
+ else if (isSmallStep)
+ SetFloatValue(static_cast<float>(m_Value - 0.1f));
+ else
+ SetFloatValue(m_Value - 1.0f);
+ wasHandled = true;
+ break;
+
+ case Qt::Key_Enter:
+ ExitEditMode();
+ wasHandled = true;
+ CTextEdit::OnLoseFocus();
+ break;
+
+ case Qt::Key_Escape:
+ if (m_RevertListener)
+ m_RevertListener->OnDiscardChanges(this);
+ ExitEditMode();
+ wasHandled = true;
+ CTextEdit::OnLoseFocus();
+ break;
+
+ /*
+ case CHotKeys::KEY_SUBTRACT:
+ case CHotKeys::KEY_SUBTRACT_OEM:
+ case CHotKeys::KEY_PERIOD_OEM:
+ wasHandled = true;
+ break;
+ */
+ }
+
+ if (!wasHandled)
+ wasHandled = CTextEdit::HandleSpecialChar(inChar, inFlags);
+
+ return wasHandled;
+}
+
+float CFloatEdit::GetData()
+{
+ return m_Value;
+}
+
+float CFloatEdit::GetDisplayData()
+{
+ float theFloat = static_cast<float>(::atof(GetDisplayString().GetCharStar()));
+ return theFloat;
+}
+
+void CFloatEdit::OnLoseFocus()
+{
+ ExitEditMode();
+ CTextEdit::OnLoseFocus();
+}
+
+void CFloatEdit::OnGainFocus()
+{
+ if (!m_IsMouseDown && IsEnabled())
+ SetReadOnly(false);
+ CTextEdit::OnGainFocus();
+}
+
+bool CFloatEdit::CanGainFocus()
+{
+ return true;
+}
+
+void CFloatEdit::ExitEditMode()
+{
+ m_EditMode = false;
+ SetReadOnly(true);
+}
+
+void CFloatEdit::EnterEditMode()
+{
+ m_EditMode = true;
+ SetReadOnly(false);
+}
+
+//==============================================================================
+/**
+ * Handles the dynamic dragging of float values.
+ */
+void CFloatEdit::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_IsMouseDown && !m_EditMode) {
+ // If we haven't started handling selecting or dragging figure out which one to do
+ if (!m_Trapping) {
+ // If moved outside of the safety zone start dragging the value.
+ if (::labs(inPoint.y - m_MouseStartPos.y) > 2) {
+ m_Trapping = true;
+// In Mac there's no infinite mouse, so don't hide it.
+ setCursorIfNotSet(CMouseCursor::CURSOR_BLANK);
+ }
+ }
+ // If in trapping mode then do the value drag processing.
+ if (m_Trapping) {
+ float theDiff = static_cast<float>(m_MouseStartPos.y - inPoint.y);
+
+ if (inFlags & CHotKeys::MODIFIER_CONTROL)
+ theDiff *= 0.1f;
+ else if (inFlags & CHotKeys::MODIFIER_SHIFT)
+ theDiff *= 10.0f;
+
+ SetFloatValue(theDiff + m_StartDragVal);
+
+ CPt theMouseLoc = CControl::ClientToScreen(m_MouseStartPos);
+ getCursor().setPos(theMouseLoc.x, theMouseLoc.y);
+
+ m_StartDragVal = GetData();
+ }
+ } else
+ CTextEdit::OnMouseMove(inPoint, inFlags);
+}
+
+void CFloatEdit::SetFloatValue(float inValue)
+{
+ // If the min and max are both zero, special case, don't bother capping the value to the
+ // min/max.
+ if (m_Min != 0 || m_Max != 0) {
+ if (inValue > m_Max)
+ inValue = m_Max;
+ else if (inValue < m_Min)
+ inValue = m_Min;
+ }
+
+ SetData(inValue);
+ ReloadData();
+ Q3DStudio::CString theNewValue = GetString();
+ SetData(theNewValue);
+}
+
+//==============================================================================
+/**
+ * Handler to start mouse dragging actions on this.
+ */
+bool CFloatEdit::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (IsEnabled()) {
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ if (m_EditMode)
+ CTextEdit::OnMouseDown(inPoint, inFlags);
+
+ m_IsMouseDown = true;
+ m_Trapping = false;
+ m_StartDragVal = GetData();
+
+ m_MouseStartPos = inPoint;
+ }
+ }
+
+ return true;
+}
+
+//==============================================================================
+/**
+ * Handler to finish the mouse dragging actions.
+ */
+void CFloatEdit::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_IsMouseDown && !m_Trapping && m_IsReadOnly) {
+ SetReadOnly(false);
+ SelectAllText();
+ EnterEditMode();
+ }
+
+ CTextEdit::OnMouseUp(inPoint, inFlags);
+
+ resetCursor();
+ m_IsMouseDown = false;
+
+ if (m_Trapping)
+ FireFocusEvent(true);
+}
+
+//==============================================================================
+/**
+ * Set the minimum value allowed in this edit field.
+ */
+void CFloatEdit::SetMin(float inMin)
+{
+ m_Min = inMin;
+}
+
+//==============================================================================
+/**
+ * Set the maximum value allowed in this field.
+ */
+void CFloatEdit::SetMax(float inMax)
+{
+ m_Max = inMax;
+}
+
+//==============================================================================
+/**
+ * Handle mouse wheel messages to allow scrolling of the values.
+ */
+bool CFloatEdit::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ bool theRet = false;
+ if (GetParent() && GetParent()->HasFocus(this)) {
+ float theDiff = 1.0f;
+ if (inFlags & CHotKeys::MODIFIER_CONTROL)
+ theDiff = 0.1f;
+ else if (inFlags & CHotKeys::MODIFIER_SHIFT)
+ theDiff = 10.0f;
+
+ if (inAmount < 0)
+ theDiff *= -1.0f;
+
+ SetFloatValue(GetData() + theDiff);
+
+ theRet = true;
+ }
+ return theRet;
+}
+
+//==============================================================================
+/**
+ * Set the number of decimal places to be displayed in this.
+ */
+void CFloatEdit::SetNumDecimalPlaces(short inNumDecimalPlaces)
+{
+ m_NumDecimalPlaces = inNumDecimalPlaces;
+ FormatString();
+}
+
+//==============================================================================
+/**
+ * Set the number of fill places before the start of the number to fill if the number
+ * is too short. This defaults to -1 which means none.
+ */
+void CFloatEdit::SetFixedPlaces(short inFixedPlaces)
+{
+ m_FixedPlaces = inFixedPlaces;
+}
+
+//==============================================================================
+/**
+ * Ensure that the text from clipboard can be pasted into this control
+ */
+bool CFloatEdit::CanPaste()
+{
+ bool theValid = false;
+
+ const auto text = Q3DStudio::CString::fromQString(CStudioClipboard::GetTextFromClipboard());
+ if (m_Caret.show) {
+ Q3DStudio::CString theNewString(m_DisplayString);
+ long theCaret = m_Caret.position;
+
+ if (HasSelectedText()) {
+ theNewString.Delete(GetSelectionLeft(), GetSelectionRight() - GetSelectionLeft());
+ theCaret = Q3DStudio::MIN(GetSelectionLeft(), GetSelectionRight());
+ }
+ theNewString.Insert(theCaret, text);
+ theValid = Validate(theNewString);
+ } else {
+ theValid = Validate(text);
+ }
+
+ return theValid;
+}
+
+//==============================================================================
+/**
+ * Returns true if the given string can be pasted into the control
+ */
+bool CFloatEdit::Validate(const Q3DStudio::CString &inString)
+{
+ Q3DStudio::CString theCheckString("");
+ Q3DStudio::CString theTemp;
+ bool theRetVal = true;
+ long theIter;
+
+ for (theIter = 0; theIter < inString.Length(); ++theIter) {
+ theTemp = inString.Extract(theIter, 1);
+
+ if (!CanAcceptChar(theCheckString, theTemp[0], theIter)) {
+ theRetVal = false;
+ break;
+ } else
+ theCheckString += inString.Extract(theIter, 1);
+ }
+
+ return theRetVal;
+}
+
+//==============================================================================
+/**
+ * Refreshes the display string from the internal data
+ */
+void CFloatEdit::RefreshDisplayFromData()
+{
+ SetDisplayString(GetString());
+ Invalidate();
+}
diff --git a/src/Authoring/Studio/Controls/FloatEdit.h b/src/Authoring/Studio/Controls/FloatEdit.h
new file mode 100644
index 00000000..4e29283b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/FloatEdit.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_FLOAT_EDIT_H
+#define INCLUDED_FLOAT_EDIT_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TextEdit.h"
+
+#include <QCursor>
+
+GENERIC_FUNCTOR_1(CRevertListener, OnDiscardChanges, CControl *);
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CMouseCursor;
+
+class CFloatEdit : public CTextEdit
+{
+protected:
+ float m_Value; ///<
+ short m_NumDecimalPlaces; ///<
+ short m_FixedPlaces; ///<
+ bool m_IsMouseDown; ///<
+ bool m_Trapping; ///<
+ float m_StartDragVal; ///<
+ float m_Min; ///<
+ float m_Max; ///<
+ bool m_EditMode; ///<
+ Q3DStudio::CString m_ValueString; ///<
+ CPt m_MouseStartPos; ///< Used to reset the mouse pos when dragging values.
+ CRevertListener *m_RevertListener; ///< Used when user presses escape.
+
+public:
+ CFloatEdit();
+ virtual ~CFloatEdit();
+
+ Q3DStudio::CString GetString() override;
+ virtual void SetData(float inValue, bool inFireEvent = true);
+ void SetData(const Q3DStudio::CString &inData, bool inFireEvent = true) override;
+ bool CanAcceptChar(const Q3DStudio::CString &inCheckString, unsigned int inChar,
+ unsigned int inPosition) override;
+ bool CanPaste() override;
+
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags) override;
+
+ virtual void SetMin(float inMin);
+ virtual void SetMax(float inMax);
+
+ void OnLoseFocus() override;
+ void OnGainFocus() override;
+ bool CanGainFocus() override;
+
+ float GetData();
+ float GetDisplayData();
+
+ void SetNumDecimalPlaces(short inNumDecimalPlaces);
+ void SetFixedPlaces(short inFixedPlaces);
+ void RefreshDisplayFromData() override;
+
+ virtual void SetRevertListener(CRevertListener *inListener) { m_RevertListener = inListener; }
+
+protected:
+ bool HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags) override;
+
+ void SetFloatValue(float inValue);
+ bool Validate(const Q3DStudio::CString &inString);
+
+ void EnterEditMode();
+ void ExitEditMode();
+
+ void FormatString();
+
+ void AddCharNegative();
+ void AddCharPeriod();
+};
+
+#endif // INCLUDED_FLOAT_EDIT_H
diff --git a/src/Authoring/Studio/Controls/FlowLayout.cpp b/src/Authoring/Studio/Controls/FlowLayout.cpp
new file mode 100644
index 00000000..9864321d
--- /dev/null
+++ b/src/Authoring/Studio/Controls/FlowLayout.cpp
@@ -0,0 +1,1009 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FlowLayout.h"
+#include "Renderer.h"
+#include "MasterP.h"
+#include "ControlData.h"
+#include "CoreUtils.h"
+#include "UICMath.h"
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+using namespace Q3DStudio::Control;
+
+//=============================================================================
+/**
+ * Checks to see if this has hit it's max X
+ */
+bool CanBeExpandedX(CControl *inControl)
+{
+ bool theRetVal = false;
+ CPt theSize = inControl->GetSize();
+ CPt theMaxSize = inControl->GetMaximumSize();
+ if (theSize.x < theMaxSize.x) {
+ theRetVal = true;
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Checks to see if this has hit it's max Y
+ */
+bool CanBeExpandedY(CControl *inControl)
+{
+ bool theRetVal = false;
+ CPt theSize = inControl->GetSize();
+ CPt theMaxSize = inControl->GetMaximumSize();
+ if (theSize.y < theMaxSize.y) {
+ theRetVal = true;
+ }
+ return theRetVal;
+}
+
+IMPLEMENT_OBJECT_COUNTER(CFlowLayout)
+
+//=============================================================================
+/**
+ * Constructs a new FlowLayout flowing vertically (down).
+ */
+CFlowLayout::CFlowLayout(CControl *inControl, bool inUseControl)
+ : m_FlowDirection(FLOW_VERTICAL)
+ , m_VerticalAlignment(ALIGN_TOP)
+ , m_HorizontalAlignment(ALIGN_LEFT)
+ , m_ResizingChildren(false)
+ , m_LeftMargin(0)
+ , m_RightMargin(0)
+ , m_TopMargin(0)
+ , m_BottomMargin(0)
+ , m_ChildGap(0)
+ , m_DebugFlag(false)
+ , m_AutoMin(true)
+ , m_SuspendRecalcLayout(false)
+{
+ ADDTO_OBJECT_COUNTER(CFlowLayout)
+
+ m_BlankDisable = false;
+ m_HasUnusedSpace = false;
+
+ if (inControl != nullptr) {
+ m_BlankControl = inControl;
+ } else if (inUseControl) {
+ m_BlankControl = new CBlankControl();
+ } else {
+ m_BlankControl = nullptr;
+ }
+
+ SetMaximumSize(CPt(LONG_MAX, LONG_MAX));
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CFlowLayout::~CFlowLayout()
+{
+ delete m_BlankControl;
+
+ REMOVEFROM_OBJECT_COUNTER(CFlowLayout)
+}
+
+//=============================================================================
+/**
+ * The left margin is the gap on the left side of the flow layout where no
+ * controls are drawn. The default left margin of a flow layout is zero. If
+ * you have a horizontal flow layout with left alignment, you can use the margin
+ * to push all the child controls over by a fixed amount.
+ * @param inMargin Width in pixels of the left margin
+ * @return the previous width of the left margin
+ */
+long CFlowLayout::SetLeftMargin(long inMargin)
+{
+ long theOldMargin = m_LeftMargin;
+ m_LeftMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The left margin is the gap on the left side of the flow layout where no
+ * controls are drawn. The default left margin of a flow layout is zero. Only
+ * valid with a horizontal flow layout with left alignment.
+ * @return the gap on the left side of this flow, in pixels
+ */
+long CFlowLayout::GetLeftMargin() const
+{
+ return m_LeftMargin;
+}
+
+long CFlowLayout::GetRightMargin() const
+{
+ return m_RightMargin;
+}
+
+long CFlowLayout::GetTopMargin() const
+{
+ return m_TopMargin;
+}
+
+long CFlowLayout::GetBottomMargin() const
+{
+ return m_BottomMargin;
+}
+
+//=============================================================================
+/**
+ * Disables the drawing of blank rectangles in blank areas on the screen
+ * @param NONE
+ * @return NONE
+ */
+void CFlowLayout::DisableBlank()
+{
+ m_BlankDisable = true;
+}
+
+//=============================================================================
+/**
+ * The right margin is the gap on the rgiht side of the flow layout where no
+ * controls are drawn. The default right margin of a flow layout is zero. If
+ * you have a horizontal flow layout with right alignment, you can use the margin
+ * to push all the child controls over by a fixed amount.
+ * @param inMargin Width in pixels of the right margin
+ * @return the previous width of the right margin
+ */
+long CFlowLayout::SetRightMargin(long inMargin)
+{
+ long theOldMargin = m_RightMargin;
+ m_RightMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The top margin is the gap on the top of the flow layout where no controls
+ * are drawn. The default top margin of a flow layout is zero. If you have
+ * a vertical flow layout with top alignment, you can use the margin to push
+ * all the child controls down by a fixed amount.
+ * @param inMargin Height in pixels of the top margin
+ * @return the previous height of the top margin
+ */
+long CFlowLayout::SetTopMargin(long inMargin)
+{
+ long theOldMargin = m_TopMargin;
+ m_TopMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * The bottom margin is the gap on the bottom of the flow layout where no
+ * controls are drawn. The default bottom margin of a flow layout is zero. If
+ * you have a vertical flow layout with bottom alignment, you can use the margin
+ * to push all the child controls up by a fixed amount.
+ * @param inMargin Height in pixels of the bottom margin
+ * @return the previous height of the bottom margin
+ */
+long CFlowLayout::SetBottomMargin(long inMargin)
+{
+ long theOldMargin = m_BottomMargin;
+ m_BottomMargin = inMargin;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldMargin;
+}
+
+//=============================================================================
+/**
+ * This functions sets a fixed gap between children of the flow layout.
+ * @param inGap pixel gap between each child of the flow layout
+ * @return the previous pixel gap between children
+ */
+long CFlowLayout::SetGapBetweenChildren(long inGap)
+{
+ long theOldGap = m_ChildGap;
+ m_ChildGap = inGap;
+ ResetMinMaxPref();
+ RecalcLayout();
+ return theOldGap;
+}
+
+//=============================================================================
+/**
+ * Get the preferred size of this control.
+ * The preferred size is the sum of all the preferred sizes of all the children.
+ * @return the preferred size of this control.
+ */
+// CPt CFlowLayout::GetPreferredSize( )
+//{
+// return m_PreferredSize;
+//}
+
+//=============================================================================
+/**
+ * Get the minimum size of this control.
+ * The minimum size is the sum of all the minimum sizes of all the children in
+ * the flow direction, and the largest minimum size in the non-flow direction.
+ * @return the minimum size of this control.
+ */
+// CPt CFlowLayout::GetMinimumSize( )
+//{
+// return m_MinimumSize;
+//}
+
+//=============================================================================
+/**
+ * Get the maximum size of this control.
+ * The maximum size is the sum of all the maximum sizes of all the children in
+ * the flow direction, and the smallest maximum size in the non-flow direction.
+ * @return the maximum size of this control.
+ */
+// CPt CFlowLayout::GetMaximumSize( )
+//{
+// return m_MaximumSize;
+//}
+
+//=============================================================================
+/**
+ * Set the direction the components are flowing.
+ * Vertical means the components will be positioned one on top of the other,
+ * starting at the top and flowing down.
+ * Horizontal means the components will be positions side by side, starting on
+ * the left and flowing right.
+ * @param inFlowDirection the new flow direction.
+ */
+void CFlowLayout::SetFlowDirection(EFlowDirection inFlowDirection)
+{
+ if (inFlowDirection != m_FlowDirection) {
+ m_FlowDirection = inFlowDirection;
+
+ RecalcLayout();
+ }
+}
+
+//=============================================================================
+/**
+ * Add a child control to this control.
+ * This adds the child control to this and includes it in the layout.
+ * @param inControl the control to be added.
+ * @param inInsertBefore the location to insert the control, nullptr = end.
+ */
+void CFlowLayout::AddChild(CControl *inControl, CControl *inInsertBefore /*= nullptr*/)
+{
+ CControl::AddChild(inControl, inInsertBefore);
+
+ if (GetParent() != nullptr) {
+ ResetMinMaxPref();
+ GetParent()->OnChildSizeChanged(this);
+ }
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Remove a child from this control.
+ * @param inControl the control to be removed.
+ */
+void CFlowLayout::RemoveChild(CControl *inControl)
+{
+ CControl::RemoveChild(inControl);
+
+ if (GetParent() != nullptr) {
+ ResetMinMaxPref();
+ GetParent()->OnChildSizeChanged(this);
+ }
+
+ RecalcLayout();
+}
+
+/**
+ * Sets the flag on whether to suspend the recalculation of the layout.
+ * Recalc is expensive, especially when there are many children.
+ * if you need to add lots of children items to this control, do this:
+ * SuspendRecalcLayout( true ); AddLotsOFItems( ); SuspendRecalcLayout( false );
+ * DoSomethingThatWillTriggerRecalcLayout( );
+ * @param inSuspendFlag true would suspend recalc
+ * @see RecalcLayout
+ * @see CListBoxControl
+*/
+void CFlowLayout::SuspendRecalcLayout(bool inSuspendFlag)
+{
+ m_SuspendRecalcLayout = inSuspendFlag;
+}
+
+//=============================================================================
+/**
+ * Recalculate the positions of all the child controls of this control.
+ * This does the work of all the layout of the children and figures out where
+ * everything belongs.
+ */
+void CFlowLayout::RecalcLayout()
+{
+ UICPROFILE(RecalcLayout);
+
+ // if you need to add lots of children items to this control, do this:
+ // SuspendRecalcLayout( true ); AddLotsOFItems( ); SuspendRecalcLayout( false );
+ // DoSomethingThatWillTriggerRecalcLayout( );
+ if (m_SuspendRecalcLayout)
+ return;
+
+ m_ResizingChildren = true;
+
+ CPt thePosition(0, 0);
+ CPt theCurSize = GetSize();
+ long theNumExpandableY = 0;
+ long theNumExpandableX = 0;
+ m_HasUnusedSpace = false;
+
+ if ((m_FlowDirection == FLOW_HORIZONTAL && m_HorizontalAlignment != ALIGN_RIGHT)
+ || (m_FlowDirection == FLOW_VERTICAL && m_VerticalAlignment != ALIGN_BOTTOM)) {
+ thePosition.x = m_LeftMargin;
+ thePosition.y = m_TopMargin;
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = *thePos;
+ RecalcChild(theChild->GetControl(), thePosition, theNumExpandableX, theNumExpandableY);
+
+ if (m_FlowDirection == FLOW_HORIZONTAL)
+ thePosition += CPt(m_ChildGap, 0);
+ else
+ thePosition += CPt(0, m_ChildGap);
+ }
+ } else {
+ // thePosition.x = m_RightMargin;
+ // thePosition.y = m_BottomMargin;
+ ControlGraph::SReverseIterator thePos = GetReverseChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = *thePos;
+ RecalcChild(theChild->GetControl(), thePosition, theNumExpandableX, theNumExpandableY);
+
+ if (m_FlowDirection == FLOW_HORIZONTAL)
+ thePosition -= CPt(m_ChildGap, 0);
+ else
+ thePosition -= CPt(0, m_ChildGap);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if (thePosition.y < theCurSize.y && m_FlowDirection == FLOW_VERTICAL) {
+ if (theNumExpandableY > 0)
+ ResizeExpandableControlsY((theCurSize.y - thePosition.y), theNumExpandableY);
+ else
+ m_HasUnusedSpace = true;
+
+ // SDJ: See below
+ // ResetChildPositions( );
+ } else if (thePosition.x < theCurSize.x && m_FlowDirection == FLOW_HORIZONTAL) {
+ if (theNumExpandableX > 0)
+ ResizeExpandableControlsX((theCurSize.x - thePosition.x), theNumExpandableX);
+ else
+ m_HasUnusedSpace = true;
+
+ // SDJ: See below
+ // ResetChildPositions( );
+ }
+
+ // SDJ: There is a case where the top margin is not properly calculated when
+ // neither of the above conditions is met. Calling ResetChildPositions fixes
+ // this, but might be ineffiecient. If you attempt to fix this, replace the
+ // commented out calls to ResetChildPositions above.
+ ResetChildPositions();
+
+ m_ResizingChildren = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * XXX
+ */
+void CFlowLayout::RecalcChild(CControl *inChild, CPt &ioPosition, long &ioNumExpandableX,
+ long &ioNumExpandableY)
+{
+ CPt theTotalPrefSizes = GetTotalPreferredSizes();
+ CPt theCurSize = GetSize();
+ CPt theChildSize = theCurSize;
+
+ theCurSize.x -= m_RightMargin;
+ theCurSize.y -= m_BottomMargin;
+
+ if (inChild->IsVisible()) {
+ inChild->ResetMinMaxPref();
+ CPt thePrefSize = inChild->GetPreferredSize();
+ CPt theMinSize = inChild->GetMinimumSize();
+ CPt theMaxSize = inChild->GetMaximumSize();
+
+ if (theMaxSize.x > theCurSize.x - ioPosition.x)
+ theMaxSize.x = theCurSize.x - ioPosition.x;
+ if (theMaxSize.x < 0)
+ theMaxSize.x = 0;
+ if (theMaxSize.y > theCurSize.y - ioPosition.y)
+ theMaxSize.y = theCurSize.y - ioPosition.y;
+ if (theMaxSize.y < 0)
+ theMaxSize.y = 0;
+
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ CPt theChildPosition;
+ theChildPosition.y = ioPosition.y;
+ theChildPosition.x = 0;
+ theChildSize.x = theCurSize.x;
+ ///*
+ // SDJ; Why is this commented out?
+ switch (m_HorizontalAlignment) {
+ case ALIGN_LEFT:
+ theChildPosition.x = 0;
+ break;
+
+ case ALIGN_RIGHT:
+ theChildPosition.x =
+ (GetSize().x - (m_LeftMargin + m_RightMargin)) - inChild->GetSize().x;
+ break;
+
+ case ALIGN_MIDDLE:
+ theChildPosition.x = (GetSize().x / 2) - (inChild->GetSize().x / 2);
+ break;
+
+ case ALIGN_HORIZ_NEITHER:
+ // NO BREAK
+ default:
+ theChildPosition.x = inChild->GetPosition().x;
+ break;
+ }
+ //*/
+
+ inChild->SetPosition(theChildPosition);
+
+ if (theTotalPrefSizes.y) {
+ float theModifier = ((float)thePrefSize.y) / ((float)theTotalPrefSizes.y);
+ // Check to see if they have hit their min or max
+ if (theMinSize.y > ::dtol(theCurSize.y * theModifier)) {
+ theChildSize.y = theMinSize.y;
+ ++ioNumExpandableY;
+
+ } else if (theMaxSize.y < ::dtol(theCurSize.y * theModifier)) {
+ theChildSize.y = theMaxSize.y;
+ } else {
+ // This item can be expanded
+ ++ioNumExpandableY;
+ theChildSize.y = inChild->GetSize().y;
+ }
+ }
+
+ // If the child cannot be expanded, maintain its current width
+ // if ( !CanBeExpandedX( inChild ) )
+ // theChildSize.x = inChild->GetSize( ).x;
+
+ theChildSize.x = Q3DStudio::MAX(theChildSize.x, theMinSize.x);
+ theChildSize.y = Q3DStudio::MAX(theChildSize.y, theMinSize.y);
+
+ theChildSize.x = Q3DStudio::MIN(theChildSize.x, theMaxSize.x);
+ theChildSize.y = Q3DStudio::MIN(theChildSize.y, theMaxSize.y);
+
+ inChild->SetSize(theChildSize);
+ ioPosition.y += theChildSize.y;
+ } else {
+ CPt theChildPosition;
+ theChildPosition.x = ioPosition.x;
+
+ switch (m_VerticalAlignment) {
+ case ALIGN_TOP:
+ theChildPosition.y = m_TopMargin;
+ break;
+
+ case ALIGN_BOTTOM:
+ theChildPosition.y = GetSize().y - inChild->GetSize().y;
+ break;
+
+ case ALIGN_CENTER:
+ theChildPosition.y = (GetSize().y / 2) - (inChild->GetSize().y / 2);
+ break;
+
+ case ALIGN_VERT_NEITHER:
+ // NO BREAK
+ default:
+ theChildPosition.y = inChild->GetPosition().y;
+ break;
+ }
+
+ inChild->SetPosition(theChildPosition);
+
+ if (theTotalPrefSizes.x) {
+ float theModifier = ((float)thePrefSize.x) / ((float)theTotalPrefSizes.x);
+ // Check to see if they have hit their min or max
+ if (theMinSize.x > ::dtol(theCurSize.x * theModifier)) {
+ theChildSize.x = theMinSize.x;
+ ++ioNumExpandableX;
+
+ } else if (theMaxSize.x < ::dtol(theCurSize.x * theModifier)) {
+ theChildSize.x = theMaxSize.x;
+ } else {
+ // This item can be expanded
+ theChildSize.x = ::dtol((theCurSize.x * theModifier));
+ ++ioNumExpandableX;
+ }
+ }
+
+ // If the child cannot by expanded, maintain the same height
+ if (!CanBeExpandedY(inChild))
+ theChildSize.y = inChild->GetSize().y;
+
+ theChildSize.x = Q3DStudio::MAX(theChildSize.x, theMinSize.x);
+ theChildSize.y = Q3DStudio::MAX(theChildSize.y, theMinSize.y);
+
+ theChildSize.x = Q3DStudio::MIN(theChildSize.x, theMaxSize.x);
+ theChildSize.y = Q3DStudio::MIN(theChildSize.y, theMaxSize.y);
+
+ inChild->SetSize(theChildSize);
+ ioPosition.x += theChildSize.x;
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resets all child positions accd to their size
+ */
+void CFlowLayout::ResetChildPositions()
+{
+ if ((m_FlowDirection == FLOW_HORIZONTAL && m_HorizontalAlignment != ALIGN_RIGHT)
+ || (m_FlowDirection == FLOW_VERTICAL && m_VerticalAlignment != ALIGN_BOTTOM)) {
+ CPt thePosition(m_LeftMargin, m_TopMargin);
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePosition.x = theChild->GetPosition().x + m_LeftMargin;
+ theChild->SetPosition(thePosition);
+ thePosition.y = thePosition.y + theChildSize.y + m_ChildGap;
+ } else {
+ thePosition.y = theChild->GetPosition().y + m_TopMargin;
+ theChild->SetPosition(thePosition);
+ thePosition.x = thePosition.x + theChildSize.x + m_ChildGap;
+ }
+ }
+ }
+
+ if (m_HasUnusedSpace && m_BlankControl) {
+ CPt theBlankPosition;
+ if (m_FlowDirection == FLOW_HORIZONTAL) {
+ theBlankPosition.x = thePosition.x;
+ theBlankPosition.y = 0;
+ } else {
+ theBlankPosition.x = 0;
+ theBlankPosition.y = thePosition.y;
+ }
+ m_BlankControl->SetPosition(theBlankPosition);
+ m_BlankControl->SetSize(GetSize() - theBlankPosition);
+ }
+ } else {
+ CPt thePosition(GetSize() - CPt(m_RightMargin, m_BottomMargin));
+ ControlGraph::SReverseIterator thePos = GetReverseChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePosition.x = theChild->GetPosition().x - m_RightMargin;
+ thePosition.y = thePosition.y - theChildSize.y - m_ChildGap;
+ theChild->SetPosition(thePosition);
+ } else {
+ thePosition.y = theChild->GetPosition().y - m_BottomMargin;
+ thePosition.x = thePosition.x - theChildSize.x - m_ChildGap;
+ theChild->SetPosition(thePosition);
+ }
+ }
+ }
+
+ if (m_HasUnusedSpace && m_BlankControl) {
+ CPt theBlankSize;
+ if (m_FlowDirection == FLOW_HORIZONTAL) {
+ theBlankSize.x = thePosition.x;
+ theBlankSize.y = GetSize().y;
+ } else {
+ theBlankSize.x = GetSize().x;
+ theBlankSize.y = thePosition.y;
+ }
+ m_BlankControl->SetPosition(CPt(0, 0));
+ m_BlankControl->SetSize(theBlankSize);
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resizes any expandable Controls in the Y Dir
+ *
+ * @param inExtraSpace the Amount left
+ * @param inNumExpandable used to calc percentages
+ */
+void CFlowLayout::ResizeExpandableControlsY(long inExtraSpace, long inNumExpandable)
+{
+ m_HasUnusedSpace = false;
+ long theSpaceDistributed = 0;
+ if (inExtraSpace / inNumExpandable < 1) {
+ this->DistributeRemaining(inExtraSpace);
+ } else {
+ ControlGraph::SIterator thePos = GetChildren();
+ long theNumExpandable = 0;
+
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedY(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theMaxSize = theChild->GetMaximumSize();
+ CPt theChildSize = theChild->GetSize();
+ float theModifier = (float)1 / inNumExpandable;
+ if (theMaxSize.y < ::dtol((inExtraSpace * theModifier) + theChildSize.y)) {
+ theSpaceDistributed += theMaxSize.y - theChildSize.y;
+ theChildSize.y = theMaxSize.y;
+ } else {
+ theSpaceDistributed += ::dtol((inExtraSpace * theModifier));
+ theChildSize.y += ::dtol((inExtraSpace * theModifier));
+ ++theNumExpandable;
+ }
+
+ theChild->SetSize(theChildSize);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if ((inExtraSpace - theSpaceDistributed) > 0) {
+ if (theNumExpandable > 0) {
+ this->ResizeExpandableControlsY((inExtraSpace - theSpaceDistributed),
+ theNumExpandable);
+ } else {
+ m_HasUnusedSpace = true;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Resizes any expandable Controls in the X Dir
+ *
+ * @param inExtraSpace the Amount left
+ * @param inNumExpandable used to calc percentages
+ */
+void CFlowLayout::ResizeExpandableControlsX(long inExtraSpace, long inNumExpandable)
+{
+ m_HasUnusedSpace = false;
+ long theSpaceDistributed = 0;
+ if (inExtraSpace / inNumExpandable < 1) {
+ this->DistributeRemaining(inExtraSpace);
+ } else {
+ long theNumExpandable = 0;
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedX(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theMaxSize = theChild->GetMaximumSize();
+ CPt theChildSize = theChild->GetSize();
+ float theModifier = (float)1 / inNumExpandable;
+ if (theMaxSize.x < ::dtol((inExtraSpace * theModifier) + theChildSize.x)) {
+ theSpaceDistributed += theMaxSize.x - theChildSize.x;
+ theChildSize.x = theMaxSize.x;
+ } else {
+ theSpaceDistributed += ::dtol((inExtraSpace * theModifier));
+ theChildSize.x += ::dtol((inExtraSpace * theModifier));
+ ++theNumExpandable;
+ }
+
+ theChild->SetSize(theChildSize);
+ }
+ }
+
+ // If there is extra space and there are items able to expand still
+ if ((inExtraSpace - theSpaceDistributed) > 0) {
+ if (theNumExpandable > 0) {
+ this->ResizeExpandableControlsX((inExtraSpace - theSpaceDistributed),
+ theNumExpandable);
+ } else {
+ m_HasUnusedSpace = true;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Distributes remaining size to items that can be expanded.
+ * this should only be called when theNumExpandable items > the extra space
+ */
+void CFlowLayout::DistributeRemaining(long inExtraSpace)
+{
+ long theNumToDistribute = inExtraSpace;
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext() && theNumToDistribute > 0; ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedY(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ theChildSize.y++;
+ theChild->SetSize(theChildSize.x, theChildSize.y);
+ theNumToDistribute--;
+ }
+ }
+ } else {
+ ControlGraph::SIterator thePos = GetChildren();
+ // Loop through teh children for items that can expand
+ for (; thePos.HasNext() && theNumToDistribute > 0; ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+
+ // If this item can be expanded then expand accd to new percent
+ if (CanBeExpandedX(theChild->GetControl()) && theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ theChildSize.x++;
+ theChild->SetSize(theChildSize.x, theChildSize.y);
+ theNumToDistribute--;
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Gets the total prefered sizes of the children
+ */
+CPt CFlowLayout::GetTotalPreferredSizes()
+{
+ CPt theTotalPrefSizes(0, 0);
+
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ theTotalPrefSizes += theChild->GetPreferredSize();
+ }
+ }
+
+ return theTotalPrefSizes;
+}
+
+//==============================================================================
+/**
+ * sets the size and does a recalc
+ */
+void CFlowLayout::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ CControl::SetSize(inSize);
+
+ RecalcLayout();
+ }
+}
+
+void CFlowLayout::SetLayout(CPt inSize, CPt inPosition)
+{
+ CControl::SetLayout(inSize, inPosition);
+
+ RecalcLayout();
+}
+//==============================================================================
+/**
+ * override to force a recalc when the parent is changed
+ */
+void CFlowLayout::OnParentChanged(CControl *inParent)
+{
+ CControl::OnParentChanged(inParent);
+ RecalcLayout();
+}
+
+//==============================================================================
+/**
+ * Sets this object's alignment
+ */
+void CFlowLayout::SetAlignment(EVerticalAlignment inVertical, EHorizontalAlignment inHorizontal)
+{
+ m_VerticalAlignment = inVertical;
+ m_HorizontalAlignment = inHorizontal;
+
+ RecalcLayout();
+}
+
+//==============================================================================
+/**
+ * Overloaded draw function. Draws the extra control if it is needed
+ */
+void CFlowLayout::Draw(CRenderer *inRenderer)
+{
+ // If there is unused space in this control, then use the blank control
+ if (m_HasUnusedSpace && m_BlankControl) {
+ if (m_BlankControl->IsVisible()) {
+ inRenderer->PushTranslation(m_BlankControl->GetPosition());
+ if (!m_BlankDisable)
+ m_BlankControl->Draw(inRenderer);
+ inRenderer->PopTranslation();
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * called when a child's size changes
+ */
+void CFlowLayout::OnChildSizeChanged(CControl *inChild)
+{
+ Q_UNUSED(inChild);
+
+ if (!m_ResizingChildren) {
+ ResetMinMaxPref();
+
+ if (GetParent() != nullptr)
+ GetParent()->OnChildSizeChanged(this);
+
+ RecalcLayout();
+ }
+}
+
+//==============================================================================
+/**
+ * Called to calc the min/max/and pre size of this layout. NOTE: this function
+ * no longer adjusts the max size of the flow layout. The max size is not dictated
+ * by the size of the children, so it can be set independently with a call to
+ * SetMaximumSize().
+ */
+void CFlowLayout::ResetMinMaxPref()
+{
+ if (m_AutoMin) {
+ CPt thePreferredSize(0, 0);
+ CPt theMinimumSize(0, 0);
+
+ thePreferredSize = theMinimumSize;
+
+ // Keep track of the largest, minimum size of all the children for use later
+ CPt theBiggestChildMinSize(0, 0);
+
+ // Go through all the children and find their maximum sizes.
+ ControlGraph::SIterator thePos = GetChildren();
+
+ for (; thePos.HasNext(); ++thePos) {
+ std::shared_ptr<CControlData> theChild = (*thePos);
+ if (theChild->IsVisible()) {
+ theChild->ResetMinMaxPref();
+ CPt theChildPrefSize = theChild->GetPreferredSize();
+ CPt theChildMinSize = theChild->GetMinimumSize();
+
+ // use the sum of the children in the flow direction and the smallest size
+ // in the non-flow direction.
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ thePreferredSize.y += theChildPrefSize.y;
+
+ theMinimumSize.y += theChildMinSize.y;
+
+ if (theChildMinSize.x > theBiggestChildMinSize.x)
+ theBiggestChildMinSize = theChildMinSize;
+ } else {
+ thePreferredSize.x += theChildPrefSize.x;
+
+ theMinimumSize.x += theChildMinSize.x;
+
+ if (theChildMinSize.y > theBiggestChildMinSize.y)
+ theBiggestChildMinSize = theChildMinSize;
+ }
+ }
+ }
+
+ // If this is a vertical flow
+ if (m_FlowDirection == FLOW_VERTICAL) {
+ // The width is determined by minimum size of the largest child
+ theMinimumSize.x = theBiggestChildMinSize.x;
+
+ // If the flow is aligned to top, the top margin must be added to the minimum height
+ if (m_VerticalAlignment == ALIGN_TOP)
+ theMinimumSize.y += m_TopMargin;
+ // If the flow is aligned to bottom, the bottom margin must be added to the minimum
+ // height
+ else if (m_VerticalAlignment == ALIGN_BOTTOM)
+ theMinimumSize.y += m_BottomMargin;
+
+ theMinimumSize.y += (m_ChildGap * GetChildCount());
+ }
+ // If this is a horzontal flow
+ else {
+ // The height is determined by minimum size of the largest child
+ theMinimumSize.y = theBiggestChildMinSize.y;
+
+ // If the flow is left aligned, the left margin must be added to the minimum width
+ if (m_HorizontalAlignment == ALIGN_LEFT)
+ theMinimumSize.x += m_LeftMargin;
+ // If the flow is right aligned, the right margin must be added to the minimum width
+ else if (m_HorizontalAlignment == ALIGN_RIGHT)
+ theMinimumSize.x += m_RightMargin;
+
+ theMinimumSize.x += (m_ChildGap * GetChildCount());
+ }
+
+ thePreferredSize.x = max(thePreferredSize.x, theMinimumSize.x);
+ thePreferredSize.y = max(thePreferredSize.y, theMinimumSize.y);
+
+ SetMinimumSize(theMinimumSize);
+ SetPreferredSize(thePreferredSize);
+ }
+}
+
+//=============================================================================
+/**
+ * Override the default in case the blank control was hit
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * @return true if the mouse event is processed.
+ */
+bool CFlowLayout::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = CControl::OnMouseDown(inPoint, inFlags);
+ if (!theRetVal) {
+ if (m_BlankControl && m_BlankControl->HitTest(inPoint))
+ GrabFocus(nullptr);
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Override the default in case the blank control was hit
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * @return true if the mouse event is processed.
+ */
+bool CFlowLayout::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = CControl::OnMouseRDown(inPoint, inFlags);
+ if (!theRetVal) {
+ if (m_BlankControl && m_BlankControl->HitTest(inPoint))
+ GrabFocus(nullptr);
+ }
+ return theRetVal;
+}
diff --git a/src/Authoring/Studio/Controls/FlowLayout.h b/src/Authoring/Studio/Controls/FlowLayout.h
new file mode 100644
index 00000000..1f7523a1
--- /dev/null
+++ b/src/Authoring/Studio/Controls/FlowLayout.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_FLOW_LAYOUT_H
+#define INCLUDED_FLOW_LAYOUT_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "BlankControl.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+
+//==============================================================================
+/**
+* @class CFlowLayout
+* @brief CHRIS.EDWARDS needs to enter a brief description here.
+*
+* CHRIS.EDWARDS needs to enter a long description here.
+*/
+class CFlowLayout : public CControl
+{
+public:
+ enum EFlowDirection {
+ FLOW_VERTICAL,
+ FLOW_HORIZONTAL,
+ };
+
+ enum EVerticalAlignment {
+ ALIGN_TOP,
+ ALIGN_BOTTOM,
+ ALIGN_CENTER,
+ ALIGN_VERT_NEITHER,
+ };
+
+ enum EHorizontalAlignment {
+ ALIGN_LEFT,
+ ALIGN_RIGHT,
+ ALIGN_MIDDLE,
+ ALIGN_HORIZ_NEITHER,
+ };
+
+ CFlowLayout(CControl *inControl = nullptr, bool inUseControl = true);
+ virtual ~CFlowLayout();
+
+ DEFINE_OBJECT_COUNTER(CFlowLayout)
+
+ void SetSize(CPt inSize) override;
+ void SetLayout(CPt inSize, CPt inPosition) override;
+ void OnParentChanged(CControl *inParent) override;
+
+ void SetAlignment(EVerticalAlignment inVertical, EHorizontalAlignment inHorizontal);
+ long SetLeftMargin(long inMargin);
+ long GetLeftMargin() const;
+ long SetRightMargin(long inMargin);
+ long GetRightMargin() const;
+ long SetTopMargin(long inMargin);
+ long GetTopMargin() const;
+ long SetBottomMargin(long inMargin);
+ long GetBottomMargin() const;
+ long SetGapBetweenChildren(long inGap);
+ void SetDebug(bool inFlag) { m_DebugFlag = inFlag; }
+ void SetAutoMin(bool inFlag) { m_AutoMin = inFlag; }
+
+ virtual void SetFlowDirection(EFlowDirection inFlowDirection);
+ void AddChild(CControl *inControl, CControl *inInsertBefore = nullptr) override;
+ void RemoveChild(CControl *inControl) override;
+ void Draw(CRenderer *inRenderer) override;
+ virtual void DisableBlank();
+ void OnChildSizeChanged(CControl *inControl) override;
+
+ virtual void RecalcLayout();
+ void SuspendRecalcLayout(bool inSuspendFlag);
+ void ResetMinMaxPref() override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ CPt GetTotalPreferredSizes();
+ void ResizeExpandableControlsY(long inExtraSpace, long inNumExpandable);
+ void ResizeExpandableControlsX(long inExtraSpace, long inNumExpandable);
+ void ResetChildPositions();
+ void DistributeRemaining(long inExtraSpace);
+ void RecalcChild(CControl *inChild, CPt &ioPosition, long &ioNumExpandableX,
+ long &ioNumExpandableY);
+
+ bool m_HasUnusedSpace;
+ bool m_BlankDisable;
+ EFlowDirection m_FlowDirection;
+ EVerticalAlignment m_VerticalAlignment;
+ EHorizontalAlignment m_HorizontalAlignment;
+ bool m_ResizingChildren;
+ long m_LeftMargin; ///< gap at left of this control
+ long m_RightMargin; ///< gap at right of this control
+ long m_TopMargin; ///< gap at top of this control
+ long m_BottomMargin; ///< gap at bottom of this control
+ long m_ChildGap; ///< gap between each child control when laying out everything
+ bool m_DebugFlag;
+ bool m_AutoMin;
+
+ bool m_SuspendRecalcLayout; ///< flag to suspend recalculation of the layout; slow down is
+ ///noticeable if this control has many children
+
+private:
+ CControl *m_BlankControl; ///< used when all items are at their max and there si still space
+};
+
+#endif // INCLUDED_FLOW_LAYOUT_H
diff --git a/src/Authoring/Studio/Controls/GenericComboDropDown.h b/src/Authoring/Studio/Controls/GenericComboDropDown.h
new file mode 100644
index 00000000..cd95b3a2
--- /dev/null
+++ b/src/Authoring/Studio/Controls/GenericComboDropDown.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_GENERIC_COMBO_DROP_DOWN_H
+#define INCLUDED_GENERIC_COMBO_DROP_DOWN_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "GenericEdit.h"
+#include "ComboTextBox.h"
+
+#include <QMenu>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CButtonControl;
+class CButtonUpListener;
+
+//==============================================================================
+/**
+ * Combo-box GUI element. Allows user to choose from a list of strings, and the
+ * selected string is displayed in a box.
+ */
+class CGenericComboDropDown : public CGenericEdit, public IChangeDataListener
+{
+public:
+ CGenericComboDropDown(const bool inUseFloat = false);
+ virtual ~CGenericComboDropDown();
+
+ void OnDropDownSelect();
+ void OnButtonUp(CControl *inButton);
+ bool CanGainFocus() override;
+ void AddItem(const Q3DStudio::CString &inString, bool inEnable = true);
+ void RemoveAllItems();
+ void SelectItem(long inIndex, bool inFireChangeEvent = true);
+ long GetSelectedItem();
+ long GetItemCount();
+ Q3DStudio::CString GetItemText(long inIndex);
+ Q3DStudio::CString GetCurrentText();
+
+ virtual long DoDropDownMenu();
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags) override;
+ bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags) override;
+ void LayoutChildren() override { CGenericEdit::LayoutChildren(); }
+
+ // IChangeDataListener
+ void OnChangeData(const Q3DStudio::CString &inOldString,
+ const Q3DStudio::CString &inNewString) override;
+
+protected:
+ virtual void ChangeSelection(long inNewSelection);
+ virtual void OffsetSelection(long inAmount);
+ void SetDisplayText(const Q3DStudio::CString &inString, bool inFireChangeEvent = true);
+ void UpdateReadOnly(bool inReadOnlyFlag);
+
+ long m_SelectedItem;
+ Q3DStudio::CString m_CurrentText;
+ CComboTextBox *m_TextBox; ///< ComboTextBox (StringEdit derivative) that processes text data
+ CButtonControl *m_Button;
+ QMenu m_DropDownMenu; ///< Context menu used to display a list of options for the control
+ QActionGroup m_actionGroup;
+ bool m_ReverseOffset; ///< True if we want the mousewheel to scroll the other direction in the
+ ///list;useful for numerical lists like
+};
+
+#endif // INCLUDED_GENERIC_COMBO_DROP_DOWN_H
diff --git a/src/Authoring/Studio/Controls/GenericEdit.h b/src/Authoring/Studio/Controls/GenericEdit.h
new file mode 100644
index 00000000..5af96a6b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/GenericEdit.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_GENERIC_EDIT_H
+#define INCLUDED_GENERIC_EDIT_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "LazyFlow.h"
+#include "Multicaster.h"
+#include "CColor.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//==============================================================================
+// Functors
+//==============================================================================
+/// Function to be called when the edit control changes
+GENERIC_FUNCTOR(CGenericEditCommitListener, OnCommitData);
+GENERIC_FUNCTOR(CGenericEditChangeListener, OnChangeData);
+GENERIC_FUNCTOR(CGenericEditRevertListener, OnRevertData);
+
+//==============================================================================
+/**
+ * Base class for most edit controls. Provides functions for adding/removing/
+ * notifying listeners when the control's value changes. Implements some
+ * common functionality across all edit controls.
+ */
+class CGenericEdit : public Q3DStudio::Control::CRuntimeLazyFlow
+{
+public:
+ CGenericEdit();
+ virtual ~CGenericEdit();
+
+ void AddCommitListener(CGenericEditCommitListener *inListener);
+ void RemoveCommitListener(CGenericEditCommitListener *inListener);
+ void FireCommitDataEvent();
+
+ void AddChangeListener(CGenericEditChangeListener *inListener);
+ void RemoveChangeListener(CGenericEditChangeListener *inListener);
+ void FireChangeDataEvent();
+
+ void AddRevertListener(CGenericEditRevertListener *inListener);
+ void RemoveRevertListener(CGenericEditRevertListener *inListener);
+ void FireRevertDataEvent();
+
+ void SetFillBackground(bool inFill);
+ void SetBackgroundColor(const ::CColor &inColor);
+
+ // CControl
+ void Draw(CRenderer *inRenderer);
+
+protected:
+ CMulticaster<CGenericEditCommitListener *> m_CommitListeners;
+ CMulticaster<CGenericEditChangeListener *> m_ChangeListeners;
+ CMulticaster<CGenericEditRevertListener *> m_RevertListeners;
+ bool m_FillBackground;
+ ::CColor m_BackgroundColor;
+};
+
+#endif // INCLUDED_GENERIC_EDIT_H
diff --git a/src/Authoring/Studio/Controls/InsertionLine.cpp b/src/Authoring/Studio/Controls/InsertionLine.cpp
new file mode 100644
index 00000000..5edbcd93
--- /dev/null
+++ b/src/Authoring/Studio/Controls/InsertionLine.cpp
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "InsertionLine.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+#include "StudioUtils.h"
+#include "CColor.h"
+#include "CoreUtils.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CInsertionLine::CInsertionLine()
+ : m_LineWidth(0)
+ , m_LineHeight(2)
+ , m_LineColor(CColor(0, 0, 0))
+{
+ SetVisible(false);
+
+ m_InsertLeftImage = CResourceCache::GetInstance()->GetBitmap("Insert-Rearrange-Left.png");
+ m_InsertRightImage = CResourceCache::GetInstance()->GetBitmap("Insert-Rearrange-Right.png");
+
+ const auto theLeftSize = m_InsertLeftImage.size();
+ const auto theRightSize = m_InsertRightImage.size();
+
+ if (theLeftSize.height() >= theRightSize.width()) {
+ SetSize(CPt(theLeftSize.width() + theRightSize.width(), theLeftSize.height()));
+ SetMaximumSize(CPt(LONG_MAX, theLeftSize.height()));
+ } else {
+ SetSize(CPt(theLeftSize.width() + theRightSize.width(), theRightSize.height()));
+ SetMaximumSize(CPt(LONG_MAX, theLeftSize.height()));
+ }
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CInsertionLine::~CInsertionLine()
+{
+}
+
+//=============================================================================
+/**
+ * Sets the position of the line. Overridden since the line is technically
+ * drawn in the middle of the area specified as the size of this control, so
+ * we have to adjust the point accordingly. If you specify that the line should
+ * be drawn at inPoint = ( 100, 100 ), the line will be drawn there, but the
+ * actual control will be positioned at y = 100 - m_LineHeight.
+ * @param inPoint starting point of the line to be drawn
+ */
+void CInsertionLine::SetPosition(CPt inPoint)
+{
+ inPoint.y -= m_LineHeight;
+ COverlayControl::SetPosition(inPoint);
+}
+
+//=============================================================================
+/**
+ * Sets the width of the horizontal line. The arrows on the ends will
+ * automatically overlap this line. Thus the control will be exactly as wide
+ * as you specify here. You should call this method instead of SetSize.
+ * @param inWidth The new width of the line
+ */
+void CInsertionLine::SetLineWidth(long inWidth)
+{
+ m_LineWidth = inWidth;
+
+ SetSize(CPt(m_LineWidth, GetSize().y));
+}
+
+//=============================================================================
+/**
+ * Draws the insertion line at the current position.
+ * @param inRenderer Renderer to draw to
+ */
+void CInsertionLine::Draw(CRenderer *inRenderer)
+{
+ // Actual insertion line
+ CPt theTotalSize = GetSize();
+ inRenderer->FillSolidRect(
+ CRct(CPt(0, ::dtol(theTotalSize.y / 2.0f) - ::dtol(m_LineHeight / 2.0f)),
+ CPt(m_LineWidth, m_LineHeight)),
+ m_LineColor);
+
+ // Left arrow (draws on top of the left side of the line)
+ inRenderer->DrawBitmap(QPoint(0, 0), m_InsertLeftImage);
+
+ // Right arrow (draws on top of the right side of the line)
+ const auto theRightSize = m_InsertRightImage.size();
+ inRenderer->DrawBitmap(QPoint(theTotalSize.x - theRightSize.width(), 0), m_InsertRightImage);
+}
+
+//=============================================================================
+/**
+ * Check to see if inPoint is over this control or not. Overridden so that
+ * this overlay control does not interfere with drag-and-drop.
+ * @param inPoint not used
+ * @return false
+ */
+bool CInsertionLine::HitTest(const CPt &inPoint) const
+{
+ Q_UNUSED(inPoint);
+
+ return false;
+}
diff --git a/src/Authoring/Studio/Controls/InsertionLine.h b/src/Authoring/Studio/Controls/InsertionLine.h
new file mode 100644
index 00000000..60ec01bf
--- /dev/null
+++ b/src/Authoring/Studio/Controls/InsertionLine.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_INSERTION_LINE_H
+#define INCLUDED_INSERTION_LINE_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "OverlayControl.h"
+#include "CColor.h"
+
+#include <QPixmap>
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//=============================================================================
+/**
+ * Class for drawing a horizontal insertion line on top of other controls.
+ * Useful for indicating drag-and-drop locations. Example usage: Item is
+ * being inserted between Item1 and Item2.
+ * Item1
+ * >----<
+ * Item2
+ */
+class CInsertionLine : public COverlayControl
+{
+public:
+ CInsertionLine();
+ virtual ~CInsertionLine();
+ virtual void SetPosition(CPt inPoint);
+ void SetLineWidth(long inWidth);
+ virtual void Draw(CRenderer *inRenderer);
+ virtual bool HitTest(const CPt &inPoint) const;
+
+protected:
+ long m_LineWidth;
+ long m_LineHeight;
+ CColor m_LineColor;
+ QPixmap m_InsertLeftImage;
+ QPixmap m_InsertRightImage;
+};
+#endif // INCLUDED_INSERTION_LINE_H
diff --git a/src/Authoring/Studio/Controls/InsertionOverlay.cpp b/src/Authoring/Studio/Controls/InsertionOverlay.cpp
new file mode 100644
index 00000000..fc26f68c
--- /dev/null
+++ b/src/Authoring/Studio/Controls/InsertionOverlay.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "InsertionOverlay.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CInsertionOverlay::CInsertionOverlay()
+ : m_Width(0)
+{
+ SetVisible(false);
+
+ m_InsertLeftImage = CResourceCache::GetInstance()->GetBitmap("Insert-Left.png");
+ m_InsertRightImage = CResourceCache::GetInstance()->GetBitmap("Insert-Right.png");
+
+ const auto theLeftSize = m_InsertLeftImage.size();
+ const auto theRightSize = m_InsertRightImage.size();
+
+ if (theLeftSize.height() >= theRightSize.height())
+ SetSize(CPt(theLeftSize.width() + theRightSize.width(), theLeftSize.height()));
+ else
+ SetSize(CPt(theLeftSize.width() + theRightSize.width(), theRightSize.height()));
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CInsertionOverlay::~CInsertionOverlay()
+{
+}
+
+//=============================================================================
+/**
+ * Sets the width of the control. The arrows are always drawn at the left
+ * and right side of the control, as defined by this width.
+ * @param inWidth total width of the control
+ */
+void CInsertionOverlay::SetWidth(long inWidth)
+{
+ m_Width = inWidth;
+
+ SetSize(CPt(m_Width, GetSize().y));
+}
+
+//=============================================================================
+/**
+ * Draws the insertion markers.
+ * Draws arrows at the left and right sides of the control, as defined by m_Width.
+ * @param inRenderer Renderer to draw to
+ */
+void CInsertionOverlay::Draw(CRenderer *inRenderer)
+{
+ // Left arrow
+ inRenderer->DrawBitmap(CPt(0, 0), m_InsertLeftImage);
+
+ // Right arrow
+ const auto theRightSize = m_InsertRightImage.size();
+ inRenderer->DrawBitmap(QPoint(m_Width - theRightSize.width(), 0), m_InsertRightImage);
+}
+
+//=============================================================================
+/**
+ * Check to see if inPoint is over this control or not. Overridden so that
+ * this overlay control does not interfere with drag-and-drop.
+ * @param inPoint not used
+ * @return false
+ */
+bool CInsertionOverlay::HitTest(const CPt &inPoint) const
+{
+ Q_UNUSED(inPoint);
+
+ return false;
+}
diff --git a/src/Authoring/Studio/Controls/InsertionOverlay.h b/src/Authoring/Studio/Controls/InsertionOverlay.h
new file mode 100644
index 00000000..0e43af3b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/InsertionOverlay.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_INSERTION_OVERLAY_H
+#define INCLUDED_INSERTION_OVERLAY_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "OverlayControl.h"
+
+#include <QPixmap>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+
+//=============================================================================
+/**
+ * In drag-and-drop operations it's often desirable to indicate when you are
+ * dropping an item onto another item. This control gives that visual
+ * representation by drawing an arrow at either side. Example: Item2 is selected.
+ * + Item1
+ * > + Item2 <
+ * + Item3
+ */
+class CInsertionOverlay : public COverlayControl
+{
+public:
+ CInsertionOverlay();
+ virtual ~CInsertionOverlay();
+ void SetWidth(long inWidth);
+ virtual void Draw(CRenderer *inRenderer);
+ virtual bool HitTest(const CPt &inPoint) const;
+
+protected:
+ long m_Width;
+ QPixmap m_InsertLeftImage;
+ QPixmap m_InsertRightImage;
+};
+#endif // INCLUDED_INSERTION_OVERLAY_H
diff --git a/src/Authoring/Studio/Controls/LazyFlow.cpp b/src/Authoring/Studio/Controls/LazyFlow.cpp
new file mode 100644
index 00000000..8b8e74fe
--- /dev/null
+++ b/src/Authoring/Studio/Controls/LazyFlow.cpp
@@ -0,0 +1,318 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "LazyFlow.h"
+#include "ControlData.h"
+
+using namespace Q3DStudio::Control;
+
+template <typename TIteratorType>
+struct SVisibleFilteredIterator
+{
+ TIteratorType m_BaseIterator;
+ SVisibleFilteredIterator(TIteratorType inIter)
+ : m_BaseIterator(inIter)
+ {
+ FindNextValidItem();
+ }
+ void FindNextValidItem()
+ {
+ while (m_BaseIterator.IsDone() == false
+ && m_BaseIterator.GetCurrent()->IsVisible() == false)
+ ++m_BaseIterator;
+ }
+
+ SVisibleFilteredIterator<TIteratorType> &operator++()
+ {
+ ++m_BaseIterator;
+ FindNextValidItem();
+ return *this;
+ }
+ std::shared_ptr<CControlData> operator*() { return *m_BaseIterator; }
+ bool IsDone() { return m_BaseIterator.IsDone(); }
+};
+
+struct SaneMinMaxPref : public SSizeGroup
+{
+ SaneMinMaxPref(const SSizeGroup &inData)
+ : SSizeGroup(inData)
+ {
+ // ensure max and pref are greater than or equal to minimum
+ m_Pref.x = max(m_Pref.x, m_Min.x);
+ m_Pref.y = max(m_Pref.y, m_Min.y);
+ m_Max.x = max(m_Max.x, m_Min.x);
+ m_Max.y = max(m_Max.y, m_Min.y);
+
+ // Ensure the maximum has valid information
+ if (m_Max.x == 0)
+ m_Max.x = m_Pref.x;
+ if (m_Max.y == 0)
+ m_Max.y = m_Pref.y;
+
+ // Ensure preference is no greater than the maximum
+ m_Pref.x = min(m_Pref.x, m_Max.x);
+ m_Pref.y = min(m_Pref.y, m_Max.y);
+ }
+};
+
+CPt CLazyFlowBase::GetMinimumSize()
+{
+ MaybeRecalcMinMaxPerf();
+ return CControl::GetMinimumSize();
+}
+CPt CLazyFlowBase::GetPreferredSize()
+{
+ MaybeRecalcMinMaxPerf();
+ return CControl::GetPreferredSize();
+}
+CPt CLazyFlowBase::GetMaximumSize()
+{
+ MaybeRecalcMinMaxPerf();
+ return CControl::GetMaximumSize();
+}
+
+void CLazyFlowBase::LayoutChildren()
+{
+ TControlSizeList theSizeList;
+ DistributeChildSizes(theSizeList);
+ PerformLayout(theSizeList);
+ Invalidate();
+}
+
+void CLazyFlowBase::MarkChildrenNeedLayout()
+{
+ m_MinMaxPreferredDirty = true;
+ CControl::MarkChildrenNeedLayout();
+}
+
+void CLazyFlowBase::NotifyParentNeedsLayout()
+{
+ if (m_CalculatingMinMaxPref == false)
+ CControl::NotifyParentNeedsLayout();
+}
+
+void CLazyFlowBase::SetLayout(CPt inSize, CPt inPosition)
+{
+ bool wasDirty = m_MinMaxPreferredDirty;
+ CControl::SetLayout(inSize, inPosition);
+ m_MinMaxPreferredDirty = wasDirty;
+}
+
+long CLazyFlowBase::GetGapTotal()
+{
+ long theNumVisibleChildren = 0;
+
+ // Ensure we don't count any gaps for children that are invisible
+ for (SVisibleFilteredIterator<ControlGraph::SIterator> theIter = GetChildren();
+ theIter.IsDone() == false; ++theIter, ++theNumVisibleChildren) {
+ }
+
+ if (theNumVisibleChildren > 0)
+ return m_ChildGap * theNumVisibleChildren;
+
+ return 0;
+}
+
+void CLazyFlowBase::DistributeChildSizes(TControlSizeList &ioList)
+{
+ ILazyFlowLayoutSpecializer &theSpecializer(GetSpecializer());
+ SSizeGroup theChildSizes(CalcChildrenMinMaxPref());
+ CPt theMin(theChildSizes.m_Min);
+ CPt thePref(theChildSizes.m_Pref);
+
+ CPt theSize = GetSize();
+ long &theSizeSum(theSpecializer.GetSummedVariable(theSize));
+ long &theSizeMax = theSpecializer.GetMaxedVariable(theSize);
+
+ long theTotalInlineMargin =
+ ILazyFlowLayoutSpecializer::SafeSum(GetBeginMargin(), GetEndMargin());
+ // long theTotalBesideMargin = ILazyFlowLayoutSpecializer::SafeSum( GetBeforeRowMargin(),
+ // GetAfterRowMargin() );
+ long theGapTotal(GetGapTotal());
+ // Remove child independent size information.
+ theSizeSum = theSizeSum - theGapTotal - theTotalInlineMargin;
+ theSizeMax = theSizeMax - GetBeforeRowMargin() - GetAfterRowMargin();
+
+ // sanitize the size after we remove the child indepedent size information
+ theSize.x = max(theMin.x, theSize.x);
+ theSize.y = max(theMin.y, theSize.y);
+
+ // If we have more space than we can use.
+ if (theSizeSum >= theSpecializer.GetSummedVariable(thePref)) {
+ vector<long> theResizeableChildren;
+ long theIndex = 0;
+ CPt theLayoutRect;
+ for (SVisibleFilteredIterator<ControlGraph::SIterator> theIter = GetChildren();
+ theIter.IsDone() == false; ++theIter, ++theIndex) {
+ std::shared_ptr<CControlData> theChildControl(*theIter);
+ SaneMinMaxPref saneSizes(GetChildSizes(*theIter));
+ long theSaneMax(theSpecializer.GetMaxedVariable(saneSizes.m_Max));
+ CPt newSize(theSpecializer.ToPoint(theSpecializer.GetSummedVariable(saneSizes.m_Pref),
+ min(theSizeMax, theSaneMax)));
+ theLayoutRect = theSpecializer.SumMax(theLayoutRect, newSize);
+ ioList.push_back(make_pair(theChildControl, newSize));
+ if (theSpecializer.GetSummedVariable(saneSizes.m_Max) == LONG_MAX)
+ theResizeableChildren.push_back(theIndex);
+ }
+
+ long theRectSum = theSpecializer.GetSummedVariable(theLayoutRect);
+ if (theResizeableChildren.size() && theSizeSum > theRectSum) {
+ long extra = (theSizeSum - theRectSum) / (long)theResizeableChildren.size();
+ for (vector<long>::iterator theIter = theResizeableChildren.begin(),
+ theEnd = theResizeableChildren.end();
+ theIter != theEnd; ++theIter) {
+ pair<std::shared_ptr<CControlData>, CPt> &theChildAndSize(ioList[*theIter]);
+ CPt theNewSize = theChildAndSize.second;
+ theNewSize =
+ theSpecializer.ToPoint(theSpecializer.GetSummedVariable(theNewSize) + extra,
+ theSpecializer.GetMaxedVariable(theNewSize));
+ theChildAndSize.second = theNewSize;
+ }
+ }
+ } else {
+ long theMinSum = theSpecializer.GetSummedVariable(theMin);
+ long thePrefSum = theSpecializer.GetSummedVariable(thePref);
+ long minSizeX = max(theMinSum, theSizeSum);
+ // We know that we got less than the pref size.
+ long requestRange = thePrefSum - theMinSum;
+ long actualRange = minSizeX - theMinSum;
+ float ratio = 0.0f;
+ if (requestRange != 0)
+ ratio = (float)actualRange / (float)requestRange;
+
+ // so we distribute the remaining to each object based on the ratio between
+ // its preferred size to its minimums size, thus reducing each object equally
+ // but on terms of the ratio between it's preferred size and minimum size.
+ for (SVisibleFilteredIterator<ControlGraph::SIterator> theIter = GetChildren();
+ theIter.IsDone() == false; ++theIter) {
+ SaneMinMaxPref saneSizes(GetChildSizes(*theIter));
+ std::shared_ptr<CControlData> theChildControl(*theIter);
+ CPt childPref(saneSizes.m_Pref);
+ CPt childMin(saneSizes.m_Min);
+ long range = theSpecializer.GetSummedVariable(childPref)
+ - theSpecializer.GetSummedVariable(childMin);
+ long newRange = (long)((float)range * ratio + .5f);
+ long newMin = theSpecializer.GetSummedVariable(childMin);
+ CPt newSize = theSpecializer.ToPoint(newMin + newRange, theSizeMax);
+ ioList.push_back(make_pair(theChildControl, newSize));
+ }
+ }
+}
+
+void CLazyFlowBase::PerformLayout(TControlSizeList &ioList)
+{
+ ILazyFlowLayoutSpecializer &theSpecializer(GetSpecializer());
+ CPt thePos(theSpecializer.ToPoint(GetBeginMargin(), GetBeforeRowMargin()));
+ long &theSummedVar = theSpecializer.GetSummedVariable(thePos);
+
+ for (TControlSizeList::iterator iter = ioList.begin(), end = ioList.end(); iter != end;
+ ++iter) {
+ iter->first->SetLayout(iter->second, thePos);
+ // Bump the position out to the next object
+ theSummedVar += theSpecializer.GetSummedVariable(iter->second) + m_ChildGap;
+ }
+}
+
+// Ignoring margins, calculate the min,max, and preferred sizes of the aggregate of our children
+SSizeGroup CLazyFlowBase::CalcChildrenMinMaxPref()
+{
+ ILazyFlowLayoutSpecializer &theSpecializer(GetSpecializer());
+ SSizeGroup retval;
+ for (SVisibleFilteredIterator<ControlGraph::SIterator> theIter = GetChildren();
+ theIter.IsDone() == false; ++theIter) {
+ SaneMinMaxPref saneSizes(GetChildSizes(*theIter));
+ retval.m_Min = theSpecializer.SumMax(retval.m_Min, saneSizes.m_Min);
+ retval.m_Max = theSpecializer.SumMax(retval.m_Max, saneSizes.m_Max);
+ retval.m_Pref = theSpecializer.SumMax(retval.m_Pref, saneSizes.m_Pref);
+ }
+ return retval;
+}
+
+void CLazyFlowBase::RecalcMinMaxPref()
+{
+ ILazyFlowLayoutSpecializer &theSpecializer(GetSpecializer());
+ // Vertical means that we take the max of the x widths
+ // and sum the y heights;
+ // ensuring that LONG_MAX always sums to LONG_MAX
+ long theRowMargin =
+ ILazyFlowLayoutSpecializer::SafeSum(GetBeforeRowMargin(), GetAfterRowMargin());
+ CPt theMin(theSpecializer.ToPoint(GetBeginMargin() + GetEndMargin(), theRowMargin));
+ CPt theMax(theMin);
+ CPt thePref(theMin);
+ SSizeGroup theChildSizes(CalcChildrenMinMaxPref());
+ theMin = theSpecializer.SumMax(theChildSizes.m_Min, theMin);
+ theMax = theSpecializer.SumMax(theChildSizes.m_Max, theMax);
+ thePref = theSpecializer.SumMax(theChildSizes.m_Pref, thePref);
+
+ long theGapTotal(GetGapTotal());
+ theMin = theSpecializer.ToPoint(
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetSummedVariable(theMin), theGapTotal),
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetMaxedVariable(theMin), theRowMargin));
+
+ thePref = theSpecializer.ToPoint(
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetSummedVariable(thePref), theGapTotal),
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetMaxedVariable(thePref),
+ theRowMargin));
+
+ theMax = theSpecializer.ToPoint(
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetSummedVariable(theMax), theGapTotal),
+ ILazyFlowLayoutSpecializer::SafeSum(theSpecializer.GetMaxedVariable(theMax), theRowMargin));
+
+ // Let's ensure we keep sane min,max,etc.
+ thePref.x = max(thePref.x, theMin.x);
+ thePref.y = max(thePref.y, theMin.y);
+ theMax.x = max(theMax.x, theMin.x);
+ theMax.y = max(theMax.y, theMin.y);
+
+ SetMinimumSize(theMin);
+ SetPreferredSize(thePref);
+ SetMaximumSize(theMax);
+}
+
+void CLazyFlowBase::MaybeRecalcMinMaxPerf() const
+{
+ if (m_MinMaxPreferredDirty) {
+ m_MinMaxPreferredDirty = false;
+ if (m_AutoMinMaxPref) {
+ m_CalculatingMinMaxPref = true;
+ CLazyFlowBase &flow(const_cast<CLazyFlowBase &>(*this));
+ flow.RecalcMinMaxPref();
+ m_CalculatingMinMaxPref = false;
+ }
+ }
+}
+
+SSizeGroup CLazyFlowBase::GetChildSizes(std::shared_ptr<CControlData> inControl)
+{
+ SSizeGroup theGroup;
+ theGroup.m_Min = inControl->GetMinimumSize();
+ theGroup.m_Max = inControl->GetMaximumSize();
+ theGroup.m_Pref = inControl->GetPreferredSize();
+ return theGroup;
+}
diff --git a/src/Authoring/Studio/Controls/LazyFlow.h b/src/Authoring/Studio/Controls/LazyFlow.h
new file mode 100644
index 00000000..c5fb7fd3
--- /dev/null
+++ b/src/Authoring/Studio/Controls/LazyFlow.h
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+#ifndef LAZYFLOWH
+#define LAZYFLOWH
+#include "Control.h"
+
+#ifdef WIN32
+#include <minmax.h>
+#endif
+
+namespace Q3DStudio {
+namespace Control {
+
+ // Specializers used to specialize the layout algorithm in the X or Y direction.
+ // You could, in fact, specialzie the algorithm in any arbitrary direction including
+ // 3d where components are sumed in the chosen direction and maxed in any other
+ // orthogonal non-chosen directions.
+
+ // Specializes the general layout algorithm in the x or y direction.
+ struct ILazyFlowLayoutSpecializer
+ {
+ protected:
+ virtual ~ILazyFlowLayoutSpecializer() {}
+
+ public:
+ static inline long SafeSum(long lhs, long rhs)
+ {
+ if (lhs == LONG_MAX || rhs == LONG_MAX)
+ return LONG_MAX;
+ return lhs + rhs;
+ }
+ // These functions allow us to apply the lazy flow layout algorithm to
+ // either horizontal or vertical layouts.
+ // Sum the variable in the direction we are going, and max any other variables
+ CPt SumMax(const CPt &inCurrentSum, const CPt &inOther)
+ {
+ long curSum = GetSummedVariable(inCurrentSum);
+ long nextSum = GetSummedVariable(inOther);
+ long resultSum = SafeSum(curSum, nextSum);
+#ifdef WIN32
+ // windows.h redefines max
+ long resultMax = max(GetMaxedVariable(inCurrentSum), GetMaxedVariable(inOther));
+#else
+ long resultMax = std::max(GetMaxedVariable(inCurrentSum), GetMaxedVariable(inOther));
+#endif
+ return ToPoint(resultSum, resultMax);
+ }
+ // Return the summed variable
+ virtual long &GetSummedVariable(CPt &inPt) = 0;
+ const long &GetSummedVariable(const CPt &inPt)
+ {
+ return GetSummedVariable(const_cast<CPt &>(inPt));
+ }
+ // return the maxed variable
+ virtual long &GetMaxedVariable(CPt &inPt) = 0;
+ const long &GetMaxedVariable(const CPt &inPt)
+ {
+ return GetMaxedVariable(const_cast<CPt &>(inPt));
+ }
+ // Create a new point from the combination of a summed variable and a maxed variable.
+ virtual CPt ToPoint(long inSummedVariable, long inMaxedVariable) = 0;
+ };
+
+ struct SVerticalLayoutSpecializer : public ILazyFlowLayoutSpecializer
+ {
+ long &GetSummedVariable(CPt &inPt) override { return inPt.y; }
+ long &GetMaxedVariable(CPt &inPt) override { return inPt.x; }
+ CPt ToPoint(long inSummedVariable, long inMaxedVariable) override
+ {
+ return CPt(inMaxedVariable, inSummedVariable);
+ }
+ };
+
+ struct SHorizontalLayoutSpecializer : public ILazyFlowLayoutSpecializer
+ {
+ long &GetSummedVariable(CPt &inPt) override { return inPt.x; }
+ long &GetMaxedVariable(CPt &inPt) override { return inPt.y; }
+ CPt ToPoint(long inSummedVariable, long inMaxedVariable) override
+ {
+ return CPt(inSummedVariable, inMaxedVariable);
+ }
+ };
+
+ struct SSizeGroup
+ {
+ CPt m_Min;
+ CPt m_Max;
+ CPt m_Pref;
+ };
+
+ class CLazyFlowBase : public CControl
+ {
+ mutable bool m_MinMaxPreferredDirty;
+ mutable bool m_CalculatingMinMaxPref;
+ bool m_AutoMinMaxPref;
+ CPt m_BeginMargin;
+ CPt m_EndMargin;
+
+ public:
+ typedef vector<pair<std::shared_ptr<CControlData>, CPt>> TControlSizeList;
+
+ CLazyFlowBase()
+ : m_MinMaxPreferredDirty(true)
+ , m_CalculatingMinMaxPref(false)
+ , m_AutoMinMaxPref(true)
+ , m_ChildGap(0)
+ {
+ }
+
+ CPt GetMinimumSize() override;
+ CPt GetPreferredSize() override;
+ CPt GetMaximumSize() override;
+
+ virtual void SetGapBetweenChildren(long inGap)
+ {
+ m_ChildGap = inGap;
+ MarkChildrenNeedLayout();
+ }
+ virtual long GetGapBetweenChildren() const { return m_ChildGap; }
+
+ // Also sets min max preferred dirty.
+ void LayoutChildren() override;
+ void MarkChildrenNeedLayout() override;
+ void NotifyParentNeedsLayout() override;
+ void SetLayout(CPt inSize, CPt inPosition) override;
+
+ virtual void SetAutoMinMaxPref(bool inAuto) { m_AutoMinMaxPref = inAuto; }
+ bool GetAutoMinMaxPref() const { return m_AutoMinMaxPref; }
+
+ // Legacy with FlowLayout.
+ virtual void SetAutoMin(bool inAuto) { m_AutoMinMaxPref = inAuto; }
+
+ virtual void SetTopMargin(long inPixels)
+ {
+ m_BeginMargin.y = inPixels;
+ MarkChildrenNeedLayout();
+ }
+ virtual long GetTopMargin() const { return m_BeginMargin.y; }
+
+ virtual void SetLeftMargin(long inPixels)
+ {
+ m_BeginMargin.x = inPixels;
+ MarkChildrenNeedLayout();
+ }
+ virtual long GetLeftMargin() const { return m_BeginMargin.x; }
+
+ virtual void SetBottomMargin(long inPixels)
+ {
+ m_EndMargin.y = inPixels;
+ MarkChildrenNeedLayout();
+ }
+ virtual long GetBottomMargin() const { return m_EndMargin.y; }
+
+ virtual void SetRightMargin(long inPixels)
+ {
+ m_EndMargin.x = inPixels;
+ MarkChildrenNeedLayout();
+ }
+ virtual long GetRightMargin() const { return m_EndMargin.x; }
+
+ protected:
+ void SetMinMaxPrefDirty() { m_MinMaxPreferredDirty = true; }
+ virtual ILazyFlowLayoutSpecializer &GetSpecializer() = 0;
+
+ long m_ChildGap;
+ long GetGapTotal();
+ // Margins are begin/end meaning before any children, after any children
+ long GetBeginMargin() { return GetSpecializer().GetSummedVariable(m_BeginMargin); }
+ long GetEndMargin() { return GetSpecializer().GetSummedVariable(m_EndMargin); }
+ // And then margines that are beside the row of children, either before the row or after it.
+ long GetBeforeRowMargin() { return GetSpecializer().GetMaxedVariable(m_BeginMargin); }
+ long GetAfterRowMargin() { return GetSpecializer().GetMaxedVariable(m_EndMargin); }
+
+ // I only implement these algorithms once, and I use a specializer to make them apply
+ // to either a left-to-right or a top-to-bottom layout.
+ void DistributeChildSizes(TControlSizeList &ioList);
+ void PerformLayout(TControlSizeList &ioList);
+ SSizeGroup CalcChildrenMinMaxPref();
+
+ virtual SSizeGroup GetChildSizes(std::shared_ptr<CControlData> inControl);
+
+ void RecalcMinMaxPref();
+ void MaybeRecalcMinMaxPerf() const;
+ };
+
+ template <typename TSpecializerType>
+ class TLazyFlowBase : public CLazyFlowBase
+ {
+ TSpecializerType m_Specializer;
+
+ public:
+ ILazyFlowLayoutSpecializer &GetSpecializer() override { return m_Specializer; }
+ };
+
+ struct SVerticalLazyFlow : public TLazyFlowBase<SVerticalLayoutSpecializer>
+ {
+ };
+
+ struct SHorizontalLazyFlow : public TLazyFlowBase<SHorizontalLayoutSpecializer>
+ {
+ };
+
+ class CRuntimeLazyFlow : public CLazyFlowBase
+ {
+ public:
+ enum EFlowDirection {
+ FLOW_VERTICAL,
+ FLOW_HORIZONTAL,
+ };
+
+ private:
+ SHorizontalLayoutSpecializer m_HorizontalSpecifier;
+ SVerticalLayoutSpecializer m_VerticalSpecifier;
+ EFlowDirection m_FlowDirection;
+
+ public:
+ CRuntimeLazyFlow()
+ : m_FlowDirection(FLOW_VERTICAL)
+ {
+ }
+
+ virtual void SetFlowDirection(EFlowDirection inFlowDirection)
+ {
+ m_FlowDirection = inFlowDirection;
+ MarkChildrenNeedLayout();
+ }
+
+ protected:
+ ILazyFlowLayoutSpecializer &GetSpecializer() override
+ {
+ if (m_FlowDirection == FLOW_VERTICAL)
+ return m_VerticalSpecifier;
+ return m_HorizontalSpecifier;
+ }
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Controls/ListBoxItem.cpp b/src/Authoring/Studio/Controls/ListBoxItem.cpp
new file mode 100644
index 00000000..885d8f87
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListBoxItem.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ListBoxItem.h"
+#include "Renderer.h"
+#include "HotKeys.h"
+#include "SystemPreferences.h"
+#include "StudioPreferences.h"
+
+IMPLEMENT_OBJECT_COUNTER(CListBoxItem)
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CListBoxItem::CListBoxItem()
+ : m_Selected(false)
+ , m_BorderColor(CStudioPreferences::GetControlRectSideLineColor())
+ , m_BackgroundColorSelected(CSystemPreferences::GetSelectedItemColor())
+ , m_BackgroundColorUnselected(CColor(255, 255, 255))
+{
+ ADDTO_OBJECT_COUNTER(CListBoxItem)
+
+ // this reserves the minimum height for the ListBoxItem in the ListBox first
+ SetAbsoluteSize(CPt(0, CStudioPreferences::GetDefaultTextEditSize()));
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CListBoxItem::~CListBoxItem()
+{
+ REMOVEFROM_OBJECT_COUNTER(CListBoxItem)
+}
+
+//==============================================================================
+/**
+ * Draws the bounding rectangle and white edit box for the List Box Item class.
+ *
+ * @param inRenderer The renderer that is responsible for drawing this Item.
+ */
+void CListBoxItem::Draw(CRenderer *inRenderer)
+{
+ const auto rect = QRect(QPoint(0, 0), GetSize());
+ inRenderer->PushClippingRect(rect);
+
+ // Fill the interior of the ListBoxItem
+ CColor theFillColor = m_BackgroundColorUnselected;
+
+ // If this ListBox is selected
+ if (m_Selected)
+ theFillColor = m_BackgroundColorSelected;
+
+ inRenderer->FillSolidRect(rect, theFillColor);
+
+ /*
+ CRct theRect = GetSize();
+ CPt theLowerRight( theRect.position.x + theRect.size.x - 1, theRect.position.y + theRect.size.y
+ - 1 );
+ CPt theLowerLeft( theRect.position.x, theLowerRight.y );
+
+ // draw the bottom line for this item;
+ // the top will be drawn by the prev item, otherwise it's the bounding box for the ListBox
+ inRenderer->PushPen( m_BorderColor, 1 );
+ inRenderer->MoveTo( theLowerLeft );
+ inRenderer->LineTo( theLowerRight );
+ inRenderer->PopPen();
+ */
+
+ inRenderer->PopClippingRect();
+}
+
+//=============================================================================
+/**
+ * Handles Mouse down events on this CListBoxItem. When a Mouse Click occurs
+ * on this ListBoxItem, fire off a SelectionEvent, to let the parent
+ * CListBoxControl know that this is selected, and to perform neccesary selection
+ * actions, e.g. Single Selection, or Multiple Selection.
+ *
+ * @param inPoint where the mouse was clicked, in local coordinates.
+ * @param inFlags Modifier keys that are down at time of event. Can be any
+ * combination of the following: MODIFIER_CONTROL | MODIFIER_SHIFT |
+ * MODIFIER_ALT | MOUSE_LBUTTON | MOUSE_RBUTTON | MOUSE_MBUTTON
+ */
+bool CListBoxItem::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theIsHandled = CControl::OnMouseDown(inPoint, inFlags);
+ m_SelectionListeners.FireEvent(&CListBoxSelectionListener::OnSelectItem, this, inFlags);
+ return theIsHandled;
+}
+
+//==============================================================================
+/**
+ * Overridden to help recalc the minimum size of the item
+ */
+void CListBoxItem::OnParentChanged(CControl *inParent)
+{
+ CControl::OnParentChanged(inParent);
+ RecalcMinimumSize();
+}
+
+//==============================================================================
+/**
+ * Overridden to help recalc the minimum size of the item
+ */
+void CListBoxItem::SetMinimumSize(CPt inSize)
+{
+ CControl::SetMinimumSize(inSize);
+ RecalcMinimumSize();
+}
+
+//==============================================================================
+/**
+ * Set the minimum width of the item based on the size of the parent
+ */
+void CListBoxItem::RecalcMinimumSize()
+{
+ if (GetParent()) {
+ CPt theSize = CControl::GetMinimumSize();
+ CPt theParentSize = GetParent()->GetSize();
+
+ // If the minimum width of the item is less than the
+ // current width of the parent, set the size to be the
+ // width of the parent
+ if (theParentSize.x > (theSize.x - GetPosition().x))
+ theSize.x = theParentSize.x - GetPosition().x;
+
+ CControl::SetMinimumSize(theSize);
+ }
+}
+
+//==============================================================================
+/**
+ * Sets this ListBoxItem to be selected or unselected.
+ *
+ * @param inSelected True if this CListBoxItem is selected.
+ */
+void CListBoxItem::SetSelectedState(bool inSelected)
+{
+ m_Selected = inSelected;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Returns true if this ListBoxItem is selected.
+ *
+ * @return true if this CListBoxItem is selected.
+ */
+bool CListBoxItem::IsSelected() const
+{
+ return m_Selected;
+}
+
+//==============================================================================
+/**
+ * Sets the color of this control's border. Defines the color of the bounding
+ * rectangle.
+ *
+ * @param inColor the Color of this control's border.
+ */
+void CListBoxItem::SetBorderColor(const CColor &inColor)
+{
+ m_BorderColor = inColor;
+}
+
+//==============================================================================
+/**
+ * Returns the color of this control's border.
+ *
+ * @return the color of the border of this control.
+ */
+CColor CListBoxItem::GetBorderColor() const
+{
+ return m_BorderColor;
+}
+
+//==============================================================================
+/**
+ * Sets the background color for the ListBoxItem when it is selected.
+ *
+ * @param inColor the background color for the ListBoxItem when it is selected.
+ */
+void CListBoxItem::SetBGColorSelected(const CColor &inColor)
+{
+ m_BackgroundColorSelected = inColor;
+}
+
+//==============================================================================
+/**
+ * Returns the background color for the ListBoxItem when it is selected.
+ *
+ * @return the background color for the ListBoxItem when it is selected.
+ */
+CColor CListBoxItem::GetBGColorSelected() const
+{
+ return m_BackgroundColorSelected;
+}
+
+//==============================================================================
+/**
+ * Sets the background color for the ListBoxItem when it is not selected.
+ *
+ * @param inColor the background color for the ListBoxItem when it is not selected.
+ */
+void CListBoxItem::SetBGColorUnselected(const CColor &inColor)
+{
+ m_BackgroundColorUnselected = inColor;
+}
+
+//==============================================================================
+/**
+ * Returns the background color for the ListBoxItem when it is not selected.
+ *
+ * @return the background color for the ListBoxItem when it is not selected.
+ */
+CColor CListBoxItem::GetBGColorUnselected() const
+{
+ return m_BackgroundColorUnselected;
+}
+
+//==============================================================================
+/**
+ * Add a SelectionListener to this control's list of SelectionListeners.
+ *
+ * @param inListener The SelectionListener to add to the list of SelectionListeners
+ */
+void CListBoxItem::AddSelectionListener(CListBoxSelectionListener *inListener)
+{
+ m_SelectionListeners.AddListener(inListener);
+}
+
+//==============================================================================
+/**
+ * Remove a SelectionListener from this control's list of SelectionListeners.
+ *
+ * @param inListener The SelectionListener to remove from the list of SelectionListeners
+ */
+void CListBoxItem::RemoveSelectionListener(CListBoxSelectionListener *inListener)
+{
+ m_SelectionListeners.RemoveListener(inListener);
+}
diff --git a/src/Authoring/Studio/Controls/ListBoxItem.h b/src/Authoring/Studio/Controls/ListBoxItem.h
new file mode 100644
index 00000000..26e1ecb9
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListBoxItem.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2005 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_LIST_BOX_ITEM_H
+#define INCLUDED_LIST_BOX_ITEM_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "FlowLayout.h"
+#include "Multicaster.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CListBoxItem;
+
+//==============================================================================
+// Functor
+//==============================================================================
+GENERIC_FUNCTOR_2(CListBoxSelectionListener, OnSelectItem, CListBoxItem *, long);
+
+//==============================================================================
+/**
+ * abstract CListBoxItem base class that all other ListBoxItem classes
+ * must inherit from. e.g. CListBoxStringItem, CActionListBoxItem
+ */
+class CListBoxItem : public CControl
+{
+public:
+ CListBoxItem();
+ virtual ~CListBoxItem();
+
+ DEFINE_OBJECT_COUNTER(CListBoxItem)
+
+ virtual Q3DStudio::CString GetString() = 0; ///< returns the string representation of this item
+
+ void Draw(CRenderer *inRenderer) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnParentChanged(CControl *inParent) override;
+ void SetMinimumSize(CPt inSize) override;
+
+ virtual void SetSelectedState(bool inSelected);
+ virtual void RecalcMinimumSize();
+ bool IsSelected() const;
+
+ // Color Accessors / Mutators
+ void SetBorderColor(const CColor &inColor);
+ CColor GetBorderColor() const;
+ void SetBGColorSelected(const CColor &inColor);
+ CColor GetBGColorSelected() const;
+ void SetBGColorUnselected(const CColor &inColor);
+ CColor GetBGColorUnselected() const;
+
+ // SelectionListener methods
+ void AddSelectionListener(CListBoxSelectionListener *inListener);
+ void RemoveSelectionListener(CListBoxSelectionListener *inListener);
+
+protected:
+ bool m_Selected;
+ CColor m_BorderColor;
+ QColor m_BackgroundColorSelected;
+ CColor m_BackgroundColorUnselected;
+ CMulticaster<CListBoxSelectionListener *>
+ m_SelectionListeners; ///< Usually only just the ListBoxControl owning this item
+};
+
+#endif // INCLUDED_LIST_BOX_ITEM_H
diff --git a/src/Authoring/Studio/Controls/ListBoxStringItem.cpp b/src/Authoring/Studio/Controls/ListBoxStringItem.cpp
new file mode 100644
index 00000000..d8efe02b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListBoxStringItem.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Include
+//==============================================================================
+#include "ListBoxStringItem.h"
+#include "Renderer.h"
+#include "SystemPreferences.h"
+
+//==============================================================================
+/**
+ * Constructor
+ *
+ * A ListBoxStringItem contains an immutable StringEdit, extending the base
+ * class ListBoxItem to give basic String display and containing funtionality.
+ * TODO: Need to implement some way to set the alignment of the text.
+ * Right now, setting the alignment makes the drawing of the control
+ * all dirty. That's why it's taken out, for now.
+ *
+ * @param inString the String which this ListBoxStringItem is to hold
+ */
+CListBoxStringItem::CListBoxStringItem()
+ : m_TextColor(0, 0, 0)
+ , m_SelectedTextColor(CSystemPreferences::GetSelectedTextColor())
+{
+ m_StringEdit.SetReadOnly(true);
+ m_StringEdit.AllowAutoSize(true);
+ m_StringEdit.SetAlignment(CTextEdit::LEFT);
+ m_StringEdit.SetFillBackground(false);
+ m_StringEdit.SetTextColor(m_TextColor);
+ AddChild(&m_StringEdit);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CListBoxStringItem::~CListBoxStringItem()
+{
+}
+
+//==============================================================================
+/**
+ * @return the String that this ListBoxStringItem contains
+ */
+void CListBoxStringItem::SetString(const Q3DStudio::CString &inString)
+{
+ m_StringEdit.SetData(inString, false);
+
+ SetMinimumSize(m_StringEdit.GetSize());
+ SetSize(m_StringEdit.GetSize());
+}
+
+//==============================================================================
+/**
+ * @return the String that this ListBoxStringItem contains
+ */
+Q3DStudio::CString CListBoxStringItem::GetString()
+{
+ return m_StringEdit.GetString();
+}
+
+//=============================================================================
+/**
+ * Overridden so that we can change the color of the text based upon selection.
+ * @param inSelected true to select this item, false to deselect it
+ */
+void CListBoxStringItem::SetSelectedState(bool inSelected)
+{
+ CListBoxItem::SetSelectedState(inSelected);
+
+ if (inSelected)
+ m_StringEdit.SetTextColor(m_SelectedTextColor);
+ else
+ m_StringEdit.SetTextColor(m_TextColor);
+}
diff --git a/src/Authoring/Studio/Controls/ListBoxStringItem.h b/src/Authoring/Studio/Controls/ListBoxStringItem.h
new file mode 100644
index 00000000..e950d494
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListBoxStringItem.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_LIST_BOX_STRING_ITEM_H
+#define INCLUDED_LIST_BOX_STRING_ITEM_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "ListBoxItem.h"
+#include "StringEdit.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+/**
+ * CListBoxStringItem contains an immutable StringEdit, extending the base
+ * class ListBoxItem to give basic String displaying and storage.
+ */
+class CListBoxStringItem : public CListBoxItem
+{
+public:
+ CListBoxStringItem();
+ virtual ~CListBoxStringItem();
+
+ virtual void SetString(const Q3DStudio::CString &inString);
+ virtual Q3DStudio::CString GetString(); ///< returns the string representation of this item
+
+ virtual void SetSelectedState(bool inSelected); ///< Overridden so that we can change the color
+ ///of the text based upon selection
+
+protected:
+ CStringEdit m_StringEdit;
+ CColor m_TextColor;
+ CColor m_SelectedTextColor;
+};
+
+#endif // INCLUDED_LIST_BOX_STRING_ITEM_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/ListLayout.cpp b/src/Authoring/Studio/Controls/ListLayout.cpp
new file mode 100644
index 00000000..912cec13
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListLayout.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "ListLayout.h"
+#include "MasterP.h"
+#include "ControlData.h"
+
+using namespace Q3DStudio;
+using namespace Q3DStudio::Control;
+
+CListLayout::CListLayout(bool inAlignChildrenLength /*= false */)
+ : m_IsResizing(false)
+ , m_AlignChildrenLength(inAlignChildrenLength)
+{
+}
+
+CListLayout::~CListLayout()
+{
+}
+
+//=============================================================================
+/**
+ * Add a child to this control.
+ * Overrides to make the layout get recalculated as well.
+ * @param inChild the child to be added.
+ * @param inInsertBefore the location to be added.
+ * @see CControl::AddChild.
+ */
+void CListLayout::AddChild(CControl *inChild, CControl *inInsertBefore /*= nullptr*/)
+{
+ CControl::AddChild(inChild, inInsertBefore);
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Remove a child from this control.
+ * Overrides to make the layout get recalculated as well.
+ * @param inChild the child to be removed.
+ * @see CControl::RemoveChild.
+ */
+void CListLayout::RemoveChild(CControl *inChild)
+{
+ CControl::RemoveChild(inChild);
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * Overrides CControl::SetSize to propagate down to children too.
+ * @param inSize the new size.
+ */
+void CListLayout::SetSize(CPt inSize)
+{
+ if (inSize.x != GetSize().x) {
+ m_IsResizing = true;
+
+ // Go through all the children and set their sizes too
+ ControlGraph::SIterator theChildren = GetChildren();
+ for (; theChildren.HasNext(); ++theChildren) {
+ std::shared_ptr<CControlData> theChild = (*theChildren);
+ if (theChild->IsVisible()) {
+ // Only care about the width, keep the height the same.
+ theChild->SetSize(inSize.x, theChild->GetSize().y);
+ }
+ }
+ m_IsResizing = false;
+ }
+
+ CControl::SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ * Recalculate the layout of all the sub controls.
+ * This will resize this control appropriately and reposition all the children.
+ */
+void CListLayout::RecalcLayout()
+{
+ m_IsResizing = true;
+
+ long theHeight = 0;
+ CPt theCurrentPos(0, 0);
+ long theMinLen = 0;
+
+ ControlGraph::SIterator theChildren = GetChildren();
+ if (m_AlignChildrenLength) {
+ // Go through the first time to determine what is the minimum length for the children
+ for (; theChildren.HasNext(); ++theChildren) {
+ std::shared_ptr<CControlData> theChild = (*theChildren);
+ // Only handle visible children
+ if (theChild->IsVisible()) {
+ CPt theMinimumSize(theChild->GetMinimumSize());
+ if (theMinimumSize.x > theMinLen)
+ theMinLen = theMinimumSize.x;
+ }
+ }
+ // reset this
+ theChildren = GetChildren();
+ }
+
+ // Go through all the children and set their position, as well as the minimum length if required
+ // to
+ for (; theChildren.HasNext(); ++theChildren) {
+ std::shared_ptr<CControlData> theChild = (*theChildren);
+ // Only handle visible children
+ if (theChild->IsVisible()) {
+ CPt theChildSize = theChild->GetSize();
+ theHeight += theChildSize.y;
+
+ if (m_AlignChildrenLength)
+ theChild->SetSize(CPt(theMinLen, theChildSize.y));
+ else { // this would be the only loop to get the minimum length
+ CPt theMinimumSize(theChild->GetMinimumSize());
+ if (theMinimumSize.x > theMinLen)
+ theMinLen = theMinimumSize.x;
+ }
+
+ // Update the position
+ theChild->SetPosition(theCurrentPos);
+ theCurrentPos.y += theChildSize.y;
+ }
+ }
+
+ SetMinimumSize(CPt(theMinLen, theHeight));
+ SetMaximumSize(CPt(LONG_MAX, theHeight));
+
+ SetSize(CPt(theMinLen, theHeight));
+
+ m_IsResizing = false;
+}
+
+//=============================================================================
+/**
+ * Notification to this control that one of the child controls has changed size.
+ * @param inControl the control that changed size.
+ */
+void CListLayout::OnChildSizeChanged(CControl *inControl)
+{
+ Q_UNUSED(inControl);
+
+ // If we are not actively resizing then redo the layout.
+ if (!m_IsResizing) {
+ UICPROFILE(OnChildSizeChanged);
+
+ RecalcLayout();
+ }
+}
+
+#ifdef _DEBUG
+bool CListLayout::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ return CControl::OnMouseDown(inPoint, inFlags);
+}
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/ListLayout.h b/src/Authoring/Studio/Controls/ListLayout.h
new file mode 100644
index 00000000..3b1d7768
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ListLayout.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_LIST_LAYOUT_H
+#define INCLUDED_LIST_LAYOUT_H 1
+
+#pragma once
+
+#include "Control.h"
+
+class CListLayout : public CControl
+{
+public:
+ CListLayout(bool inAlignChildrenLength = false);
+ virtual ~CListLayout();
+
+ void SetSize(CPt inSize) override;
+
+ void AddChild(CControl *inChild, CControl *inInsertBefore = nullptr) override;
+ void RemoveChild(CControl *inChild) override;
+
+ void OnChildSizeChanged(CControl *inControl) override;
+
+ void RecalcLayout();
+
+#ifdef _DEBUG
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+#endif
+
+protected:
+ bool m_IsResizing;
+ bool m_AlignChildrenLength; ///< True when all children in this list should align up
+};
+#endif // INCLUDED_LIST_LAYOUT_H
diff --git a/src/Authoring/Studio/Controls/NameEdit.cpp b/src/Authoring/Studio/Controls/NameEdit.cpp
new file mode 100644
index 00000000..b8cface6
--- /dev/null
+++ b/src/Authoring/Studio/Controls/NameEdit.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "NameEdit.h"
+#include "INamable.h"
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inPropertyProvider interface that will provide properties by name
+ * (such as a CAsset object). Can be nullptr.
+ */
+CNameEdit::CNameEdit(INamable *inNamable)
+ : m_Namable(inNamable)
+ , m_IsSettingData(false)
+ , m_LastMoustDown(0, 0)
+ , m_LastFocused(true)
+ , m_CanEdit(false)
+{
+ ASSERT(m_Namable); // if you don't have a namable, use CTextEditInPlace instead.
+
+ SetData(m_Namable->GetName(), false);
+ AddCommitListener(this);
+ AllowAutoSize(true);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CNameEdit::~CNameEdit()
+{
+ RemoveCommitListener(this);
+}
+
+//=============================================================================
+/**
+ * Called when a CTextEdit control's data is changed. In this case, the name
+ * field is being changed, so the new value is pushed down to Client.
+ * @param inControl the control that had it's data changed
+ */
+void CNameEdit::OnSetData(CControl *inControl)
+{
+ if (!m_IsSettingData && inControl == this && m_Namable) {
+ if (m_Namable->GetName() != GetString()) {
+ m_IsSettingData = true;
+ m_Namable->SetName(GetString());
+ m_IsSettingData = false;
+
+ // Refresh what it is in m_Nameable, because the UI data is already changed and the
+ // underlying data model may not agree ( ie not accepting empty or renaming isn't
+ // allowed )
+ // Ideally UI shouldn't change, it should informing the data model through some kind of
+ // transaction (like how blackfish does it)
+ SetData(m_Namable->GetName(), false);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Called when something on an asset changes, making it dirty. Resets the name
+ * displayed by this control, in case it was the name that changed on the asset.
+ */
+void CNameEdit::OnDirty()
+{
+ SetData(m_Namable->GetName());
+ RefreshDisplayFromData();
+}
+
+bool CNameEdit::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_LastMoustDown = inPoint;
+ m_CanEdit = !m_LastFocused;
+
+ return CTextEditInPlace::OnMouseDown(inPoint, inFlags);
+}
+
+void CNameEdit::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (IsInFocus() && !m_IsInEditMode && !(inFlags & CHotKeys::MOUSE_RBUTTON)) {
+ if (m_CanEdit && m_LastMoustDown == inPoint) {
+ SetEditMode(true);
+ SelectAllText();
+ }
+ } else {
+ CTextEditInPlace::OnMouseUp(inPoint, inFlags);
+ }
+
+ m_CanEdit = false;
+}
+
+void CNameEdit::OnGainFocus()
+{
+ CTextEditInPlace::OnGainFocus();
+ m_LastFocused = false;
+}
+
+void CNameEdit::OnLoseFocus()
+{
+ m_LastFocused = true;
+ CTextEditInPlace::OnLoseFocus();
+}
diff --git a/src/Authoring/Studio/Controls/NameEdit.h b/src/Authoring/Studio/Controls/NameEdit.h
new file mode 100644
index 00000000..cf54bb35
--- /dev/null
+++ b/src/Authoring/Studio/Controls/NameEdit.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_NAME_EDIT
+#define INCLUDED_NAME_EDIT 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TextEditInPlace.h"
+
+class INamable;
+
+//=============================================================================
+/**
+ * @class CNameEdit Edit control for statically displaying a "name" property.
+ * Double-clicking on the text enables you to edit it. Changes to the underlying
+ * name property are automatically handled by this control.
+ */
+class CNameEdit : public CTextEditInPlace, public CCommitDataListener
+{
+public:
+ CNameEdit(INamable *inNamable);
+ virtual ~CNameEdit();
+ void OnSetData(CControl *inControl) override;
+ virtual void OnDirty();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnGainFocus() override;
+ void OnLoseFocus() override;
+
+protected:
+ INamable *m_Namable;
+ bool m_IsSettingData;
+
+ CPt m_LastMoustDown;
+ bool m_LastFocused;
+ bool m_CanEdit;
+};
+
+#endif // INCLUDED_NAME_EDIT
diff --git a/src/Authoring/Studio/Controls/OverlayControl.cpp b/src/Authoring/Studio/Controls/OverlayControl.cpp
new file mode 100644
index 00000000..cca9b402
--- /dev/null
+++ b/src/Authoring/Studio/Controls/OverlayControl.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+#include "OverlayControl.h"
+#include "Renderer.h"
+
+IMPLEMENT_OBJECT_COUNTER(COverlayControl)
+
+//=============================================================================
+/**
+ * Create a new OverlayControl.
+ */
+COverlayControl::COverlayControl()
+ : m_HasSizeChanged(false)
+ , m_WasVisible(false)
+ , m_Alpha(255)
+{
+ ADDTO_OBJECT_COUNTER(COverlayControl)
+}
+
+COverlayControl::~COverlayControl()
+{
+ REMOVEFROM_OBJECT_COUNTER(COverlayControl)
+}
+
+//=============================================================================
+/**
+ * Notification from the parent control that it is going to begin drawing children.
+ * This is used to erase this component before the children are drawn.
+ * @param inRenderer the renderer that is being drawn to.
+ */
+void COverlayControl::BeginDrawChildren(CRenderer *inRenderer)
+{
+ const auto size = GetParent()->GetSize();
+ QRect theClippingRect(0, 0, size.x, size.y);
+
+// doing this = lockup on MacX.
+// not doing this = playhead redraw errors on Windows
+#ifdef WIN32
+ inRenderer->PushClippingRect(theClippingRect);
+#endif
+ // If we don't have a renderer then create one.
+ if (IsVisible() && m_BufferedRenderer == NULL) {
+ m_BufferedRenderer = new CBufferedRenderer(QSize(size.x, size.y));
+ }
+
+ else if (m_HasSizeChanged && m_BufferedRenderer != NULL) {
+ CRct theRect(m_PreviousSize);
+ theRect.Offset(-m_PositionOffset + GetPosition());
+
+ // Overwrite the current contents, clearing this object's drawing.
+ inRenderer->BitBltFrom(theRect, m_BufferedRenderer, 0, 0);
+
+ m_HasSizeChanged = false;
+
+ m_BufferedRenderer = new CBufferedRenderer(QSize(size.x, size.y));
+ }
+
+ else {
+ if ((IsVisible() || m_WasVisible) && m_BufferedRenderer != NULL) {
+ // If this moved since the last begin draw then take in that offset too.
+ CRct theRect(GetSize());
+ theRect.Offset(-m_PositionOffset + GetPosition());
+
+ // Overwrite the current contents, clearing this object's drawing.
+ inRenderer->BitBltFrom(theRect, m_BufferedRenderer, 0, 0);
+
+ if (m_WasVisible)
+ m_BufferedRenderer = nullptr;
+
+ m_WasVisible = false;
+ }
+ }
+
+ m_PositionOffset.x = 0;
+ m_PositionOffset.y = 0;
+
+#ifdef WIN32
+ inRenderer->PopClippingRect();
+#endif
+}
+
+//=============================================================================
+/**
+ * Overrides OnDraw to perform custom drawing logic.
+ * This basically needs to draw all the time regardless of invalidation.
+ * The dirty rect will be properly updated if this is dirty.
+ * @param inRenderer the renderer to draw to.
+ * @param inDirtyRect the resulting dirty rect from drawing.
+ * @param inIgnoreValidation true if this should draw everything.
+ */
+void COverlayControl::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ CRct theClippingRect(-GetPosition(), GetParent()->GetSize());
+ inRenderer->PushClippingRect(theClippingRect);
+
+ if (IsVisible() && m_BufferedRenderer != nullptr) {
+ // Copy the current contents of the background.
+ m_BufferedRenderer->BitBltFrom(CRct(GetSize()), inRenderer, 0, 0);
+
+ bool isInvalidated = IsInvalidated();
+
+ if (isInvalidated || inIgnoreValidation) {
+ // If this is invalidated then update the dirty rect appropriately
+ CRct theBoundingBox = inRenderer->GetClippingRect();
+
+ CRct theRect(GetSize());
+ theRect.And(theBoundingBox);
+ theRect.Offset(inRenderer->GetTranslation());
+ inDirtyRect.Or(theRect);
+ }
+
+ // if ( m_Alpha != 255 )
+ //{
+ // DrawAlpha( inRenderer );
+ //}
+ // else
+ {
+ // Always draw this component to the renderer, regardless of whether it's dirty or not.
+ Draw(inRenderer);
+ }
+
+ Invalidate(false);
+ }
+ CControl::OnDraw(inRenderer, inDirtyRect, inIgnoreValidation);
+
+ inRenderer->PopClippingRect();
+}
+
+//=============================================================================
+/**
+ * Handles drawing this control as being transparent.
+ * @param inRenderer the renderer to draw to.
+ */
+void COverlayControl::DrawAlpha(CRenderer * /*inRenderer*/)
+{
+ /* // Copy the contents of the current background, this will make it so only the areas
+ // that are drawn to are actually blended
+ BUFFEREDRENDERERTYPE theIntermediate( m_BufferedRenderer,
+ m_BufferedRenderer->GetClippingRect() );
+ Draw( &theIntermediate );
+
+ // Do the transparent draw.
+ theIntermediate.TransparentBltTo( inRenderer, m_Alpha );*/
+}
+
+//=============================================================================
+/**
+ * Logs the changes in position so that it can properly reset the background.
+ * @param inPosition the new position.
+ */
+void COverlayControl::SetPosition(CPt inPosition)
+{
+ if (inPosition != GetPosition()) {
+ // Offset the position, make it relative just in case multiple changes occur per cycle.
+ m_PositionOffset += inPosition - GetPosition();
+
+ CControl::SetPosition(inPosition);
+ }
+}
+
+//=============================================================================
+/**
+ * Override of SetSize so that the renderer can be ditched.
+ * @param inSize the new size of this control.
+ */
+void COverlayControl::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ // Record how big this was so the previous drawing can be erased.
+ if (!m_HasSizeChanged && m_BufferedRenderer != nullptr) {
+ m_HasSizeChanged = true;
+ m_PreviousSize = GetSize();
+ }
+
+ CControl::SetSize(inSize);
+ }
+}
+
+//=============================================================================
+/**
+ * Set whether or not this control is visible.
+ * Override to handle final clearing of this control when not visible.
+ * @param inIsVisible true if this is to be visible.
+ */
+void COverlayControl::SetVisible(bool inIsVisible)
+{
+ if (!inIsVisible && IsVisible()) {
+ m_WasVisible = true;
+ }
+ CControl::SetVisible(inIsVisible);
+}
+
+//=============================================================================
+/**
+ * Set the alpha blend of this control.
+ * This defaults to 255 which is opaque, if it is anything other than 255 then
+ * this control will be drawn with transparency.
+ * @param inAlpha the new opacity, 255 = opaque, 0 = transparent.
+ */
+void COverlayControl::SetAlpha(short inAlpha)
+{
+ m_Alpha = inAlpha;
+}
+
+bool COverlayControl::IsChildInvalidated() const
+{
+ return true;
+}
diff --git a/src/Authoring/Studio/Controls/OverlayControl.h b/src/Authoring/Studio/Controls/OverlayControl.h
new file mode 100644
index 00000000..9ad622f8
--- /dev/null
+++ b/src/Authoring/Studio/Controls/OverlayControl.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_OVERLAY_CONTROL_H
+#define INCLUDED_OVERLAY_CONTROL_H 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "Control.h"
+
+//=============================================================================
+// Forwards
+//=============================================================================
+
+#include "BufferedRenderer.h"
+#include "UICObjectCounter.h"
+
+//=============================================================================
+/**
+ * Overlay control provides a way to a control on top of another and move it
+ * around. The drawing is handled so that you do not need to invalidate the
+ * bottom control and you don't get trails to forming on the bottom control.
+ * This is accomplished through the use of a buffered renderer.
+ */
+class COverlayControl : public CControl
+{
+public:
+ COverlayControl();
+ virtual ~COverlayControl();
+
+ DEFINE_OBJECT_COUNTER(COverlayControl)
+
+ virtual void SetSize(CPt inSize);
+ virtual void SetPosition(CPt inPosition);
+ virtual void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false);
+ virtual void BeginDrawChildren(CRenderer *inRenderer);
+ virtual void SetVisible(bool inIsVisible);
+ void SetAlpha(short inAlpha);
+ virtual bool IsChildInvalidated() const;
+
+protected:
+ void DrawAlpha(CRenderer *inRenderer);
+
+ CPt m_PositionOffset;
+ Q3DStudio::CAutoMemPtr<CBufferedRenderer> m_BufferedRenderer;
+ bool m_HasSizeChanged;
+ CPt m_PreviousSize;
+ bool m_WasVisible;
+ short m_Alpha;
+};
+#endif // INCLUDED_OVERLAY_CONTROL_H
diff --git a/src/Authoring/Studio/Controls/ProceduralButton.h b/src/Authoring/Studio/Controls/ProceduralButton.h
new file mode 100644
index 00000000..47b39046
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ProceduralButton.h
@@ -0,0 +1,569 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PROCEDURAL_BUTTON_H
+#define INCLUDED_PROCEDURAL_BUTTON_H 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Renderer.h"
+#include "ButtonControl.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Template class for making procedural buttons. A procedural button has
+ * additional drawing options compared to a normal button. You must specify
+ * a derivative of CButtonControl as the class for the procedural button to
+ * extend.
+ */
+template <typename TButton>
+class CProceduralButton : public TButton
+{
+public:
+ /// Fill color styles for procedurally drawn buttons
+ enum EFillStyle {
+ EFILLSTYLE_GRADIENT, ///< Button background is filled using a gradient
+ EFILLSTYLE_FLOOD, ///< Button background is filled in a solid color
+ EFILLSTYLE_NONE ///< Button background is not filled at all
+ };
+
+ /// Struct enabling you to specify whether or not to draw each border of the button individually
+ struct SBorderOptions
+ {
+ bool m_DrawLeft; ///< If true, you want the left border to be drawn
+ bool m_DrawTop; ///< If true, you want the top border to be drawn
+ bool m_DrawRight; ///< If true, you want the right border to be drawn
+ bool m_DrawBottom; ///< If true, you want the bottom border to be drawn
+
+ /// Default Constructor - makes all borders be drawn
+ SBorderOptions()
+ {
+ m_DrawLeft = true;
+ m_DrawTop = true;
+ m_DrawRight = true;
+ m_DrawBottom = true;
+ }
+
+ /// Constructor to initialize each member of the struct individually
+ SBorderOptions(bool inLeft, bool inTop, bool inRight, bool inBottom)
+ {
+ m_DrawLeft = inLeft;
+ m_DrawTop = inTop;
+ m_DrawRight = inRight;
+ m_DrawBottom = inBottom;
+ }
+ };
+
+public:
+ CProceduralButton();
+ virtual ~CProceduralButton();
+
+ void SetFillStyleUp(EFillStyle inStyle);
+ void SetFillStyleDown(EFillStyle inStyle);
+ void SetFillStyleDisabled(EFillStyle inStyle);
+ void SetFillStyleOver(EFillStyle inStyle);
+ void SetFillStyleAll(EFillStyle inStyle);
+ void SetFillColorUp(const CColor &inColor);
+ void SetFillColorDown(const CColor &inColor);
+ void SetFillColorDisabled(const CColor &inColor);
+ void SetFillColorOver(const CColor &inColor);
+
+ void SetBorderVisibilityAll(const SBorderOptions &inBorderOptions);
+ void SetBorderVisiblityUp(const SBorderOptions &inBorderOptions);
+ void SetBorderVisiblityDown(const SBorderOptions &inBorderOptions);
+ void SetBorderVisiblityDisabled(const SBorderOptions &inBorderOptions);
+ void SetBorderVisiblityOver(const SBorderOptions &inBorderOptions);
+
+ void SetLeftBorderColor(const CColor &inColor);
+ void SetTopBorderColor(const CColor &inColor);
+ void SetRightBorderColor(const CColor &inColor);
+ void SetBottomBorderColor(const CColor &inColor);
+
+protected:
+ virtual void Render(CRenderer *inRenderer);
+ void DrawBackground(CRenderer *inRenderer);
+ void DrawBorder(CRenderer *inRenderer);
+ EFillStyle GetCurrentFillStyle();
+ CColor GetCurrentBackgroundColor();
+ SBorderOptions GetCurrentBorderOptions();
+
+protected:
+ EFillStyle m_UpFillStyle;
+ EFillStyle m_DownFillStyle;
+ EFillStyle m_DisabledFillStyle;
+ EFillStyle m_OverFillStyle;
+ CColor m_UpColor;
+ CColor m_DownColor;
+ CColor m_DisabledColor;
+ CColor m_OverColor;
+ CColor m_LeftBorderColor;
+ CColor m_TopBorderColor;
+ CColor m_RightBorderColor;
+ CColor m_BottomBorderColor;
+ SBorderOptions m_UpBorders;
+ SBorderOptions m_DownBorders;
+ SBorderOptions m_DisabledBorders;
+ SBorderOptions m_OverBorders;
+};
+
+//==============================================================================
+// Template implemenations
+//==============================================================================
+
+//=============================================================================
+/**
+ * Constructor
+ */
+template <typename TButton>
+CProceduralButton<TButton>::CProceduralButton()
+ : TButton()
+ , m_UpFillStyle(EFILLSTYLE_GRADIENT)
+ , m_DownFillStyle(EFILLSTYLE_GRADIENT)
+ , m_DisabledFillStyle(EFILLSTYLE_NONE)
+ , m_OverFillStyle(EFILLSTYLE_GRADIENT)
+ , m_UpColor(CStudioPreferences::GetBaseColor())
+ , m_DownColor(CStudioPreferences::GetButtonDownColor())
+ , m_DisabledColor(CStudioPreferences::GetBaseColor())
+ , m_OverColor(CStudioPreferences::GetBaseColor())
+ , m_LeftBorderColor(CStudioPreferences::GetButtonShadowColor())
+ , m_TopBorderColor(CStudioPreferences::GetButtonShadowColor())
+ , m_RightBorderColor(CStudioPreferences::GetButtonShadowColor())
+ , m_BottomBorderColor(CStudioPreferences::GetButtonShadowColor())
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+template <typename TButton>
+CProceduralButton<TButton>::~CProceduralButton()
+{
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillStyleUp(EFillStyle inStyle)
+{
+ if (m_UpFillStyle != inStyle) {
+ m_UpFillStyle = inStyle;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillStyleDown(EFillStyle inStyle)
+{
+ if (m_DownFillStyle != inStyle) {
+ m_DownFillStyle = inStyle;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillStyleDisabled(EFillStyle inStyle)
+{
+ if (m_DisabledFillStyle != inStyle) {
+ m_DisabledFillStyle = inStyle;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillStyleOver(EFillStyle inStyle)
+{
+ if (m_OverFillStyle != inStyle) {
+ m_OverFillStyle = inStyle;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillStyleAll(EFillStyle inStyle)
+{
+ SetFillStyleUp(inStyle);
+ SetFillStyleDown(inStyle);
+ SetFillStyleDisabled(inStyle);
+ SetFillStyleOver(inStyle);
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillColorUp(const CColor &inColor)
+{
+ if (m_UpColor != inColor) {
+ m_UpColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillColorDown(const CColor &inColor)
+{
+ if (m_DownColor != inColor) {
+ m_DownColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillColorDisabled(const CColor &inColor)
+{
+ if (m_DisabledColor != inColor) {
+ m_DisabledColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetFillColorOver(const CColor &inColor)
+{
+ if (m_OverColor != inColor) {
+ m_OverColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+template <typename TButton>
+void CProceduralButton<TButton>::SetBorderVisibilityAll(const SBorderOptions &inBorderOptions)
+{
+ SetBorderVisiblityUp(inBorderOptions);
+ SetBorderVisiblityDown(inBorderOptions);
+ SetBorderVisiblityDisabled(inBorderOptions);
+ SetBorderVisiblityOver(inBorderOptions);
+}
+
+template <typename TButton>
+void CProceduralButton<TButton>::SetBorderVisiblityUp(const SBorderOptions &inBorderOptions)
+{
+ m_UpBorders = inBorderOptions;
+}
+
+template <typename TButton>
+void CProceduralButton<TButton>::SetBorderVisiblityDown(const SBorderOptions &inBorderOptions)
+{
+ m_DownBorders = inBorderOptions;
+}
+
+template <typename TButton>
+void CProceduralButton<TButton>::SetBorderVisiblityDisabled(const SBorderOptions &inBorderOptions)
+{
+ m_DisabledBorders = inBorderOptions;
+}
+
+template <typename TButton>
+void CProceduralButton<TButton>::SetBorderVisiblityOver(const SBorderOptions &inBorderOptions)
+{
+ m_OverBorders = inBorderOptions;
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetLeftBorderColor(const CColor &inColor)
+{
+ if (m_LeftBorderColor != inColor) {
+ m_LeftBorderColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetTopBorderColor(const CColor &inColor)
+{
+ if (m_TopBorderColor != inColor) {
+ m_TopBorderColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetRightBorderColor(const CColor &inColor)
+{
+ if (m_RightBorderColor != inColor) {
+ m_RightBorderColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::SetBottomBorderColor(const CColor &inColor)
+{
+ if (m_BottomBorderColor != inColor) {
+ m_BottomBorderColor = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::Render(CRenderer *inRenderer)
+{
+ // Fill the background if desired
+ DrawBackground(inRenderer);
+
+ // Draw the button icon
+ TButton::Render(inRenderer);
+
+ // Draw the border
+ DrawBorder(inRenderer);
+}
+
+//=============================================================================
+/**
+ * Fills the background of this button with the appropriate color and style.
+ * The style is specified by calling "GetCurrentFillStyle" which may indicate
+ * not to fill the background at all.
+ * @param inRenderer the renderer to draw to
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::DrawBackground(CRenderer *inRenderer)
+{
+ CPt theSize = TButton::GetSize();
+
+ // Fill in the background color if necessary
+ EFillStyle theFillStyle = GetCurrentFillStyle();
+
+ switch (theFillStyle) {
+ // Perform a gradient fill (block of color that varies slightly along the y-axis)
+ case EFILLSTYLE_GRADIENT: {
+ // The gradient inverts if the button is currently in the down state
+ bool theInvertGradientFlag = (TButton::GetButtonState() == TButton::EBUTTONSTATE_DOWN) ? true : false;
+ inRenderer->DrawGradientBitmap(QRect(0, 0, theSize.x, theSize.y), GetCurrentBackgroundColor(), theInvertGradientFlag,
+ 0.75);
+ } break;
+
+ // Fill the whole area with a block of solid color
+ case EFILLSTYLE_FLOOD:
+ inRenderer->FillSolidRect(QRect(0, 0, theSize.x, theSize.y), GetCurrentBackgroundColor());
+ break;
+
+ // Do not fill the background at all
+ case EFILLSTYLE_NONE:
+ // No break
+ default:
+ // No filling in of the background
+ break;
+ }
+}
+
+//=============================================================================
+/**
+ * Draws the borders around this button as specified in the border options for
+ * the current state of the button. You can turn border drawing on and off
+ * per side of the button, and you can also specify different colors for each
+ * side of the button.
+ * @param inRenderer renderer to draw to
+ */
+template <typename TButton>
+void CProceduralButton<TButton>::DrawBorder(CRenderer *inRenderer)
+{
+ CPt theSize = TButton::GetSize();
+ SBorderOptions theBorders = CProceduralButton::GetCurrentBorderOptions();
+
+ if (theBorders.m_DrawLeft) {
+ inRenderer->PushPen(m_LeftBorderColor);
+ inRenderer->MoveTo(CPt(0, theSize.y - 1));
+ inRenderer->LineTo(CPt(0, 0));
+ inRenderer->PopPen();
+ }
+
+ if (theBorders.m_DrawTop) {
+ inRenderer->PushPen(m_TopBorderColor);
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(theSize.x - 1, 0));
+ inRenderer->PopPen();
+ }
+
+ if (theBorders.m_DrawRight) {
+ inRenderer->PushPen(m_RightBorderColor);
+ if (!theBorders.m_DrawTop)
+ inRenderer->MoveTo(CPt(theSize.x - 1, 0));
+ else
+ inRenderer->MoveTo(CPt(theSize.x - 1, 1));
+ inRenderer->LineTo(CPt(theSize.x - 1, theSize.y - 1));
+ inRenderer->PopPen();
+ }
+
+ if (theBorders.m_DrawBottom) {
+ inRenderer->PushPen(m_BottomBorderColor);
+ inRenderer->MoveTo(CPt(theSize.x - 1, theSize.y - 1));
+ if (!theBorders.m_DrawLeft)
+ inRenderer->LineTo(CPt(0, theSize.y - 1));
+ else
+ inRenderer->LineTo(CPt(1, theSize.y - 1));
+ inRenderer->PopPen();
+ }
+}
+
+//=============================================================================
+/**
+ * @return the current fill style based upon state (up, down, mouse over, disabled) of the button
+ */
+template <typename TButton>
+typename CProceduralButton<TButton>::EFillStyle
+CProceduralButton<TButton>::GetCurrentFillStyle()
+{
+ // Default to the up state
+ EFillStyle theFillStyle = m_UpFillStyle;
+
+ typename TButton::EButtonState theState = TButton::GetButtonState();
+
+ // If the mouse is over the button, switch to the mouse over style
+ if (TButton::IsMouseOver())
+ theFillStyle = m_OverFillStyle;
+
+ // If the button is currently pressed, this cancels the up and over states, so return the down
+ // style
+ if (theState == TButton::EBUTTONSTATE_DOWN)
+ theFillStyle = m_DownFillStyle;
+
+ // If this button is disabled just return the disabled style (overrides any other states)
+ if (!TButton::IsEnabled())
+ theFillStyle = m_DisabledFillStyle;
+
+ return theFillStyle;
+}
+
+//=============================================================================
+/**
+ * @return the current background color based upon state (up, down, mouse over, disabled) of the
+ * button
+ */
+template <typename TButton>
+::CColor CProceduralButton<TButton>::GetCurrentBackgroundColor()
+{
+ // Default to the up color
+ ::CColor theColor = m_UpColor;
+
+ typename TButton::EButtonState theState = TButton::GetButtonState();
+
+ // If the mouse is over the button, switch to the mouse over color
+ if (TButton::IsMouseOver())
+ theColor = m_OverColor;
+
+ // If the button is currently pressed, this cancels the up and over states, so return the down
+ // color
+ if (theState == TButton::EBUTTONSTATE_DOWN)
+ theColor = m_DownColor;
+
+ // If this button is disabled just return the disabled color (overrides any other states)
+ if (!TButton::IsEnabled())
+ theColor = m_DisabledColor;
+
+ return theColor;
+}
+
+//=============================================================================
+/**
+ * @return the current border drawing options based upon state (up, down, mouse over, disabled) of
+ * the button
+ */
+template <typename TButton>
+typename CProceduralButton<TButton>::SBorderOptions
+CProceduralButton<TButton>::GetCurrentBorderOptions()
+{
+ SBorderOptions theOptions = m_UpBorders;
+ typename TButton::EButtonState theState = TButton::GetButtonState();
+
+ // If the mouse is over the button, switch to the mouse over border options
+ if (TButton::IsMouseOver())
+ theOptions = m_OverBorders;
+
+ // If the button is currently pressed, this cancels the up and over states, so return the over
+ // border options
+ if (theState == TButton::EBUTTONSTATE_DOWN)
+ theOptions = m_DownBorders;
+
+ // If this button is disabled just return the disabled border options (overrides any other
+ // states)
+ if (!TButton::IsEnabled())
+ theOptions = m_DisabledBorders;
+
+ return theOptions;
+}
+
+#endif // INCLUDED_PROCEDURAL_BUTTON_H
diff --git a/src/Authoring/Studio/Controls/Renderer.cpp b/src/Authoring/Studio/Controls/Renderer.cpp
new file mode 100644
index 00000000..1a750196
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Renderer.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "Renderer.h"
+
+#include <QPoint>
+#include <QRect>
+
+/**
+ * Draws the outline of a rectangle in the specified colors.
+ *
+ * @param inRect Rectangle to be outlined
+ * @param inTop Color of the top line
+ * @param inRight Color of the line on the right side
+ * @param inBottom Color of the line on the bottom
+ * @param inLeft Color of the line on the left side
+ */
+void CRenderer::DrawRectOutline(const QRect &inRect, const QColor &inTop, const QColor &inRight,
+ const QColor &inBottom, const QColor &inLeft)
+{
+ QPoint theUpperLeft(inRect.topLeft());
+ QPoint theUpperRight(inRect.topRight());
+ QPoint theLowerRight(inRect.bottomRight());
+ QPoint theLowerLeft(inRect.bottomLeft());
+
+ // Top
+ PushPen(inTop, 1);
+ MoveTo(theUpperLeft.x(), theUpperLeft.y());
+ LineTo(theUpperRight.x(), theUpperRight.y());
+ PopPen();
+
+ // Right
+ PushPen(inRight, 1);
+ LineTo(theLowerRight.x(), theLowerRight.y());
+ PopPen();
+
+ // Bottom
+ PushPen(inBottom, 1);
+ LineTo(theLowerLeft.x(), theLowerLeft.y());
+ PopPen();
+
+ // Left
+ PushPen(inLeft, 1);
+ LineTo(theUpperLeft.x(), theUpperLeft.y());
+ PopPen();
+}
diff --git a/src/Authoring/Studio/Controls/Renderer.h b/src/Authoring/Studio/Controls/Renderer.h
new file mode 100644
index 00000000..6feca4ed
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Renderer.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_RENDERER_H
+#define INCLUDED_RENDERER_H 1
+
+#pragma once
+
+#include <QColor>
+#include <QPoint>
+
+#include "CColor.h"
+
+QT_BEGIN_NAMESPACE
+class QPainter;
+class QPixmap;
+class QRect;
+class QSize;
+class QString;
+QT_END_NAMESPACE
+
+class CRenderer
+{
+ typedef std::vector<QPoint> TTranslationList;
+
+public:
+ virtual ~CRenderer() {}
+
+ virtual QPainter* GetPainter() = 0;
+ virtual void FillSolidRect(const QRect &inCoordinates, const QColor &inColor) = 0;
+ virtual void MoveTo(const QPoint &inPoint) = 0;
+ virtual void MoveTo(long inX, long inY) = 0;
+ virtual void LineTo(const QPoint &inPoint) = 0;
+ virtual void LineTo(long inX, long inY) = 0;
+
+ virtual void PushPen(const QColor &inColor,
+ int inWidth = 1) = 0; //, UINT inStyle = PS_SOLID );
+ virtual void PopPen() = 0;
+ virtual void BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc) = 0;
+
+ virtual void DrawText(float inX, float inY, const QString &inText) = 0;
+ virtual void DrawText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingRect, const QColor &inColor = Qt::black) = 0;
+ virtual void DrawBoldText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingRect, const QColor &inColor = Qt::black) = 0;
+
+ virtual QSize GetTextSize(const QString &inText) = 0;
+
+ virtual void DrawBitmap(const QPoint &inPos, const QPixmap &inImage) = 0;
+ virtual void Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor,
+ const QColor &inBottomRightColor) = 0;
+ virtual void DrawRectOutline(const QRect &inRect, const QColor &inTop, const QColor &inRight,
+ const QColor &inBottom, const QColor &inLeft);
+ virtual void DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted,
+ double inScalingFactor = .99) = 0;
+
+ virtual void DrawGradient(const QRect &inRect, const QColor &inBeginColor,
+ const QColor &inEndColor) = 0;
+
+ virtual void PushTranslation(const QPoint &inTranslation) = 0;
+ virtual void PopTranslation() = 0;
+ virtual QPoint GetTranslation() = 0;
+
+ virtual QRect GetClippingRect() = 0;
+ virtual void PushClippingRect(const QRect &inRect) = 0;
+ virtual void PushAbsoluteClippingRect(const QRect &inRect) = 0;
+ virtual void PopClippingRect() = 0;
+
+ virtual QRect Scroll(const QPoint &inAmount, const QRect &inVisibleSize) = 0;
+
+ virtual void FillHashed(const QRect &inRect, const QColor &inForeGroundColor) = 0;
+
+ virtual QPixmap pixmap() const = 0;
+
+protected:
+ QPoint m_Translation;
+ TTranslationList m_Translations;
+};
+#endif // INCLUDED_RENDERER_H
diff --git a/src/Authoring/Studio/Controls/SIcon.cpp b/src/Authoring/Studio/Controls/SIcon.cpp
new file mode 100644
index 00000000..5df99717
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SIcon.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SIcon.h"
+#include "Renderer.h"
+#include "MasterP.h"
+#include "ResourceCache.h"
+
+//=============================================================================
+/**
+ * Create a new Icon with no image.
+ */
+CSIcon::CSIcon()
+{
+ SetAbsoluteSize(CPt(0, 0));
+ SetImage(QPixmap());
+}
+
+//=============================================================================
+/**
+ * Create a new Icon with the specified images loaded from the resource cache.
+ * @param inResName Name of the image to be loaded for the normal icon
+ * @param inDisabledResName Name of the image to be loaded for the icon when it's disabled
+ */
+CSIcon::CSIcon(const QString &inResName, const QString &inDisabledResName /*= ""*/)
+{
+ SetAbsoluteSize(CPt(0, 0));
+ CResourceCache *theCache = CResourceCache::GetInstance();
+ m_EnabledImage = theCache->GetBitmap(inResName);
+ m_DisabledImage = theCache->GetBitmap(inDisabledResName);
+ SetImage(m_EnabledImage);
+}
+
+//=============================================================================
+/**
+ * Create a new Icon with the specified image.
+ * Any pixels in the image with the transparent color will not be drawn.
+ * The size of this control will be set to the size of the image.
+ * @param inResource string specifying what image to draw.
+ * @param inDisabledResource string specifying what disabled image to draw
+ */
+CSIcon::CSIcon(const QPixmap &inResource, const QPixmap &inDisabledResource /*=NULL*/)
+ : m_DisabledImage(inDisabledResource)
+ , m_EnabledImage(inResource)
+{
+ SetAbsoluteSize(CPt(0, 0));
+ SetImage(m_EnabledImage);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSIcon::~CSIcon()
+{
+}
+
+//=============================================================================
+/**
+ * Set the image that this icon is drawing as.
+ * Any pixels in the image with the transparent color will not be drawn.
+ * The size of this control will be set to the size of the image.
+ * @param inResource the resource ID if the new image to draw.
+ * @param inPreserveSize false to check and set size ( setting to true can reduce flickering in some
+ * cases )
+ */
+void CSIcon::SetImage(const QPixmap &inResource, bool inPreserveSize)
+{
+ m_Image = inResource;
+
+ if (!inPreserveSize) {
+ // If we successfully loaded the new image then modify the size.
+ if (!m_Image.isNull())
+ SetAbsoluteSize(CPt(GetImageSize().width(), GetImageSize().height()));
+ else
+ SetAbsoluteSize(CPt(0, 0));
+ }
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Set image to the one specified
+ * @param inResName Name of the image to be loaded for the normal icon
+ * @param inPreserveSize false to check and set size ( setting to true can reduce flickering in some
+ * cases )
+ */
+void CSIcon::SetImage(const QString &inResName, bool inPreserveSize)
+{
+ CResourceCache *theCache = CResourceCache::GetInstance();
+ SetImage(theCache->GetBitmap(inResName), inPreserveSize);
+}
+
+//=============================================================================
+/**
+ * Draw this image.
+ * @param inRenderer the renderer to draw to.
+ */
+void CSIcon::Draw(CRenderer *inRenderer)
+{
+ inRenderer->PushClippingRect(QRect(QPoint(0,0), GetSize()));
+ if (!m_Image.isNull()) {
+ inRenderer->DrawBitmap(CPt(0, 0), m_Image);
+ }
+ inRenderer->PopClippingRect();
+}
+
+//=============================================================================
+/**
+ * Get the size of this image.
+ */
+QSize CSIcon::GetImageSize()
+{
+ return m_Image.size();
+}
+
+//=============================================================================
+/**
+ * Enables or disables the parent and switches the image to be displayed for
+ * this icon to a disabled or enabled image.
+ */
+void CSIcon::SetParentEnabled(bool inIsEnabled)
+{
+ CControl::SetParentEnabled(inIsEnabled);
+ if (!IsEnabled()) {
+ SetImage(m_DisabledImage);
+ } else {
+ SetImage(m_EnabledImage);
+ }
+ Invalidate();
+}
diff --git a/src/Authoring/Studio/Controls/SIcon.h b/src/Authoring/Studio/Controls/SIcon.h
new file mode 100644
index 00000000..53201e01
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SIcon.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_ICON_H
+#define INCLUDED_ICON_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+
+#include <QPixmap>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//=============================================================================
+/**
+ * Class for drawing bitmapped icon controls to the renderer.
+ */
+class CSIcon : public CControl
+{
+public:
+ CSIcon();
+ CSIcon(const QString &inResName, const QString &inDisabledResName = QString());
+ CSIcon(const QPixmap &inResource, const QPixmap &inDisabledResource = {});
+
+ virtual ~CSIcon();
+
+ void SetImage(const QPixmap &inResource, bool inPreserveSize = false);
+ void SetImage(const QString &inResName, bool inPreserveSize = false);
+ QSize GetImageSize();
+ void SetParentEnabled(bool inParentEnabled) override;
+
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+ QPixmap m_Image;
+ QPixmap m_DisabledImage;
+ QPixmap m_EnabledImage;
+};
+#endif // INCLUDED_ICON_H
diff --git a/src/Authoring/Studio/Controls/ScrollController.cpp b/src/Authoring/Studio/Controls/ScrollController.cpp
new file mode 100644
index 00000000..8b1dd72f
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollController.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ScrollController.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "Scroller.h"
+#include "Control.h"
+
+using namespace Q3DStudio;
+
+const long CScrollController::DEFAULT_SCROLL_AMOUNT = 20;
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CScrollController::CScrollController()
+ : m_Orientation(IScrollerBar::VERTICAL)
+ , m_ScrollerBar(NULL)
+ , m_Control(NULL)
+ , m_ScrollingForward(false)
+ , m_ScrollingBackward(false)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CScrollController::~CScrollController()
+{
+}
+
+//=============================================================================
+/**
+ * Sets the control. Used to get the check if the mouse is over the control.
+ */
+void CScrollController::SetControl(CControl *inControl)
+{
+ m_Control = inControl;
+}
+
+//=============================================================================
+/**
+ * Sets the scroller bar for this control. Used to get the thumb position in
+ * OnMouseDown.
+ */
+void CScrollController::SetScrollerBar(IScrollerBar *inScrollerBar)
+{
+ m_ScrollerBar = inScrollerBar;
+}
+
+//=============================================================================
+/**
+ * Tells this control which way to draw itself.
+ * @param inOrientation Direction of the scroller that is using this control
+ */
+void CScrollController::SetOrientation(IScrollerBar::EOrientation inOrientation)
+{
+ m_Orientation = inOrientation;
+}
+
+//=============================================================================
+/**
+ * Returns the amount to scroll
+ */
+long CScrollController::DetermineScrollAmount()
+{
+ return DEFAULT_SCROLL_AMOUNT;
+}
+
+//=============================================================================
+/**
+ * Call to scroll forwards.
+ * Forwards is right/down depending on orientation.
+ */
+void CScrollController::OnScrollForward(CControl *inButton)
+{
+ if (!m_ScrollingForward && inButton) {
+ m_ScrollingForward = true;
+
+ Q3DStudio::CString theName = "CScrollController::OnScrollForward";
+ if (m_Control) {
+ theName += "::%s";
+ theName.Format(theName, static_cast<const wchar_t *>(m_Control->GetName()));
+ }
+
+ m_TimerConnection = ITickTock::GetInstance().AddTimer(
+ 100, true, std::bind(&CScrollController::OnTimer, this), theName);
+ }
+
+ Q_UNUSED(inButton);
+ CPt theScrollDistance(0, 0);
+ long theScrollAmount = DetermineScrollAmount();
+
+ if (m_ScrollerBar->GetOrientation() == IScrollerBar::VERTICAL) {
+ theScrollDistance.y = theScrollAmount;
+ } else {
+ theScrollDistance.x = theScrollAmount;
+ }
+
+ m_ScrollerBar->GetScroller()->SetVisiblePosition(
+ m_ScrollerBar->GetScroller()->GetVisiblePosition() + theScrollDistance);
+ m_ScrollerBar->RepositionThumb();
+}
+
+//=============================================================================
+/**
+ * Call to scroll backwards.
+ * Backwards is left/up depending on orientation.
+ */
+void CScrollController::OnScrollBackward(CControl *inButton)
+{
+ if (!m_ScrollingBackward && inButton) {
+ m_ScrollingBackward = true;
+
+ Q3DStudio::CString theName = "CScrollController::OnScrollBackward";
+ if (m_Control) {
+ theName += "::%s";
+ theName.Format(theName, static_cast<const wchar_t *>(m_Control->GetName()));
+ }
+
+ m_TimerConnection = ITickTock::GetInstance().AddTimer(
+ 100, true, std::bind(&CScrollController::OnTimer, this), theName);
+ }
+
+ Q_UNUSED(inButton);
+ CPt theScrollDistance(0, 0);
+ long theScrollAmount = DetermineScrollAmount();
+
+ if (m_ScrollerBar->GetOrientation() == IScrollerBar::VERTICAL) {
+ theScrollDistance.y = theScrollAmount;
+ } else {
+ theScrollDistance.x = theScrollAmount;
+ }
+
+ m_ScrollerBar->GetScroller()->SetVisiblePosition(
+ m_ScrollerBar->GetScroller()->GetVisiblePosition() - theScrollDistance);
+ m_ScrollerBar->RepositionThumb();
+}
+
+//=============================================================================
+/**
+ * Cancels the scrolling. Removes the timer.
+ */
+void CScrollController::OnCancelScrolling(CControl *inButton)
+{
+ Q_UNUSED(inButton);
+
+ m_ScrollingForward = false;
+ m_ScrollingBackward = false;
+
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+}
+
+//=============================================================================
+/**
+ * Overwritten TickTockProc thats called everytime the timer is activated.
+ */
+void CScrollController::OnTimer()
+{
+ if (m_ScrollingForward) {
+ // We want it to scroll only if the mouse is over the control
+ if (m_Control->IsMouseOver()) {
+ OnScrollForward(nullptr);
+ }
+ } else if (m_ScrollingBackward) {
+ // We want it to scroll only if the mouse is over the control
+ if (m_Control->IsMouseOver()) {
+ OnScrollBackward(nullptr);
+ }
+ } else // The timer should not be called if we are not scrolling forward nor backwards
+ {
+ ASSERT(false);
+ }
+}
diff --git a/src/Authoring/Studio/Controls/ScrollController.h b/src/Authoring/Studio/Controls/ScrollController.h
new file mode 100644
index 00000000..9c9cdf41
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollController.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SCROLL_CONTROLLER_H
+#define INCLUDED_SCROLL_CONTROLLER_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+#include "StudioPreferences.h"
+#include "ITickTock.h"
+#include "ScrollerBar.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CControl;
+
+//=============================================================================
+/**
+ * Extends the blank control to draw items specific to the scroller control.
+ */
+class CScrollController
+{
+public:
+ static const long DEFAULT_SCROLL_AMOUNT;
+
+ CScrollController();
+ virtual ~CScrollController();
+ void SetControl(CControl *inControl);
+ void SetScrollerBar(IScrollerBar *inScrollerBar);
+ void SetOrientation(IScrollerBar::EOrientation inOrientation);
+
+ virtual void OnScrollForward(CControl *inControl = nullptr);
+ virtual void OnScrollBackward(CControl *inControl = nullptr);
+ virtual void OnCancelScrolling(CControl *inControl = nullptr);
+
+protected:
+ void OnTimer();
+
+ std::shared_ptr<UICDM::ISignalConnection> m_TimerConnection;
+ IScrollerBar::EOrientation m_Orientation;
+ IScrollerBar *m_ScrollerBar;
+ CControl *m_Control;
+
+ bool m_ScrollingForward;
+ bool m_ScrollingBackward;
+
+ virtual long DetermineScrollAmount();
+};
+
+#endif // INCLUDED_SCROLL_CONTROLLER_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/Scroller.cpp b/src/Authoring/Studio/Controls/Scroller.cpp
new file mode 100644
index 00000000..9dd3d006
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Scroller.cpp
@@ -0,0 +1,829 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Scroller.h"
+#include "Renderer.h"
+#include "ButtonControl.h"
+#include "MasterP.h"
+#include "UICDMSignals.h"
+#include "ControlData.h"
+
+#include <QDateTime>
+
+using namespace Q3DStudio;
+const unsigned long ORIGINAL_DELAY_TIME = 200;
+const unsigned long ORIGINAL_OFFSET_AMMOUNT = 205;
+//=============================================================================
+/**
+ * Creates a scroller.
+ * @param inCreateImmediately used by subclasses, true if using base class of CScroller
+ */
+CScroller::CScroller(bool inCreateImmediately /* = true */)
+ : m_VisibleSize(0, 0)
+ , m_VisiblePosition(0, 0)
+ , m_VerticalScrollMode(AS_NEEDED)
+ , m_HorizontalScrollMode(AS_NEEDED)
+ , m_ResizingChildren(false)
+ , m_IsMouseDown(false)
+ , m_OffsetAmmount(ORIGINAL_OFFSET_AMMOUNT)
+ , m_DelayTime(ORIGINAL_DELAY_TIME)
+{
+ if (inCreateImmediately)
+ Initialize();
+ m_ControlData->SetMouseWheelEnabled(true);
+
+ SetName("Scroller");
+}
+
+CScroller::~CScroller()
+{
+ delete m_VerticalBar;
+ delete m_HorizontalBar;
+}
+
+//=============================================================================
+/**
+ * Performed once and only once!
+ * Do not call if CScroller was called with the createImmediately flag as true.
+ */
+void CScroller::Initialize()
+{
+ m_VerticalBar = CreateVerticalBar();
+ m_HorizontalBar = CreateHorizontalBar();
+
+ AddChild(m_VerticalBar);
+ AddChild(m_HorizontalBar);
+}
+
+//=============================================================================
+/**
+ * Virtual function to get the vertical scroll bar.
+ * This allows overriding so subclasses can subclass the ScrollerBar as well.
+ * The created bar will be deleted on the destruction of this class.
+ * @return the created scrollerbar.
+ */
+CScrollerBar *CScroller::CreateVerticalBar()
+{
+ CScrollerBar *theBar = new CScrollerBar(this);
+ theBar->SetOrientation(CScrollerBar::VERTICAL);
+
+ return theBar;
+}
+
+//=============================================================================
+/**
+ * Virtual function to get the horizontal scroll bar.
+ * This allows overriding so subclasses can subclass the ScrollerBar as well.
+ * The created bar will be deleted on the destruction of this class.
+ * @return the created scrollerbar.
+ */
+CScrollerBar *CScroller::CreateHorizontalBar()
+{
+ CScrollerBar *theBar = new CScrollerBar(this);
+ theBar->SetOrientation(CScrollerBar::HORIZONTAL);
+
+ return theBar;
+}
+
+void CScroller::OnSizeChanged(CPt /*inSize*/)
+{
+ CPt theMaxPoint = GetMaxVisiblePosition();
+ CPt theVisPoint = GetVisiblePosition();
+ if (theVisPoint.x > theMaxPoint.x)
+ theVisPoint.x = theMaxPoint.x;
+ if (theVisPoint.y > theMaxPoint.y)
+ theVisPoint.y = theMaxPoint.y;
+
+ SetVisiblePosition(theVisPoint);
+ RecalcLayout();
+}
+//=============================================================================
+/**
+ * Sets the size of the scroller.
+ * @param inSize the new size for this scroller.
+ */
+void CScroller::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ CControl::SetSize(inSize);
+ OnSizeChanged(inSize);
+ }
+}
+
+void CScroller::SetLayout(CPt inSize, CPt inPosition)
+{
+ CControl::SetLayout(inSize, inPosition);
+ OnSizeChanged(inSize);
+}
+
+//=============================================================================
+/**
+ * Gets the minimum size that this scroller is allowed to be.
+ * This is calculated from the minimum size of the child control and the
+ * needed size of the scroll bars.
+ * @return the minimum size of this object.
+ */
+CPt CScroller::GetMinimumSize()
+{
+ CPt theSize(0, 0);
+ CControl *theChildControl = GetControl();
+ if (theChildControl != nullptr) {
+ CPt theChildMin = theChildControl->GetMinimumSize();
+ // If at zero the scroll bars are up then include them in the size.
+ if (theChildMin.x != 0) {
+ theSize += m_HorizontalBar->GetMinimumSize();
+ }
+ if (theChildMin.y != 0) {
+ theSize += m_VerticalBar->GetMinimumSize();
+ }
+ }
+
+ return theSize;
+}
+
+//=============================================================================
+/**
+ * Get the maximum size of this scroller.
+ * This is just the maximum size of the child window, since the scroll bars
+ * will not be up when at max size.
+ * @return the maximum size of this control.
+ */
+CPt CScroller::GetMaximumSize()
+{
+ CPt theMaxSize;
+ CControl *theChildControl = GetControl();
+ if (theChildControl != nullptr)
+ theMaxSize = theChildControl->GetMaximumSize();
+ else
+ theMaxSize = CControl::GetMaximumSize();
+
+ return theMaxSize;
+}
+
+//=============================================================================
+/**
+ * Add a child control to this control.
+ * This is used to add a new child to this control. If there is already a
+ * component in the scrolling window then inControl will take the place of the
+ * previous control and the previous will be removed as a child of this.
+ * @param inControl the control to be added.
+ * @param inInsertBefore not used.
+ */
+void CScroller::AddChild(CControl *inControl, CControl *inInsertBefore /*= nullptr*/)
+{
+ Q_UNUSED(inInsertBefore);
+
+ if (GetChildCount() > 3)
+ RemoveChild(GetChildren().GetCurrent()->GetControl());
+
+ if (GetChildCount() > 0)
+ CControl::AddChild(inControl, GetChildren().GetCurrent()->GetControl());
+ else
+ CControl::AddChild(inControl);
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Recalculate the layout of this object.
+ * This is used anytime this is changing size or needs to figure out where the
+ * controls go. This does all the work of setting the scrollerbar positions and
+ * the child window positions.
+ */
+void CScroller::RecalcLayout()
+{
+ UICPROFILE(RecalcLayout);
+
+ m_ResizingChildren = true;
+
+ CPt mySize = GetSize();
+ m_VisibleSize = mySize;
+
+ // If the side bar should be visible
+ if (IsVerticalVisible()) {
+ // Make sure the side bar is visible.
+ m_VerticalBar->SetVisible(true);
+
+ // Set the side bars position
+ m_VerticalBar->SetPosition(CPt(mySize.x - m_VerticalBar->GetMinimumSize().x, 0));
+ // the width of the vertical bar is subtracted from the visible area.
+ m_VisibleSize.x -= m_VerticalBar->GetMinimumSize().x;
+
+ if (!IsVerticalScrolling())
+ m_VerticalBar->SetEnabled(false);
+ else
+ m_VerticalBar->SetEnabled(true);
+ } else
+ m_VerticalBar->SetVisible(false);
+
+ if (IsHorizontalVisible()) {
+ // Make sure the bottom bar is visible.
+ m_HorizontalBar->SetVisible(true);
+
+ // Set the bottom bar's position.
+ m_HorizontalBar->SetPosition(CPt(0, mySize.y - m_HorizontalBar->GetMinimumSize().y));
+ // the height of the horizontal bar is subtracted from the visible area.
+ m_VisibleSize.y -= m_HorizontalBar->GetMinimumSize().y;
+
+ if (!IsHorizontalScrolling())
+ m_HorizontalBar->SetEnabled(false);
+ else
+ m_HorizontalBar->SetEnabled(true);
+ } else
+ m_HorizontalBar->SetVisible(false);
+
+ SetVisibleSize(m_VisibleSize);
+ SetVisiblePosition(GetVisiblePosition());
+
+ if (IsVerticalVisible())
+ m_VerticalBar->SetSize(CPt(m_VerticalBar->GetMinimumSize().x, m_VisibleSize.y));
+ if (IsHorizontalVisible())
+ m_HorizontalBar->SetSize(CPt(m_VisibleSize.x, m_HorizontalBar->GetMinimumSize().y));
+
+ m_ResizingChildren = false;
+}
+
+//=============================================================================
+/**
+ * Set the size of the inner control that is visible.
+ * @param inSize the size of the inner control that is to be visible.
+ */
+void CScroller::SetVisibleSize(CPt inSize)
+{
+ m_VisibleSize = inSize;
+
+ // If we have a child then set all of it's properties up.
+ CControl *theChild = GetControl();
+ if (theChild) {
+ CPt theChildSize = m_VisibleSize;
+ CPt theMinSize = theChild->GetMinimumSize();
+
+ if (theMinSize.x < theChildSize.x)
+ theMinSize.x = theChildSize.x;
+ if (theMinSize.y < theChildSize.y)
+ theMinSize.y = theChildSize.y;
+
+ // Set the sizes of the child to be either the size of the control or it's minimum size
+ if (theMinSize.x > theChildSize.x)
+ theChildSize.x = theMinSize.x;
+ if (theMinSize.y > theChildSize.y)
+ theChildSize.y = theMinSize.y;
+
+ theChild->SetSize(theChildSize);
+ }
+}
+
+//=============================================================================
+/**
+ * Check to see is the vertical bar should be visible or not.
+ * @return true if the vertical bar should be visible.
+ */
+bool CScroller::IsVerticalVisible()
+{
+ if (m_VerticalScrollMode != NEVER) {
+ if (m_VerticalScrollMode == ALWAYS || IsVerticalScrolling()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Check to see if the horizontal bar should be visible or not.
+ * @return true if the horizontal bar should be visible.
+ */
+bool CScroller::IsHorizontalVisible()
+{
+ if (m_HorizontalScrollMode != NEVER) {
+ if (m_HorizontalScrollMode == ALWAYS || IsHorizontalScrolling()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Checks to see if the client view should be scrolled vertically or not.
+ * This does not mean that the scroll bar is visible or not, just that the client
+ * view can be scrolled vertically.
+ * @return true if the client min height larger than this height.
+ */
+bool CScroller::IsVerticalScrolling()
+{
+ CControl *theChild = GetControl();
+ if (theChild != nullptr) {
+ CPt mySize = GetSize();
+ if (m_HorizontalScrollMode == ALWAYS)
+ mySize.y -= m_HorizontalBar->GetSize().y;
+
+ CPt theChildSize = theChild->GetMinimumSize();
+
+ if (theChildSize.y > mySize.y)
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Checks to see if the client view should be scrolled horizontally or not.
+ * This does not mean that the scroll bar is visible or not, just that the
+ * client view can be scrolled horizontally.
+ * @return true if the client min width is larget than this width.
+ */
+bool CScroller::IsHorizontalScrolling()
+{
+ CControl *theChild = GetControl();
+ if (theChild != nullptr) {
+ CPt mySize = GetSize();
+ if (m_VerticalScrollMode == ALWAYS)
+ mySize.x -= m_VerticalBar->GetSize().x;
+
+ CPt theChildSize = theChild->GetMinimumSize();
+
+ if (theChildSize.x > mySize.x)
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Get the child control that this is wrapping.
+ * @return the child control, or nullptr if there is not one.
+ */
+CControl *CScroller::GetControl()
+{
+ if (GetChildCount() > 2)
+ return GetChildren().GetCurrent()->GetControl();
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Get the size of this control that the Client is actually visible in.
+ * This is not the size of the client, but the size of the area that the client
+ * is being drawn to.
+ * @return the visible area of the child control.
+ */
+CPt CScroller::GetVisibleSize()
+{
+ return m_VisibleSize;
+}
+
+//=============================================================================
+/**
+ * Get the size of the actual child control.
+ * This is the total size of the child, and is always at least the size of
+ * the visible size.
+ * @return the size of the child control.
+ */
+CPt CScroller::GetContaineeSize()
+{
+ UICPROFILE(GetContaineeSize);
+
+ CControl *theControl = GetControl();
+ CPt theSize;
+ if (theControl != nullptr)
+ theSize = theControl->GetSize();
+ else
+ theSize = GetSize();
+
+ return theSize;
+}
+
+//=============================================================================
+/**
+ * Get the offset of the child control inside of this control.
+ * @return the offset location of the child.
+ */
+CPt CScroller::GetVisiblePosition()
+{
+ return m_VisiblePosition;
+}
+
+//=============================================================================
+/**
+ * Set the visible position of the child control.
+ * @param inVisiblePosition the visible position of the child control.
+ */
+void CScroller::SetVisiblePosition(CPt inVisiblePosition)
+{
+ if (inVisiblePosition.x < 0)
+ inVisiblePosition.x = 0;
+
+ if (inVisiblePosition.y < 0)
+ inVisiblePosition.y = 0;
+
+ CPt theMaxVisiblePosition = GetMaxVisiblePosition();
+
+ if (inVisiblePosition.x > theMaxVisiblePosition.x)
+ inVisiblePosition.x = theMaxVisiblePosition.x;
+
+ if (inVisiblePosition.y > theMaxVisiblePosition.y)
+ inVisiblePosition.y = theMaxVisiblePosition.y;
+
+ if (inVisiblePosition != m_VisiblePosition) {
+ CPt theScrolledAmount = m_VisiblePosition - inVisiblePosition;
+ m_ScrolledAmount += theScrolledAmount;
+
+ m_VisiblePosition = inVisiblePosition;
+
+ if (IsVerticalScrolling())
+ m_VerticalBar->RepositionThumb();
+ if (IsHorizontalScrolling())
+ m_HorizontalBar->RepositionThumb();
+
+ m_ScrollListeners.FireEvent(&CScrollListener::OnScroll, this, theScrolledAmount);
+ }
+
+ CControl *theChild = GetControl();
+ if (theChild != nullptr) {
+ theChild->SetPosition(-m_VisiblePosition);
+ }
+}
+
+//=============================================================================
+/**
+ * Get the maximum allowable offset of the child control.
+ * This is where the child would be offset to if the scroll bars were at their
+ * far ends.
+ * @return the maximum visible position allowed.
+ */
+CPt CScroller::GetMaxVisiblePosition()
+{
+ CPt theMaxPoint(0, 0);
+ CPt mySize = GetSize();
+ CControl *theChild = GetControl();
+
+ mySize.x -= IsVerticalVisible() ? m_VerticalBar->GetSize().x : 0;
+ mySize.y -= IsHorizontalVisible() ? m_HorizontalBar->GetSize().y : 0;
+
+ if (theChild != nullptr) {
+ CPt theChildSize = theChild->GetSize();
+
+ if (theChildSize.x > mySize.x)
+ theMaxPoint.x = theChildSize.x - mySize.x;
+
+ if (theChildSize.y > mySize.y)
+ theMaxPoint.y = theChildSize.y - mySize.y;
+ }
+
+ return theMaxPoint;
+}
+
+void CScroller::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ UICPROFILE(OnDraw);
+
+ CRct theDirtyRect;
+ bool isInvalidated = IsInvalidated();
+
+ if (isInvalidated || inIgnoreValidation || m_VerticalBar->IsChildInvalidated()
+ || m_HorizontalBar->IsChildInvalidated()) {
+ if (isInvalidated || inIgnoreValidation)
+ DrawBackground(inRenderer);
+ Draw(inRenderer);
+ theDirtyRect.Or(CRct(GetSize()));
+ }
+
+ CControl *theChild = GetControl();
+ if (theChild != nullptr) {
+ // Create an off screen buffer to draw the child to, this makes it so the child will draw
+ // it's
+ // entire self to the offscreen section, then we'll only draw the visible part to the actual
+ // renderer
+ CRct theClippingRect = m_VisibleSize;
+ if (m_AddtlClippingRect.size.x != 0 && m_AddtlClippingRect.size.y != 0)
+ theClippingRect.And(m_AddtlClippingRect);
+
+ inRenderer->PushClippingRect(theClippingRect);
+ inRenderer->PushTranslation(-m_VisiblePosition);
+ theChild->OnDraw(inRenderer, theDirtyRect, isInvalidated || inIgnoreValidation);
+ inRenderer->PopTranslation();
+ inRenderer->PopClippingRect();
+
+ m_ScrolledAmount = CPt(0, 0);
+ }
+
+ Invalidate(false);
+
+ CRct theBoundingBox = inRenderer->GetClippingRect();
+ theDirtyRect.And(theBoundingBox);
+ theDirtyRect.Offset(inRenderer->GetTranslation());
+ inDirtyRect.Or(theDirtyRect);
+}
+
+//=============================================================================
+/**
+ * Overrides CControl::Draw to handle custom child buffering.
+ * This just allows the child to be drawn offset without actually letting it
+ * know that it's offset.
+ * @param inRenderer the renderer to draw to.
+ */
+void CScroller::Draw(CRenderer *inRenderer)
+{
+ UICPROFILE(Draw);
+
+ CRct theDirtyRct;
+ // Only draw the side bar if it's visible
+ if (m_VerticalBar->IsVisible()) {
+ inRenderer->PushTranslation(m_VerticalBar->GetPosition());
+ m_VerticalBar->OnDraw(inRenderer, theDirtyRct, true);
+ inRenderer->PopTranslation();
+ }
+
+ // only draw the bottom bar if it's visible
+ if (m_HorizontalBar->IsVisible()) {
+ inRenderer->PushTranslation(m_HorizontalBar->GetPosition());
+ m_HorizontalBar->OnDraw(inRenderer, theDirtyRct, true);
+ inRenderer->PopTranslation();
+ }
+}
+
+//=============================================================================
+/**
+ * Gets the horizontal scroll bar of this scroller.
+ * @return the horizontal scroll bar of this scroller.
+ */
+CScrollerBar *CScroller::GetHorizontalBar()
+{
+ return m_HorizontalBar;
+}
+
+//=============================================================================
+/**
+ * Gets the vertical scroll bar of this scroller.
+ * @return the vertical scroll bar of this scroller.
+ */
+CScrollerBar *CScroller::GetVerticalBar()
+{
+ return m_VerticalBar;
+}
+
+//=============================================================================
+/**
+ * Sets the vertical scroll mode of this scroller.
+ * This controls when the scroll bars will be visible and when thes will not be
+ * visible.
+ * @param inScrollMode the vertical scroll mode.
+ */
+void CScroller::SetVerticalScrollMode(EScrollMode inScrollMode)
+{
+ m_VerticalScrollMode = inScrollMode;
+}
+
+//=============================================================================
+/**
+ * Gets the vertical scroll mode of this scroller.
+ * This controls when the scroll bars will be visible and when they will not be
+ * visible.
+ * @return the vertical scroll mode.
+ */
+CScroller::EScrollMode CScroller::GetVerticalScrollMode()
+{
+ return m_VerticalScrollMode;
+}
+
+//=============================================================================
+/**
+ * Sets the horizontal scroll mode of this scroller.
+ * This controls when the scroll bars will be visible and when they will not be
+ * visible.
+ * @param inScrollMode the vertical scroll mode.
+ */
+void CScroller::SetHorizontalScrollMode(EScrollMode inScrollMode)
+{
+ m_HorizontalScrollMode = inScrollMode;
+}
+
+//=============================================================================
+/**
+ * Gets the horizontal scroll mode of this scroller.
+ * This controls when the scroll bars will be visible and when they will not be
+ * visible.
+ * @return the horizontal scroll mode.
+ */
+CScroller::EScrollMode CScroller::GetHorizontalScrollMode()
+{
+ return m_HorizontalScrollMode;
+}
+
+void CScroller::OnChildSizeChanged(CControl *inControl)
+{
+ UICPROFILE(OnChildSizeChanged);
+
+ CControl *theChild = GetControl();
+
+ if (inControl == theChild) {
+ CPt theChildSize = theChild->GetSize();
+ CPt theMinSize = GetVisibleSize();
+ if (theChildSize.x < theMinSize.x) {
+ theChild->SetMinimumSize(CPt(theMinSize.x, theChild->GetMinimumSize().y));
+ theChildSize.x = theMinSize.x;
+ }
+ if (theChildSize.y < theMinSize.y) {
+ theChild->SetMinimumSize(CPt(theChild->GetMinimumSize().x, theMinSize.y));
+ theChildSize.y = theMinSize.y;
+ }
+
+ if (theChildSize != theChild->GetSize())
+ theChild->SetSize(theChildSize);
+ else if (!m_ResizingChildren) {
+ RecalcLayout();
+ Invalidate();
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Add a listener to get notified when this control scrolls.
+ * @param inListener the listener to be notified when this scrolls.
+ */
+void CScroller::AddScrollListener(CScrollListener *inListener)
+{
+ m_ScrollListeners.AddListener(inListener);
+}
+
+//=============================================================================
+/**
+ * Remove a listener from the list of listeners to be notified when this scrolls.
+ * @param inListener the listener to be removed from the list of listeners.
+ */
+void CScroller::RemoveScrollListener(CScrollListener *inListener)
+{
+ m_ScrollListeners.RemoveListener(inListener);
+}
+
+void CScroller::SetAdditionalClippingRect(CRct inAddtlClippingRect)
+{
+ m_AddtlClippingRect = inAddtlClippingRect;
+}
+
+bool CScroller::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CPt theVertPoint = inPoint - m_VerticalBar->GetPosition();
+ if (!m_VerticalBar->HitTest(inPoint) && !m_HorizontalBar->HitTest(inPoint)) {
+ m_IsMouseDown = true;
+ }
+
+ return CControl::OnMouseDown(inPoint, inFlags);
+}
+
+void CScroller::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_IsMouseDown) {
+ m_CurrentTickTock = std::shared_ptr<UICDM::ISignalConnection>();
+
+ CPt theOffset;
+ if ((inPoint.x < 0))
+ theOffset.x = inPoint.x;
+ else if (inPoint.x > m_VisibleSize.x)
+ theOffset.x = inPoint.x - m_VisibleSize.x;
+
+ // if ( ( inPoint.y < 0 && inPoint.y < m_PrevMousePoint.y ) || ( inPoint.y >
+ //m_VisibleSize.y && inPoint.y > m_PrevMousePoint.y ) )
+ // theOffset.y = inPoint.y - m_PrevMousePoint.y;
+
+ SetVisiblePosition(m_VisiblePosition + theOffset);
+
+ if (theOffset.x != 0) {
+ m_ScrollingDir = theOffset;
+ m_MousePos = inPoint;
+ m_MouseFlags = inFlags;
+ m_CurrentTickTock = ITickTock::GetInstance().AddTimer(
+ m_DelayTime, true, std::bind(&CScroller::OnTimer, this),
+ "CScroller::OnMouseMove::" + GetName());
+ // OnTimer( );
+ }
+ }
+
+ CControl::OnMouseMove(inPoint, inFlags);
+}
+
+void CScroller::OnTimer()
+{
+ CPt theOffset;
+ const auto theBeginCurrentTime = QDateTime::currentMSecsSinceEpoch();
+ if (m_ScrollingDir.x > 0)
+ theOffset.x = m_OffsetAmmount;
+ else if (m_ScrollingDir.x < 0)
+ theOffset.x = -m_OffsetAmmount;
+
+ SetVisiblePosition(m_VisiblePosition + theOffset);
+ CControl::OnMouseMove(m_MousePos, m_MouseFlags);
+ const auto theEndCurrentTime = QDateTime::currentMSecsSinceEpoch();
+ AdjustDelayTimeAccordingToOnTimerTime(theEndCurrentTime - theBeginCurrentTime);
+}
+
+//=============================================================================
+/**
+ * This function will adjust the delay time of the auto scroller depending on how long the timer
+ * took
+ * the numbers in here are pretty arbitrary and this should be redone at some point.
+ * @param inTime the amount of time that the timer took
+ */
+void CScroller::AdjustDelayTimeAccordingToOnTimerTime(unsigned long inTime)
+{
+ for (long theIndex = 2; theIndex < 6; theIndex++) {
+ if ((inTime)*theIndex > m_DelayTime) {
+ m_DelayTime = static_cast<unsigned long>(m_DelayTime * 10 / theIndex);
+ m_OffsetAmmount = static_cast<unsigned long>(m_OffsetAmmount * 10 / theIndex);
+ m_CurrentTickTock = ITickTock::GetInstance().AddTimer(
+ m_DelayTime, true, std::bind(&CScroller::OnTimer, this),
+ "CScroller::AdjustDelayTimeAccordingToOnTimerTime");
+ break;
+ }
+ }
+}
+
+void CScroller::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_IsMouseDown = false;
+
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ m_CurrentTickTock = std::shared_ptr<UICDM::ISignalConnection>();
+}
+
+void CScroller::OnLoseFocus()
+{
+ m_IsMouseDown = false;
+ m_CurrentTickTock = std::shared_ptr<UICDM::ISignalConnection>();
+ CControl::OnLoseFocus();
+}
+
+//=============================================================================
+/**
+ * Handles mouse wheel messages to scroll the view.
+ */
+bool CScroller::OnMouseWheel(CPt inPoint, long inScrollAmount, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseWheel(inPoint, inScrollAmount, inFlags)) {
+ if (inScrollAmount < 0)
+ SetVisiblePosition(CPt(GetVisiblePosition().x, GetVisiblePosition().y + 60));
+ else
+ SetVisiblePosition(CPt(GetVisiblePosition().x, GetVisiblePosition().y - 60));
+ }
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles messages from children to scroll the view to appropriate loc.
+ */
+void CScroller::EnsureVisible(CRct inRect)
+{
+ CControl *theChild = GetControl();
+ inRect.position -= theChild->GetPosition();
+
+ CPt theVisSize = GetVisibleSize();
+ CPt theVisPos = GetVisiblePosition();
+
+ // Check to see if the top is off the top.
+ if (inRect.position.y < theVisPos.y)
+ theVisPos.y = inRect.position.y;
+ // Check to see if the bottom is off the bottom.
+ else if (inRect.position.y + inRect.size.y > theVisPos.y + theVisSize.y)
+ theVisPos.y = inRect.position.y + inRect.size.y - theVisSize.y;
+
+ SetVisiblePosition(theVisPos);
+}
diff --git a/src/Authoring/Studio/Controls/Scroller.h b/src/Authoring/Studio/Controls/Scroller.h
new file mode 100644
index 00000000..9cb39962
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Scroller.h
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SCROLLER_H
+#define INCLUDED_SCROLLER_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "ScrollerBar.h"
+#include "FlowLayout.h"
+#include "GenericFunctor.h"
+#include "Multicaster.h"
+#include "ITickTock.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CButtonControl;
+
+GENERIC_FUNCTOR_2(CScrollListener, OnScroll, CScroller *, CPt);
+
+class IScroller
+{
+protected:
+ virtual ~IScroller() {}
+public:
+ virtual void SetVisiblePosition(CPt inVisiblePosition) = 0;
+ virtual CPt GetVisiblePosition() = 0;
+};
+
+//=============================================================================
+/**
+ * Class for creating a scroller.
+ */
+class CScroller : public CControl, public IScroller
+{
+public:
+ enum EScrollMode {
+ NEVER,
+ AS_NEEDED,
+ ALWAYS,
+ };
+
+ CScroller(bool inCreateImmediately = true);
+ virtual ~CScroller();
+
+ void Initialize();
+
+ void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect,
+ bool inIgnoreValidation /* = false */) override;
+ virtual void DrawBackground(CRenderer *) {}
+ void Draw(CRenderer *inRenderer) override;
+
+ void SetSize(CPt inSize) override;
+ void SetLayout(CPt inSize, CPt inPosition) override;
+ CPt GetMinimumSize() override;
+ CPt GetMaximumSize() override;
+
+ void AddChild(CControl *inControl, CControl *inInsertBefore = nullptr) override;
+
+ CPt GetVisibleSize();
+ CPt GetContaineeSize();
+
+ CPt GetVisiblePosition() override;
+ void SetVisiblePosition(CPt inVisiblePosition) override;
+ CPt GetMaxVisiblePosition();
+
+ CScrollerBar *GetHorizontalBar();
+ CScrollerBar *GetVerticalBar();
+
+ void SetVerticalScrollMode(EScrollMode inScrollMode);
+ EScrollMode GetVerticalScrollMode();
+
+ void SetHorizontalScrollMode(EScrollMode inScrollMode);
+ EScrollMode GetHorizontalScrollMode();
+
+ virtual void RecalcLayout();
+ void OnChildSizeChanged(CControl *inChild) override;
+
+ void AddScrollListener(CScrollListener *inScrollListener);
+ void RemoveScrollListener(CScrollListener *inScrollListener);
+
+ void SetAdditionalClippingRect(CRct inClippingRect);
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseWheel(CPt inPoint, long inScrollAmount, Qt::KeyboardModifiers inFlags) override;
+ void EnsureVisible(CRct inRect) override;
+
+ virtual void OnTimer();
+ void OnLoseFocus() override;
+ void AdjustDelayTimeAccordingToOnTimerTime(unsigned long inTime);
+
+protected:
+ void OnSizeChanged(CPt inSize) override;
+ virtual bool IsVerticalVisible();
+ virtual bool IsHorizontalVisible();
+ virtual bool IsVerticalScrolling();
+ virtual bool IsHorizontalScrolling();
+
+ virtual CScrollerBar *CreateVerticalBar();
+ virtual CScrollerBar *CreateHorizontalBar();
+
+ virtual void SetVisibleSize(CPt inSize);
+
+ virtual CControl *GetControl();
+
+ CScrollerBar *m_VerticalBar;
+ CScrollerBar *m_HorizontalBar;
+
+ CPt m_VisibleSize;
+ CPt m_VisiblePosition;
+ CPt m_MaxVisiblePosition;
+
+ CPt m_ScrolledAmount;
+ CPt m_ChildOffset;
+
+ CRct m_AddtlClippingRect;
+ CPt m_ScrollingDir;
+ CPt m_MousePos;
+ Qt::KeyboardModifiers m_MouseFlags;
+
+ EScrollMode m_VerticalScrollMode;
+ EScrollMode m_HorizontalScrollMode;
+
+ bool m_ResizingChildren;
+
+ CMulticaster<CScrollListener *> m_ScrollListeners;
+
+ bool m_IsMouseDown;
+ CPt m_PrevMousePoint;
+
+ long m_OffsetAmmount;
+ unsigned long m_DelayTime;
+ std::shared_ptr<UICDM::ISignalConnection> m_CurrentTickTock;
+};
+#endif // INCLUDED_SCROLLER_H
diff --git a/src/Authoring/Studio/Controls/ScrollerBackground.cpp b/src/Authoring/Studio/Controls/ScrollerBackground.cpp
new file mode 100644
index 00000000..fcfa5e7f
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerBackground.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ScrollerBackground.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "Scroller.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CScrollerBackground::CScrollerBackground(CColor inColor)
+ : CBlankControl(inColor)
+{
+ m_Orientation = CScrollerBar::HORIZONTAL;
+ m_ScrollerBar = nullptr;
+ m_Control = this;
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CScrollerBackground::~CScrollerBackground()
+{
+}
+
+//=============================================================================
+/**
+ * Overrides CBlankControl::Draw to handle scroller-specific drawing.
+ */
+void CScrollerBackground::Draw(CRenderer *inRenderer)
+{
+ CBlankControl::Draw(inRenderer);
+
+ if (CScrollerBar::HORIZONTAL == m_Orientation) {
+ // Horizontal lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollBGOutlineColor(), 1);
+ inRenderer->MoveTo(0, 0);
+ inRenderer->LineTo(GetSize().x, 0);
+ inRenderer->PopPen();
+ inRenderer->PushPen(CStudioPreferences::GetScrollBGOutlineColor(), 1);
+ inRenderer->MoveTo(0, GetSize().y - 1);
+ inRenderer->LineTo(GetSize().x, GetSize().y - 1);
+ inRenderer->PopPen();
+ } else {
+ // Vertical lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollBGOutlineColor(), 1);
+ inRenderer->MoveTo(0, 0);
+ inRenderer->LineTo(0, GetSize().y - 1);
+ inRenderer->MoveTo(GetSize().x - 1, 0);
+ inRenderer->LineTo(GetSize().x - 1, GetSize().y - 1);
+ inRenderer->PopPen();
+ }
+}
+
+//=============================================================================
+/**
+ * Handles mouse down events. Scrolls the thumb to the clicked position.
+ * @param inPoint location of the mouse click
+ * @param inFlags mouse event flags
+ */
+bool CScrollerBackground::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theHandledFlag = CBlankControl::OnMouseDown(inPoint, inFlags);
+
+ if (!theHandledFlag && m_ScrollerBar) {
+ CPt theThumbPosition = m_ScrollerBar->GetThumb()->GetPosition();
+ bool theScrollForwardFlag = false;
+
+ if (CScrollerBar::HORIZONTAL == m_Orientation) {
+ if (inPoint.x > theThumbPosition.x)
+ theScrollForwardFlag = true;
+ } else {
+ if (inPoint.y > theThumbPosition.y)
+ theScrollForwardFlag = true;
+ }
+
+ m_MousePos = inPoint;
+
+ if (theScrollForwardFlag)
+ OnScrollForward(this);
+ else
+ OnScrollBackward(this);
+
+ theHandledFlag = true;
+ }
+
+ return theHandledFlag;
+}
+
+//=============================================================================
+/**
+ * Handles mouse up events
+ */
+void CScrollerBackground::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CBlankControl::OnMouseUp(inPoint, inFlags);
+
+ OnCancelScrolling();
+}
+
+//=============================================================================
+/**
+ * Returns the amount to scroll
+ */
+long CScrollerBackground::DetermineScrollAmount()
+{
+ long theDistance = 0;
+ CPt theThumbPosition = m_ScrollerBar->GetThumb()->GetPosition();
+ CPt theThumbSize = m_ScrollerBar->GetThumb()->GetSize();
+
+ if (CScrollerBar::VERTICAL == m_Orientation) {
+ if ((m_ScrollingForward && (m_MousePos.y > theThumbPosition.y + theThumbSize.y))
+ || (m_ScrollingBackward && (m_MousePos.y < theThumbPosition.y))) {
+ theDistance = theThumbSize.y;
+ }
+ } else // HORIZONTAL
+ {
+ if ((m_ScrollingForward && (m_MousePos.x > theThumbPosition.x + theThumbSize.x))
+ || (m_ScrollingBackward && (m_MousePos.x < theThumbPosition.x))) {
+ theDistance = theThumbSize.x;
+ }
+ }
+
+ return theDistance;
+}
+
+//=============================================================================
+/**
+ * Overwrite the MouseMove function. We want to take care of the scenario where
+ * it is in a scrolling state, then determine whether to scroll forwards or backwards
+ * depending on the mouse position relative to the scroller thumb.
+ */
+void CScrollerBackground::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Call the default mouseMove function
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ // We only want to do stuff if it is currently scrolling
+ if (m_ScrollingForward || m_ScrollingBackward) {
+ // save the mouse position
+ m_MousePos = inPoint;
+
+ CPt theThumbPosition = m_ScrollerBar->GetThumb()->GetPosition();
+ CPt theThumbSize = m_ScrollerBar->GetThumb()->GetSize();
+
+ if (CScrollerBar::VERTICAL == m_Orientation) {
+ if (m_MousePos.y > (theThumbPosition.y + theThumbSize.y)) {
+ m_ScrollingForward = true;
+ m_ScrollingBackward = false;
+ } else {
+ m_ScrollingForward = false;
+ m_ScrollingBackward = true;
+ }
+ } else // HORIZONTAL
+ {
+ if (m_MousePos.x > (theThumbPosition.x + theThumbSize.x)) {
+ m_ScrollingForward = true;
+ m_ScrollingBackward = false;
+ } else {
+ m_ScrollingForward = false;
+ m_ScrollingBackward = true;
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Controls/ScrollerBackground.h b/src/Authoring/Studio/Controls/ScrollerBackground.h
new file mode 100644
index 00000000..885d1c88
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerBackground.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SCROLLER_BACKGROUND_H
+#define INCLUDED_SCROLLER_BACKGROUND_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+#include "StudioPreferences.h"
+#include "ScrollerBar.h"
+#include "ScrollController.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//=============================================================================
+/**
+ * Extends the blank control to draw items specific to the scroller control.
+ */
+class CScrollerBackground : public CBlankControl, public CScrollController
+{
+protected:
+ CPt m_MousePos;
+
+public:
+ CScrollerBackground(CColor inColor = CStudioPreferences::GetScrollBGColor());
+ virtual ~CScrollerBackground();
+ virtual void Draw(CRenderer *inRenderer);
+ virtual bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags);
+
+protected:
+ virtual long DetermineScrollAmount();
+};
+
+#endif // INCLUDED_SCROLLER_BACKGROUND_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/ScrollerBar.cpp b/src/Authoring/Studio/Controls/ScrollerBar.cpp
new file mode 100644
index 00000000..b054fb6e
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerBar.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ScrollerBar.h"
+#include "Scroller.h"
+#include "Renderer.h"
+#include "ButtonControl.h"
+#include "CoreUtils.h"
+#include "ScrollerBackground.h"
+#include "ScrollerThumb.h"
+#include "StudioPreferences.h"
+#include "Renderer.h"
+#include "SystemPreferences.h"
+#include "StudioUtils.h"
+#include "ScrollerButtonControl.h"
+#include "MasterP.h"
+
+//==============================================================================
+// Static variables
+//==============================================================================
+const long CScrollerBar::DEFAULT_WIDTH = 16;
+const CColor CScrollerBar::DEFAULT_COLOR = CColor(196, 194, 189);
+const CColor CScrollerBar::SCROLLER_TOP = CColor(165, 162, 161);
+
+IMPLEMENT_OBJECT_COUNTER(CScrollerBar)
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inScroller the scroller on which this is operating.
+ * @param inCreateImmediately false for lazy construction, only for subclasses.
+ */
+CScrollerBar::CScrollerBar(CScroller *inScroller, bool inCreateImmediately /* = true */)
+{
+ ADDTO_OBJECT_COUNTER(CScrollerBar)
+
+ m_Scroller = inScroller;
+ SetFlowDirection(FLOW_HORIZONTAL);
+
+ if (inCreateImmediately)
+ Initialize();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CScrollerBar::~CScrollerBar()
+{
+ REMOVEFROM_OBJECT_COUNTER(CScrollerBar)
+}
+
+//=============================================================================
+/**
+ * Initialize this scroller bar.
+ * This is used for lazy construction of this object by subclasses. If a subclass
+ * specifies lazy construction in the constructor of this class then it must call
+ * this to put it into a valid state.
+ */
+void CScrollerBar::Initialize()
+{
+ m_ScrollerThumb = CreateThumb();
+
+ // First button
+ m_ButtonBackward = new CScrollerButtonControl(this, CScrollerButtonControl::BACKWARD);
+
+ // Background
+ m_Background = new CScrollerBackground(CStudioPreferences::GetScrollBGColor());
+ m_Background->SetScrollerBar(this);
+ m_Background->AddChild(m_ScrollerThumb);
+
+ m_ScrollerThumb->SetPosition(CPt(0, 0));
+
+ // Second button (either right or bottom)
+ m_ButtonForward = new CScrollerButtonControl(this, CScrollerButtonControl::FORWARD);
+
+ // Determine the placement of the scroll bar arrows based on the system preference
+ if (CSystemPreferences::AreScrollArrowsAdjacent()) {
+ // Scroll arrows go together at one end of the bar
+ AddChild(m_Background);
+ AddChild(m_ButtonBackward);
+ AddChild(m_ButtonForward);
+ } else {
+ // Scroll arrows go on each end of the scroll bar
+ AddChild(m_ButtonBackward);
+ AddChild(m_Background);
+ AddChild(m_ButtonForward);
+ }
+}
+
+//=============================================================================
+/**
+ * Virtual class to create the thumb class.
+ * This is used to allow sub-classes to specialize the scroller thumb that they
+ * are using.
+ * @return the thumb that was created.
+ */
+CControl *CScrollerBar::CreateThumb()
+{
+ return new CScrollerThumb(this);
+}
+
+//=============================================================================
+/**
+ * Set the orientation of this scroller bar.
+ * This determines which way this is scrolling.
+ */
+void CScrollerBar::SetOrientation(EOrientation inOrientation)
+{
+ m_Orientation = inOrientation;
+ m_Background->SetOrientation(inOrientation);
+
+ if (m_Orientation == VERTICAL) {
+ // Vertical buttons
+ SetFlowDirection(FLOW_VERTICAL);
+ m_ButtonBackward->SetUpImage("scrollbar-arrows-up-normal.png");
+ m_ButtonBackward->SetDownImage("scrollbar-arrows-up-depressed.png");
+ m_ButtonBackward->SetDisabledImage("scrollbar-arrows-up-disabled.png");
+ m_ButtonForward->SetUpImage("scrollbar-arrows-down-normal.png");
+ m_ButtonForward->SetDownImage("scrollbar-arrows-down-depressed.png");
+ m_ButtonForward->SetDisabledImage("scrollbar-arrows-down-disabled.png");
+ } else {
+ // Horizontal buttons
+ SetFlowDirection(FLOW_HORIZONTAL);
+ m_ButtonBackward->SetUpImage("scrollbar-arrows-left-normal.png");
+ m_ButtonBackward->SetDownImage("scrollbar-arrows-left-depressed.png");
+ m_ButtonBackward->SetDisabledImage("scrollbar-arrows-left-disabled.png");
+ m_ButtonForward->SetUpImage("scrollbar-arrows-right-normal.png");
+ m_ButtonForward->SetDownImage("scrollbar-arrows-right-depressed.png");
+ m_ButtonForward->SetDisabledImage("scrollbar-arrows-right-disabled.png");
+ }
+}
+
+//=============================================================================
+/**
+ * Get the orientation of this scroller bar.
+ */
+CScrollerBar::EOrientation CScrollerBar::GetOrientation()
+{
+ return m_Orientation;
+}
+
+//=============================================================================
+/**
+ * Get the minimum size of this scroller bar.
+ * The minumum size is the width or height of both of it's buttons (depending
+ * on orientation.
+ * @return the minimum allowable size of this bar.
+ */
+CPt CScrollerBar::GetMinimumSize()
+{
+ if (GetOrientation() == VERTICAL) {
+ return CPt(DEFAULT_WIDTH, DEFAULT_WIDTH * 2);
+ } else {
+ return CPt(DEFAULT_WIDTH * 2, DEFAULT_WIDTH);
+ }
+}
+
+//=============================================================================
+/**
+ * Set the size of this scroller bar.
+ */
+void CScrollerBar::SetSize(CPt inSize)
+{
+ CFlowLayout::SetSize(inSize);
+
+ RepositionThumb();
+}
+
+//=============================================================================
+/**
+ * Recalculate the size and position of the thumb control.
+ */
+void CScrollerBar::RepositionThumb()
+{
+ long theBarLen;
+
+ if (GetOrientation() == VERTICAL) {
+ float thePercentage =
+ (float)m_Scroller->GetVisibleSize().y / (float)m_Scroller->GetContaineeSize().y;
+ theBarLen = (long)(thePercentage * ((float)m_Background->GetSize().y));
+
+ if (theBarLen < CScrollerThumb::MIN_LENGTH)
+ theBarLen = CScrollerThumb::MIN_LENGTH;
+
+ CPt theSize(GetMinimumSize().x, theBarLen);
+
+ m_ScrollerThumb->SetSize(theSize);
+
+ float theVisPosY = (float)(m_Scroller->GetVisiblePosition().y);
+ float theMaxPosY = (float)(m_Scroller->GetMaxVisiblePosition().y);
+ long thePosY = 0;
+ if (theMaxPosY != 0)
+ thePosY = ::dtol((theVisPosY / theMaxPosY)
+ * (m_Background->GetSize().y - m_ScrollerThumb->GetSize().y));
+
+ m_ScrollerThumb->SetPosition(CPt(0, thePosY));
+ } else {
+ float thePercentage =
+ (float)m_Scroller->GetVisibleSize().x / (float)m_Scroller->GetContaineeSize().x;
+ theBarLen = ::dtol(thePercentage * ((float)m_Background->GetSize().x));
+ if (theBarLen < CScrollerThumb::MIN_LENGTH)
+ theBarLen = CScrollerThumb::MIN_LENGTH;
+
+ CPt theSize(theBarLen, GetMinimumSize().y);
+
+ m_ScrollerThumb->SetSize(theSize);
+
+ float theVisPosX = (float)(m_Scroller->GetVisiblePosition().x);
+ float theMaxPosX = (float)(m_Scroller->GetMaxVisiblePosition().x);
+ long thePosX = 0;
+ if (theMaxPosX != 0)
+ thePosX = ::dtol((theVisPosX / theMaxPosX)
+ * (m_Background->GetSize().x - m_ScrollerThumb->GetSize().x));
+
+ m_ScrollerThumb->SetPosition(CPt(thePosX, 0));
+ }
+
+ Invalidate();
+}
+
+IScroller *CScrollerBar::GetScroller()
+{
+ return m_Scroller;
+};
+
+//=============================================================================
+/**
+ * Called by the ScrollerThumb to reposition itself.
+ * This is used to reposition the thumb, the positions here are used to derive
+ * the position of the visible window, so that this provides absolute positioning
+ * of the thumb.
+ * @param inPosition the new position of the thumb.
+ */
+void CScrollerBar::SetBarPosition(long inPosition)
+{
+ long theAvailableSpace;
+ if (GetOrientation() == HORIZONTAL) {
+ theAvailableSpace = m_Background->GetSize().x - m_ScrollerThumb->GetSize().x;
+ } else {
+ theAvailableSpace = m_Background->GetSize().y - m_ScrollerThumb->GetSize().y;
+ }
+
+ if (inPosition > theAvailableSpace)
+ inPosition = theAvailableSpace;
+ if (inPosition < 0)
+ inPosition = 0;
+
+ CPt theVisPos = m_Scroller->GetVisiblePosition();
+ if (GetOrientation() == HORIZONTAL) {
+ m_ScrollerThumb->SetPosition(CPt(inPosition, 0));
+ double theMaxPosX = (double)(m_Scroller->GetMaxVisiblePosition().x);
+ double theScrollDiff = (double)(m_Background->GetSize().x - m_ScrollerThumb->GetSize().x);
+ theVisPos.x = ::dtol((double)inPosition / theScrollDiff * theMaxPosX);
+ } else {
+ m_ScrollerThumb->SetPosition(CPt(0, inPosition));
+ double theMaxPosY = (double)(m_Scroller->GetMaxVisiblePosition().y);
+ double theScrollDiff = (double)(m_Background->GetSize().y - m_ScrollerThumb->GetSize().y);
+ theVisPos.y = ::dtol((double)inPosition / theScrollDiff * theMaxPosY);
+ }
+ m_Scroller->SetVisiblePosition(theVisPos);
+
+ RepositionThumb();
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Get the current position of the thumb.
+ * @return the current position of the thumb.
+ */
+long CScrollerBar::GetBarPosition()
+{
+ if (GetOrientation() == HORIZONTAL)
+ return m_ScrollerThumb->GetPosition().x;
+ else
+ return m_ScrollerThumb->GetPosition().y;
+}
+
+//=============================================================================
+/**
+ * Get the ScrollerThumb.
+ * @return the scroller thumb.
+ */
+CControl *CScrollerBar::GetThumb()
+{
+ return m_ScrollerThumb;
+}
+
+//=============================================================================
+/**
+ * Get the background of the thumb.
+ * @return the background of the thumb.
+ */
+CControl *CScrollerBar::GetThumbBackground()
+{
+ return m_Background;
+}
diff --git a/src/Authoring/Studio/Controls/ScrollerBar.h b/src/Authoring/Studio/Controls/ScrollerBar.h
new file mode 100644
index 00000000..3a8527ef
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerBar.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SCROLLER_BAR_H
+#define INCLUDED_SCROLLER_BAR_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "Control.h"
+#include "FlowLayout.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+class CScroller;
+class CScrollerBar;
+class CScrollerBackground;
+class CRenderer;
+class CScrollerButtonControl;
+
+class IScroller;
+
+class IScrollerBar
+{
+protected:
+ virtual ~IScrollerBar() {}
+public:
+ enum EOrientation {
+ VERTICAL,
+ HORIZONTAL,
+ };
+ virtual EOrientation GetOrientation() = 0;
+ virtual void RepositionThumb() = 0;
+ virtual IScroller *GetScroller() = 0;
+ virtual void SetBarPosition(long inPosition) = 0;
+ virtual long GetBarPosition() = 0;
+ virtual CControl *GetThumb() = 0;
+};
+
+//=============================================================================
+/**
+ * Class for creating a scrollerbar (contains a CScrollerThumb).
+ */
+class CScrollerBar : public CFlowLayout, public IScrollerBar
+{
+public:
+ static const long DEFAULT_WIDTH;
+ static const ::CColor DEFAULT_COLOR;
+ static const ::CColor SCROLLER_TOP;
+
+ CScrollerBar(CScroller *inScroller, bool inCreateImmediately = true);
+ virtual ~CScrollerBar();
+
+ DEFINE_OBJECT_COUNTER(CScrollerBar)
+
+ void SetOrientation(EOrientation inOrientation);
+ EOrientation GetOrientation() override;
+
+ CPt GetMinimumSize() override;
+
+ void SetScrollerThumbPosition(long inPosition);
+ long GetScrollerThumbPosition();
+
+ void SetSize(CPt inSize) override;
+
+ void SetBarPosition(long inPosition) override;
+ long GetBarPosition() override;
+
+ CControl *GetThumb() override;
+ virtual CControl *GetThumbBackground();
+ void RepositionThumb() override;
+ IScroller *GetScroller() override;
+
+protected:
+ virtual CControl *CreateThumb();
+ void Initialize();
+ Q3DStudio::CAutoMemPtr<CScrollerButtonControl> m_ButtonBackward;
+ Q3DStudio::CAutoMemPtr<CScrollerButtonControl> m_ButtonForward;
+ Q3DStudio::CAutoMemPtr<CControl> m_ScrollerThumb;
+ Q3DStudio::CAutoMemPtr<CScrollerBackground> m_Background;
+
+ EOrientation m_Orientation;
+ CScroller *m_Scroller;
+};
+
+#endif // INCLUDED_SCROLLER_BAR_H
diff --git a/src/Authoring/Studio/Controls/ScrollerButtonControl.cpp b/src/Authoring/Studio/Controls/ScrollerButtonControl.cpp
new file mode 100644
index 00000000..a04b8ff8
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerButtonControl.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ScrollerButtonControl.h"
+#include "Scroller.h"
+
+//=============================================================================
+/**
+ * Constructor
+ *
+ * @param inToggleButton true if this is suppose to be a toggle button. Toggle
+ * buttons stay pressed when clicked on and have to be clicked again in order
+ * to release. Normal buttons return to the up state when the button is released.
+ */
+CScrollerButtonControl::CScrollerButtonControl(IScrollerBar *inScrollerBar, EDirection inDirection)
+ : m_Direction(inDirection)
+{
+ SetAbsoluteSize(CPt(CScrollerBar::DEFAULT_WIDTH, CScrollerBar::DEFAULT_WIDTH));
+ SetScrollerBar(inScrollerBar);
+ m_Control = this;
+
+ Initialize();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CScrollerButtonControl::~CScrollerButtonControl()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+void CScrollerButtonControl::Initialize() {
+ // Add the button down listeners
+ if (m_Direction == FORWARD) {
+ SigButtonDown.connect(std::bind(&CScrollerButtonControl::OnScrollForward,
+ static_cast<CScrollController *>(this), std::placeholders::_1));
+ } else {
+ SigButtonDown.connect(std::bind(&CScrollerButtonControl::OnScrollBackward,
+ static_cast<CScrollController *>(this), std::placeholders::_1));
+ }
+ SigClicked.connect(std::bind(&CScrollerButtonControl::OnCancelScrolling,
+ static_cast<CScrollController *>(this), std::placeholders::_1));
+}
+
+//=============================================================================
+/**
+ * Handles mouse up on the button.
+ */
+void CScrollerButtonControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CButtonControl::OnMouseUp(inPoint, inFlags);
+
+ // If the mouse is not over this button, the base class will not fire an up event.
+ // However, we want to fire this event anyway so that the timer stops (otherwise,
+ // the next time you move your mouse over the button, it will start scrolling again).
+ if (!IsMouseOver())
+ SigButtonUp(this);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/ScrollerButtonControl.h b/src/Authoring/Studio/Controls/ScrollerButtonControl.h
new file mode 100644
index 00000000..a7763d8b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerButtonControl.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_SCROLLER_BUTTON_CONTROL_H
+#define INCLUDED_SCROLLER_BUTTON_CONTROL_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "ButtonControl.h"
+#include "ScrollerBar.h"
+#include "ScrollController.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//=============================================================================
+/**
+ * Subclassed button control that controls the scroller
+ */
+class CScrollerButtonControl : public CButtonControl, public CScrollController
+{
+public:
+ enum EDirection {
+ FORWARD,
+ BACKWARD,
+ };
+
+protected:
+ EDirection m_Direction;
+
+public:
+ CScrollerButtonControl(IScrollerBar *inScrollerBar, EDirection inDirection);
+ virtual ~CScrollerButtonControl();
+
+ virtual void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+
+ EDirection GetDirection() { return m_Direction; };
+
+protected:
+ void Initialize();
+};
+
+#endif // INCLUDED_SCROLLER_BUTTON_CONTROL_H
diff --git a/src/Authoring/Studio/Controls/ScrollerThumb.cpp b/src/Authoring/Studio/Controls/ScrollerThumb.cpp
new file mode 100644
index 00000000..a7fedbca
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerThumb.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ScrollerThumb.h"
+#include "ScrollerBar.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+
+//==============================================================================
+// Static variables
+//==============================================================================
+const long CScrollerThumb::MIN_LENGTH = 32;
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inScrollerBar the scroller bar this is operating on.
+ */
+CScrollerThumb::CScrollerThumb(IScrollerBar *inScrollerBar)
+ : m_MouseDown(false)
+{
+ m_ScrollerBar = inScrollerBar;
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CScrollerThumb::~CScrollerThumb()
+{
+}
+
+//=============================================================================
+/**
+ * Draw this scroller bar.
+ * @param inRenderer the renderer this is to draw to.
+ */
+void CScrollerThumb::Draw(CRenderer *inRenderer)
+{
+ if (IsEnabled()) {
+ CPt theSize = GetSize();
+ CRct theRect(theSize);
+
+ // Draw the thumb
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetScrollThumbBGColor());
+ // Draw the highlight
+ inRenderer->Draw3dRect(CRct(theRect.position.x + 1, theRect.position.y + 1,
+ theRect.size.x - 1, theRect.size.y - 1),
+ CStudioPreferences::GetScrollThumbHighlightColor(),
+ CStudioPreferences::GetScrollThumbBGColor());
+ // Draw the black border
+ inRenderer->Draw3dRect(theRect, CStudioPreferences::GetScrollThumbShadowColor(),
+ CStudioPreferences::GetScrollThumbShadowColor());
+
+ // Draw the 3 lines in the middle of the control, depending on orientation
+ if (m_ScrollerBar->GetOrientation() == CScrollerBar::VERTICAL) {
+ // Draw the light lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollThumbGripHighlightColor());
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 3, theSize.y / 2 - 5));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 2, theSize.y / 2 - 5));
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 3, theSize.y / 2 - 2));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 2, theSize.y / 2 - 2));
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 3, theSize.y / 2 + 1));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 2, theSize.y / 2 + 1));
+ inRenderer->PopPen();
+ // Draw the dark lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollThumbGripShadowColor());
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 2, theSize.y / 2 - 4));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 3, theSize.y / 2 - 4));
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 2, theSize.y / 2 - 1));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 3, theSize.y / 2 - 1));
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 2, theSize.y / 2 + 2));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 3, theSize.y / 2 + 2));
+ inRenderer->PopPen();
+ } else {
+ // Draw the light lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollThumbGripHighlightColor());
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 4, theSize.y / 2 - 3));
+ inRenderer->LineTo(CPt(theSize.x / 2 - 4, theSize.y / 2 + 2));
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 1, theSize.y / 2 - 3));
+ inRenderer->LineTo(CPt(theSize.x / 2 - 1, theSize.y / 2 + 2));
+ inRenderer->MoveTo(CPt(theSize.x / 2 + 2, theSize.y / 2 - 3));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 2, theSize.y / 2 + 2));
+ inRenderer->PopPen();
+
+ // Draw the dark lines
+ inRenderer->PushPen(CStudioPreferences::GetScrollThumbGripShadowColor());
+ inRenderer->MoveTo(CPt(theSize.x / 2 - 3, theSize.y / 2 - 2));
+ inRenderer->LineTo(CPt(theSize.x / 2 - 3, theSize.y / 2 + 3));
+ inRenderer->MoveTo(CPt(theSize.x / 2, theSize.y / 2 - 2));
+ inRenderer->LineTo(CPt(theSize.x / 2, theSize.y / 2 + 3));
+ inRenderer->MoveTo(CPt(theSize.x / 2 + 3, theSize.y / 2 - 2));
+ inRenderer->LineTo(CPt(theSize.x / 2 + 3, theSize.y / 2 + 3));
+ inRenderer->PopPen();
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Listener for the OnMouseDown to allow dragging.
+ * Begins dragging of the control.
+ */
+bool CScrollerThumb::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_MouseDown = true;
+ m_MouseDownPoint = inPoint;
+
+ Invalidate();
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Ends dragging of the control.
+ */
+void CScrollerThumb::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_MouseDown = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Used for dragging the control.
+ */
+void CScrollerThumb::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ // Only care if the mouse is down.
+ if (m_MouseDown) {
+ long theBarPos = m_ScrollerBar->GetBarPosition();
+
+ // Adjust the position based on which way we are being dragged.
+ if (m_ScrollerBar->GetOrientation() == CScrollerBar::VERTICAL) {
+ theBarPos += inPoint.y - m_MouseDownPoint.y;
+ } else {
+ theBarPos += inPoint.x - m_MouseDownPoint.x;
+ }
+
+ // Update the position.
+ m_ScrollerBar->SetBarPosition(theBarPos);
+ }
+}
+
+//=============================================================================
+/**
+ * Get the minimum size that this scroller thumb is allowed to be.
+ * @return the minimum size that this scroller thumb is allowed to be.
+ */
+CPt CScrollerThumb::GetMinimumSize()
+{
+ if (m_ScrollerBar->GetOrientation() == CScrollerBar::HORIZONTAL)
+ return CPt(MIN_LENGTH, 0);
+ else
+ return CPt(0, MIN_LENGTH);
+}
diff --git a/src/Authoring/Studio/Controls/ScrollerThumb.h b/src/Authoring/Studio/Controls/ScrollerThumb.h
new file mode 100644
index 00000000..c699c910
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ScrollerThumb.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SCROLLER_THUMB_H
+#define INCLUDED_SCROLLER_THUMB_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "FlowLayout.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class IScrollerBar;
+class CRenderer;
+
+//=============================================================================
+/**
+ * Class for creating the thumb portion of a scroller.
+ */
+class CScrollerThumb : public CControl
+{
+public:
+ static const long MIN_LENGTH;
+
+ CScrollerThumb(IScrollerBar *inScroller);
+ virtual ~CScrollerThumb();
+
+ virtual void Draw(CRenderer *inRenderer);
+
+ virtual bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags);
+
+ virtual CPt GetMinimumSize();
+
+protected:
+ IScrollerBar *m_ScrollerBar;
+ bool m_MouseDown;
+ CPt m_MouseDownPoint;
+};
+
+#endif // INCLUDED_SCROLLER_THUMB_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/SplashControl.cpp b/src/Authoring/Studio/Controls/SplashControl.cpp
new file mode 100644
index 00000000..ea23f168
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SplashControl.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SplashControl.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+#include "StudioPreferences.h"
+#include "OffscreenRenderer.h"
+#include "StringLoader.h"
+#include "StudioDefs.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CSplashControl::CSplashControl()
+{
+ // Image
+ m_Image = CResourceCache::GetInstance()->GetBitmap("obsolete_placeholder.png");
+ SetAbsoluteSize(m_Image.rect().bottomRight());
+
+ // First line of the copyright statement
+ m_CopyrightLine1.Format(
+ ::LoadResourceString(IDS_SPLASH_COPYRIGHT1),
+ static_cast<const wchar_t *>(Q3DStudio::CString(STUDIO_COPYRIGHT_YEAR)));
+
+ // Second line of the copyright statement
+ m_CopyrightLine2 = ::LoadResourceString(IDS_SPLASH_COPYRIGHT2);
+
+ // Version text
+ m_VersionInfo.Format((::LoadResourceString(IDS_UIC_STUDIO_VERSION)),
+ static_cast<const wchar_t *>(CStudioPreferences::GetVersionString()));
+
+ // Calculate the number of pixels between each line of text
+ COffscreenRenderer theOffscreenRenderer(CRct(0, 0, 1, 1));
+ auto size = theOffscreenRenderer.GetTextSize(m_CopyrightLine1.toQString());
+ m_SpaceBetweenLines = size.height();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSplashControl::~CSplashControl()
+{
+}
+
+//=============================================================================
+/**
+ * Draws the splash screen.
+ * @param inRenderer Renderer to draw to
+ */
+void CSplashControl::Draw(CRenderer *inRenderer)
+{
+ CRct theBounds(GetSize());
+ CColor theTextColor(50, 50, 50);
+ long theVertOffset = GetSize().y / 2 - 14; ///< Last line of the copyright starts here
+
+ // Splash screen bitmap
+ inRenderer->DrawBitmap(CPt(0, 0), m_Image);
+
+ // Print the copyright text, starting at the last line and going up to the first line (just to
+ // make sure that we didn't move where the text ends)
+ inRenderer->DrawText(14, static_cast<float>(theVertOffset), m_VersionInfo.toQString(), theBounds,
+ theTextColor);
+ theVertOffset -= m_SpaceBetweenLines;
+ inRenderer->DrawText(14, static_cast<float>(theVertOffset), m_CopyrightLine2.toQString(), theBounds,
+ theTextColor);
+ theVertOffset -= m_SpaceBetweenLines;
+ inRenderer->DrawText(14, static_cast<float>(theVertOffset), m_CopyrightLine1.toQString(), theBounds,
+ theTextColor);
+}
diff --git a/src/Authoring/Studio/Controls/SplashControl.h b/src/Authoring/Studio/Controls/SplashControl.h
new file mode 100644
index 00000000..dbce104c
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SplashControl.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SPLASH_CONTROL_H
+#define INCLUDED_SPLASH_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+
+
+#include <QPixmap>
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//=============================================================================
+/**
+ * Class responsible for drawing contents of a splash screen at program start-up.
+ */
+class CSplashControl : public CControl
+{
+public:
+ CSplashControl();
+ virtual ~CSplashControl();
+
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+ QPixmap m_Image;
+ Q3DStudio::CString m_CopyrightLine1;
+ Q3DStudio::CString m_CopyrightLine2;
+ Q3DStudio::CString m_VersionInfo;
+ long m_SpaceBetweenLines; ///< number of pixels between subsequent lines of the copyright
+ ///statement (calculated automatically)
+};
+
+#endif // INCLUDED_SPLASH_CONTROL_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/SplitBar.cpp b/src/Authoring/Studio/Controls/SplitBar.cpp
new file mode 100644
index 00000000..b37f60e9
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SplitBar.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "SplitBar.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+#include "MouseCursor.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+
+#include <QApplication>
+//=============================================================================
+// Static Variables
+//=============================================================================
+const long CSplitBar::DEFAULT_WIDTH = 5;
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CSplitBar::CSplitBar(CSplitterBase *inSplitter, const long inWidth /*=DEFAULT_WIDTH*/)
+ : m_Splitter(inSplitter)
+ , m_Width(inWidth)
+ , m_MouseDown(false)
+{
+
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSplitBar::~CSplitBar()
+{
+}
+
+//=============================================================================
+/**
+ * Handles mouse down events, starts the dragging.
+ * @param inPoint where the mouse was clicked.
+ * @param inFlags the state of the mouse.
+ */
+bool CSplitBar::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseDown(inPoint, inFlags);
+
+ m_MouseDown = true;
+ m_MouseDownPoint = inPoint;
+
+ Invalidate();
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles mouse up events, stops the dragging.
+ * @param inPoint where the mouse was let up.
+ * @param inFlags the state of the mouse.
+ */
+void CSplitBar::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ m_MouseDown = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Do the drawing of this splitter.
+ * @param inRenderer the renderer to draw to.
+ */
+void CSplitBar::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(CPt(0, 0), GetSize());
+ CRct theHighlightRect;
+ CRct theShadowRect;
+
+ if (m_Splitter->GetSplitDirection() == CSplitterBase::SPLIT_HORIZONTAL) {
+ theHighlightRect.position = CPt(0, 0);
+ theHighlightRect.size = CPt(GetSize().x, 1);
+ theShadowRect.position = CPt(0, theRect.size.y - 1);
+ theShadowRect.size = CPt(theRect.size.x, 1);
+ } else {
+ theHighlightRect.position = CPt(0, 0);
+ theHighlightRect.size = CPt(1, GetSize().y);
+ theShadowRect.position = CPt(theRect.size.x - 1, 0);
+ theShadowRect.size = CPt(1, theRect.size.y);
+ }
+
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetBaseColor());
+ inRenderer->FillSolidRect(theHighlightRect, CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->FillSolidRect(theShadowRect, CStudioPreferences::GetButtonShadowColor());
+}
+
+//=============================================================================
+/**
+ * Get the width of this splitter bar.
+ */
+long CSplitBar::GetWidth()
+{
+ return m_Width;
+}
+
+//=============================================================================
+/**
+ * Set the width of this splitter bar.
+ */
+void CSplitBar::SetWidth(long inWidth)
+{
+ m_Width = inWidth;
+}
+
+//=============================================================================
+/**
+ * Processes the dragging of the split bar.
+ * @param inPoint the position of the mouse.
+ * @param inFlags the state of the mouse buttons.
+ */
+void CSplitBar::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ const Qt::MouseButtons buttons = QApplication::mouseButtons();
+
+ // Don't show the cursor if the mouse is down from someone else.
+ if (!(buttons & Qt::LeftButton) && !(buttons & Qt::RightButton)) {
+ // If the buttons are not down and the mouse is over this control
+ if (HitTest(inPoint + GetPosition())) {
+ // Show the appropriate resize cursor
+ setCursorIfNotSet(m_Splitter->GetSplitDirection() == CSplitterBase::SPLIT_HORIZONTAL
+ ? CMouseCursor::CURSOR_RESIZE_UPDOWN
+ : CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+ } else {
+ resetCursor();
+ }
+ }
+
+ // Only care if the mouse is down
+ if (m_MouseDown) {
+ // When calculating the offsets remember that this object is moving with the mouse
+ // and the inPoint is relative to this position, so inPoint's relative position is
+ // changing on every drag.
+ long theSplitPos = m_Splitter->GetSplitLocation();
+
+ if (m_Splitter->GetSplitDirection() == CSplitterBase::SPLIT_HORIZONTAL) {
+ theSplitPos += inPoint.y - m_MouseDownPoint.y;
+ } else {
+ theSplitPos += inPoint.x - m_MouseDownPoint.x;
+ }
+
+ m_Splitter->SetSplitLocation(theSplitPos);
+ }
+}
+
+//=============================================================================
+/**
+ * Processes the mouse out event. Changes the cursor if necessary back to normal.
+ * @param inPoint the position of the mouse.
+ * @param inFlags the state of the mouse buttons.
+ */
+void CSplitBar::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ const Qt::MouseButtons buttons = QApplication::mouseButtons();
+ // Don't change the cursor if the mouse is down (from someone else or from ourselves)
+ if (!m_MouseDown && !(buttons & Qt::LeftButton) && !(buttons & Qt::RightButton))
+ resetCursor();
+ CControl::OnMouseOut(inPoint, inFlags);
+}
diff --git a/src/Authoring/Studio/Controls/SplitBar.h b/src/Authoring/Studio/Controls/SplitBar.h
new file mode 100644
index 00000000..f70b4469
--- /dev/null
+++ b/src/Authoring/Studio/Controls/SplitBar.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_SPLIT_BAR_H
+#define INCLUDED_SPLIT_BAR_H 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "Control.h"
+
+#include <QCursor>
+
+//=============================================================================
+// Forwards
+//=============================================================================
+
+//=============================================================================
+/**
+ * Abstract class for implementing a splitter control
+ */
+class CSplitterBase
+{
+public:
+ enum ESplitDirection {
+ SPLIT_VERTICAL,
+ SPLIT_HORIZONTAL,
+ };
+
+ virtual void SetSplitLocation(long inPixels) = 0;
+ virtual long GetSplitLocation() const = 0;
+ virtual ESplitDirection GetSplitDirection() const = 0;
+};
+
+//=============================================================================
+/**
+ * Defines the bar used in a splitter control. Handles drawing and cursors for
+ * the bar.
+ */
+class CSplitBar : public CControl
+{
+public:
+ static const long DEFAULT_WIDTH;
+
+ CSplitBar(CSplitterBase *inSplitter, long inWidth = DEFAULT_WIDTH);
+ virtual ~CSplitBar();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void Draw(CRenderer *inRenderer) override;
+
+ virtual long GetWidth();
+ virtual void SetWidth(long inWidth);
+
+protected:
+ CSplitterBase *m_Splitter;
+ long m_Width;
+
+ bool m_MouseDown;
+ CPt m_MouseDownPoint;
+};
+
+#endif // INCLUDED_SPLIT_BAR_H
diff --git a/src/Authoring/Studio/Controls/Splitter.cpp b/src/Authoring/Studio/Controls/Splitter.cpp
new file mode 100644
index 00000000..b0ae0eb0
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Splitter.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "Splitter.h"
+#include "ControlData.h"
+
+using namespace Q3DStudio;
+using namespace Q3DStudio::Control;
+//=============================================================================
+/**
+ * Constructor, creates a Splitter.
+ */
+CSplitter::CSplitter()
+ : m_SplitDirection(SPLIT_VERTICAL)
+ , m_SplitLocation(0)
+ , m_InRecalcLayoutFlag(false)
+{
+ m_SplitMinMargin = 10;
+ m_SplitMaxMargin = 10;
+ m_SplitBar = new CSplitBar(this);
+ AddChild(m_SplitBar);
+}
+
+CSplitter::~CSplitter()
+{
+ RemoveChild(m_SplitBar);
+ delete m_SplitBar;
+}
+
+//=============================================================================
+/**
+ * Set the size of this splitter.
+ * @param inSize the size of this splitter.
+ */
+void CSplitter::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ CControl::SetSize(inSize);
+
+ RecalcLayout();
+ }
+}
+
+void CSplitter::SetLayout(CPt inSize, CPt inPosition)
+{
+ CControl::SetLayout(inSize, inPosition);
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Get the preferred size of this splitter.
+ * The preferred size is the sum of both the panes.
+ * @return the preferred size of this splitter.
+ */
+CPt CSplitter::GetPreferredSize()
+{
+ CPt theSize = CControl::GetPreferredSize();
+
+ if (m_SplitDirection == SPLIT_VERTICAL) {
+ theSize.x = m_SplitBar->GetWidth();
+
+ // If vertical then sum up x's
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ CPt thePrefSize = (*thePos)->GetPreferredSize();
+ theSize.x += thePrefSize.x;
+ }
+ } else {
+ theSize.y = m_SplitBar->GetWidth();
+
+ // If horizontal then sum up y's
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ CPt thePrefSize = (*thePos)->GetPreferredSize();
+ theSize.y += thePrefSize.y;
+ }
+ }
+
+ return theSize;
+}
+
+//=============================================================================
+/**
+ * Get the minimum allowable size of this splitter.
+ * The minimum size is the sum of both the minimum sizes in the split direciton
+ * and the largest minimum in the non-split direction.
+ * @return the minimum size of this control.
+ */
+CPt CSplitter::GetMinimumSize()
+{
+ CPt theSize(0, 0);
+
+ if (m_SplitDirection == SPLIT_VERTICAL) {
+ theSize.x += m_SplitBar->GetWidth();
+
+ // Sum up in the split direction and take max min size in non-split direction
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ CPt theMinSize = (*thePos)->GetMinimumSize();
+ theSize.x += theMinSize.x;
+
+ if (theSize.y < theMinSize.y) {
+ theSize.y = theMinSize.y;
+ }
+ }
+ } else {
+ theSize.y += m_SplitBar->GetWidth();
+
+ // Sum up in the split direction and take max min size in non-split direction.
+ ControlGraph::SIterator thePos = GetChildren();
+ for (; thePos.HasNext(); ++thePos) {
+ CPt theMinSize = (*thePos)->GetMinimumSize();
+ theSize.y += theMinSize.y;
+
+ if (theSize.x > theMinSize.x) {
+ theSize.x = theMinSize.x;
+ }
+ }
+ }
+
+ return theSize;
+}
+
+//=============================================================================
+/**
+ * Get the maximum size that this control should be.
+ * The maximum size is the sum of both the maximum sizes in the split direction
+ * and the smalled maximum size in the non-split direction.
+ * @return the maximum size of this control.
+ */
+CPt CSplitter::GetMaximumSize()
+{
+ CPt theSize(0, 0);
+
+ if (m_SplitDirection == SPLIT_VERTICAL) {
+ theSize.x += m_SplitBar->GetWidth();
+ theSize.y = LONG_MAX;
+
+ // Sum up the split direction and take min max size in non-split direction.
+ ControlGraph::SIterator thePos = GetChildren();
+ // Skip the separator
+ ++thePos;
+ for (; thePos.HasNext(); ++thePos) {
+ CPt theMaxSize = (*thePos)->GetMaximumSize();
+ theSize.x += theMaxSize.x;
+
+ if (theSize.y > theMaxSize.y) {
+ theSize.y = theMaxSize.y;
+ }
+ }
+ } else {
+ theSize.y += m_SplitBar->GetWidth();
+ theSize.x = LONG_MAX;
+
+ // Sum up the split direction and take min max size in the non-split direction.
+ ControlGraph::SIterator thePos = GetChildren();
+ // Skip the separator.
+ ++thePos;
+ for (; thePos.HasNext(); ++thePos) {
+ CPt theMaxSize = (*thePos)->GetMaximumSize();
+ theSize.y += theMaxSize.y;
+
+ if (theSize.x > theMaxSize.x) {
+ theSize.x = theMaxSize.x;
+ }
+ }
+ }
+
+ return theSize;
+}
+
+//=============================================================================
+/**
+ * Set the location of the splitter bar.
+ * @param inSplitLocation the location of the splitter bar, in pixels from the right/top.
+ */
+void CSplitter::SetSplitLocation(long inSplitLocation)
+{
+ ControlGraph::SIterator theChildren = GetChildren();
+
+ std::shared_ptr<CControlData> theControl1;
+ std::shared_ptr<CControlData> theControl2;
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl1 = *theChildren;
+
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl2 = *theChildren;
+ }
+ }
+ if (m_SplitDirection == SPLIT_VERTICAL && theControl1) {
+ CPt theControl1Min = theControl1->GetMinimumSize();
+ if (theControl1Min.x > inSplitLocation)
+ inSplitLocation = theControl1Min.x;
+ } else if (theControl2) {
+ CPt theControl1Min = theControl1->GetMinimumSize();
+ if (theControl1Min.y > inSplitLocation && theControl2)
+ inSplitLocation = theControl1Min.y;
+ }
+ m_SplitLocation = inSplitLocation;
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Get the location of the splitter bar.
+ * @return the location of the splitter bar, in pixels from the right/top.
+ */
+long CSplitter::GetSplitLocation() const
+{
+ long theSplitLocation = m_SplitLocation;
+
+ // Enforce the max margin and then the min margin (in that order)
+ // this is done on the get so that internally we maintain
+ // the correct location of the splitter
+ if (m_SplitDirection == SPLIT_VERTICAL) {
+ if (m_SplitLocation > GetSize().x - m_SplitMaxMargin /*- m_SplitBar->GetWidth( )*/)
+ theSplitLocation = GetSize().x - m_SplitMaxMargin /*- m_SplitBar->GetWidth( )*/;
+ } else {
+ if (m_SplitLocation > GetSize().y - m_SplitMaxMargin)
+ theSplitLocation = GetSize().y - m_SplitMaxMargin;
+ }
+
+ if (m_SplitLocation < m_SplitMinMargin)
+ theSplitLocation = m_SplitMinMargin;
+
+ return theSplitLocation;
+}
+
+//=============================================================================
+/**
+ * Set the minimum and maximum split location. This will limit the splitter
+ * left and right or up and down.
+ * @param inSplitMinMargin the number of pixels from the left/top of the pane
+ * @param inSplitMaxMargin the number of pixels from the right/bottom of the pane.
+ */
+void CSplitter::SetSplitLimits(long inSplitMinMargin, long inSplitMaxMargin)
+{
+ m_SplitMinMargin = inSplitMinMargin;
+ m_SplitMaxMargin = inSplitMaxMargin;
+}
+
+//=============================================================================
+/**
+ * Set the direction that this is being split in.
+ * @param inSplitDirection the direction that this is being split in.
+ */
+void CSplitter::SetSplitDirection(CSplitterBase::ESplitDirection inSplitDirection)
+{
+ m_SplitDirection = inSplitDirection;
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Get the direction that this is being split in.
+ * @return the direction that this is being split in.
+ */
+CSplitterBase::ESplitDirection CSplitter::GetSplitDirection() const
+{
+ return m_SplitDirection;
+}
+
+//=============================================================================
+/**
+ * Add a child to this splitter.
+ * If more than 2 children are added then this will pop off the last one to be
+ * added before inControl.
+ * @param inControl the control to add to this.
+ * @param inInsertAfter the position to inster the control.
+ */
+void CSplitter::AddChild(CControl *inControl, CControl *inInsertAfter /*=nullptr*/)
+{
+ CControl::AddChild(inControl, inInsertAfter);
+
+ // If there are more than 3 objects then ditch the last one, allows the insert after to still
+ // work.
+ if (GetChildCount() > 3)
+ RemoveChild(GetReverseChildren().GetCurrent()->GetControl());
+
+ RecalcLayout();
+}
+
+void CSplitter::RecalcLayout()
+{
+ CPt thePoint(0, 0);
+ CPt theSize = GetSize();
+
+ std::shared_ptr<CControlData> theControl1;
+ std::shared_ptr<CControlData> theControl2;
+
+ long theSplitLocation = GetSplitLocation();
+
+ ControlGraph::SIterator theChildren = GetChildren();
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl1 = *theChildren;
+
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl2 = *theChildren;
+ }
+ }
+
+ // Prevent OnChildSizeChanged from screwing with us
+ m_InRecalcLayoutFlag = true;
+
+ if (theControl2 != nullptr) {
+ CPt theControl1Max = theControl1->GetMaximumSize();
+ CPt theControl2Max = theControl2->GetMaximumSize();
+
+ if (m_SplitDirection == SPLIT_VERTICAL) {
+ CPt theControl1Min = theControl1->GetMinimumSize();
+ if (theControl1Min.x > theSplitLocation) {
+ theSplitLocation = theControl1Min.x;
+ }
+ theControl1->SetPosition(CPt(0, 0));
+ theControl1->SetSize(CPt(theSplitLocation, min(theSize.y, theControl1Max.y)));
+
+ m_SplitBar->SetPosition(CPt(theSplitLocation, 0));
+ m_SplitBar->SetSize(CPt(m_SplitBar->GetWidth(), theSize.y));
+
+ theControl2->SetPosition(CPt(theSplitLocation + m_SplitBar->GetWidth(), 0));
+ theControl2->SetSize(CPt(theSize.x - (theSplitLocation + m_SplitBar->GetWidth()),
+ min(theSize.y, theControl2Max.y)));
+ } else {
+ CPt theControl1Min = theControl1->GetMinimumSize();
+ if (theControl1Min.y > theSplitLocation) {
+ theSplitLocation = theControl1Min.y;
+ }
+ theControl1->SetPosition(CPt(0, 0));
+ theControl1->SetSize(CPt(min(theSize.x, theControl1Max.x), theSplitLocation));
+
+ m_SplitBar->SetPosition(CPt(0, theSplitLocation));
+ m_SplitBar->SetSize(CPt(theSize.x, m_SplitBar->GetWidth()));
+
+ theControl2->SetPosition(CPt(0, theSplitLocation + m_SplitBar->GetWidth()));
+ theControl2->SetSize(CPt(min(theSize.x, theControl2Max.x),
+ theSize.y - (theSplitLocation + m_SplitBar->GetWidth())));
+ }
+ }
+
+ m_InRecalcLayoutFlag = false;
+
+ Invalidate();
+}
+
+void CSplitter::OnChildSizeChanged(CControl *)
+{
+ if (m_InRecalcLayoutFlag == false) {
+ std::shared_ptr<CControlData> theControl1;
+ std::shared_ptr<CControlData> theControl2;
+
+ ControlGraph::SIterator theChildren = GetChildren();
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl1 = *theChildren;
+
+ ++theChildren;
+ if (theChildren.HasNext()) {
+ theControl2 = *theChildren;
+ }
+ }
+
+ CPt theSize = GetSize();
+ if (theControl2 != nullptr) {
+ CPt theSize1 = theControl1->GetSize();
+ CPt theSize2 = theControl2->GetSize();
+ if (m_SplitDirection == SPLIT_VERTICAL)
+ theSize.y = max(theSize1.y, theSize2.y);
+ else
+ theSize.x = max(theSize1.x, theSize2.x);
+ }
+
+ SetSize(theSize);
+ SetMaximumSize(theSize);
+
+ RecalcLayout();
+ }
+
+ // Notify the folks
+ if (GetParent() != nullptr)
+ GetParent()->OnChildSizeChanged(this);
+}
+
+void CSplitter::SetSplitBarWidth(const long inWidth)
+{
+ m_SplitBar->SetWidth(inWidth);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/Splitter.h b/src/Authoring/Studio/Controls/Splitter.h
new file mode 100644
index 00000000..94c70781
--- /dev/null
+++ b/src/Authoring/Studio/Controls/Splitter.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SPLIT_AGENT_H
+#define INCLUDED_SPLIT_AGENT_H 1
+
+#pragma once
+
+#include "SplitBar.h"
+
+class CSplitter : public CControl, public CSplitterBase
+{
+public:
+ CSplitter();
+ virtual ~CSplitter();
+
+ virtual void SetSize(CPt inSize);
+ virtual void SetLayout(CPt inSize, CPt inPosition);
+ virtual CPt GetPreferredSize();
+ virtual CPt GetMinimumSize();
+ virtual CPt GetMaximumSize();
+
+ // CSplitterBase
+ virtual void SetSplitLocation(long inPixels);
+ virtual long GetSplitLocation() const;
+ virtual ESplitDirection GetSplitDirection() const;
+
+ virtual void SetSplitLimits(long inSplitMinMargin, long inSplitMaxMargin);
+ virtual void SetSplitDirection(CSplitterBase::ESplitDirection inSplitDirection);
+
+ virtual void AddChild(CControl *inControl, CControl *inInsertBefore = nullptr);
+
+ virtual void OnChildSizeChanged(CControl *inChild);
+
+ virtual void SetSplitBarWidth(const long inWidth);
+
+protected:
+ void RecalcLayout();
+
+ ESplitDirection m_SplitDirection;
+ long m_SplitLocation;
+ long m_SplitMinMargin;
+ long m_SplitMaxMargin;
+ CSplitBar *m_SplitBar;
+ bool m_InRecalcLayoutFlag;
+};
+
+#endif // INCLUDED_SPLIT_AGENT_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Controls/StringEdit.cpp b/src/Authoring/Studio/Controls/StringEdit.cpp
new file mode 100644
index 00000000..34b5f521
--- /dev/null
+++ b/src/Authoring/Studio/Controls/StringEdit.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Include
+//==============================================================================
+#include "StringEdit.h"
+#include "OffscreenRenderer.h"
+#include "CoreUtils.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CStringEdit::CStringEdit()
+ : m_AutoSize(false)
+{
+ // StringEdit is going to have to handle its own RevertText.
+ // This can't be done in TextEdit because FloatEdit ( VectorEdit, etc ) are using the command
+ // stack for undo/redo
+ // StringEdit doesn't have command stack set until you hit enter or lose focus.
+ // We can't make CTextEdit::RegisterCommands a virtual function since its being called in the
+ // constructor,
+ // therefore this is a pretty lame way to register for a hotkey.
+ m_CommandHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStringEdit>(this, &CStringEdit::RevertText),
+ Qt::ControlModifier, Qt::Key_Z);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CStringEdit::~CStringEdit()
+{
+}
+
+//==============================================================================
+/**
+ * Returns the string value of the control.
+ */
+Q3DStudio::CString CStringEdit::GetString()
+{
+ return m_Value;
+}
+
+//==============================================================================
+/**
+ * Sets the string value of the control.
+ */
+void CStringEdit::SetData(const Q3DStudio::CString &inValue, bool inFireEvent /*= true*/)
+{
+ if (m_Value != inValue) {
+ m_Value = inValue;
+ SetDisplayString(inValue, inFireEvent);
+
+ if (inFireEvent)
+ SetDirty(true);
+ }
+}
+
+//==============================================================================
+/**
+ * Commits changes to the value of this control when the control loses focus.
+ */
+void CStringEdit::OnLoseFocus()
+{
+ CTextEdit::OnLoseFocus();
+ FireCommitEvent();
+}
+
+//==============================================================================
+/**
+ * Commits changes to the value of this control when the Enter button is pressed.
+ * @param inHighlight true to highlight the text after committing it
+ */
+void CStringEdit::EnterText(bool inHighlight)
+{
+ CTextEdit::EnterText(inHighlight);
+ FireCommitEvent();
+}
+
+void CStringEdit::RefreshDisplayFromData()
+{
+ SetDisplayString(GetString());
+ Invalidate();
+}
+
+bool CStringEdit::CanPaste()
+{
+ return true;
+}
+
+//==============================================================================
+/**
+ * Enables or disables auto-sizing of this control. If auto-sizing is enabled
+ * the size of this control will be automatically set to fit the text that it
+ * contains.
+ * @param inAllow true to enable auto-sizing, false to disable auto-sizing
+ */
+void CStringEdit::AllowAutoSize(bool inAllow)
+{
+ m_AutoSize = inAllow;
+}
+
+//==============================================================================
+/**
+ * @return true if auto-resizing is enabled, otherwise false
+ */
+bool CStringEdit::GetAllowAutoSize()
+{
+ return m_AutoSize;
+}
+
+//==============================================================================
+/**
+ * If auto-resizing is enabled, this function will resize this control so that
+ * it is the same size as the text that it is displaying. Text size is calculated
+ * with an offscreen buffer, so this can be done outside of a draw operation.
+ */
+void CStringEdit::ResetSize()
+{
+ // If auto-resizing of the text field is enabled
+ if (m_AutoSize) {
+ // Resize the control to fit the text, plus the buffer gap
+ COffscreenRenderer theRenderer(CRct(0, 0, 100, 16));
+ CPt theSize;
+ const auto textSize = theRenderer.GetTextSize(GetDisplayString().toQString());
+ theSize.x = textSize.width() + GetBufferLength() * 3;
+ theSize.y = textSize.height() + 1;
+ SetMinimumSize(theSize);
+ SetPreferredSize(theSize);
+ SetMaximumSize(theSize);
+ }
+}
+
+//==============================================================================
+/**
+ * Primarily delegates up to the parent class, but responds by recalculating
+ * size of the text box for auto-sized strings.
+ *
+ * @param inDirty true to mark this control as dirty, which causes the string to be redrawn
+ * false to mark the control as not needing to reevaluate its text during next draw cycle
+ */
+void CStringEdit::SetDirty(bool inDirty)
+{
+ // Allow the parent to handle this situation
+ CTextEdit::SetDirty(inDirty);
+
+ ResetSize();
+}
+
+//==============================================================================
+/**
+ * Reverts the displayed text to the previous text.
+ */
+void CStringEdit::RevertText()
+{
+ SetData(m_PreviousValue);
+ SetDisplayString(m_PreviousValue);
+
+ FireCommitEvent();
+ SelectAllText();
+}
+
+//==============================================================================
+/**
+ * Handles any non-character keys that were pressed and need to be handled.
+ *
+ * @param inChar The key that was pressed
+ * @param inFlags Indicates which modifier keys were down when event occurred
+ * @return true if this function handled the character, otherwise false
+ */
+bool CStringEdit::HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool theMessageWasHandled = false;
+
+ switch (inChar) {
+ // Escape and Ctrl+Z both do basically the same thing for StringEdits,
+ // They are special cased in TextEditInPlace though.
+ case Qt::Key_Escape:
+ RevertText();
+ theMessageWasHandled = true;
+ break;
+
+ default:
+ theMessageWasHandled = CTextEdit::HandleSpecialChar(inChar, inFlags);
+ }
+
+ return theMessageWasHandled;
+}
+
+//==============================================================================
+/**
+ * Called when this control gains focus. Shows the caret, clears the current
+ * selection and invalidates the control so that it will get redrawn.
+ */
+void CStringEdit::OnGainFocus()
+{
+ CTextEdit::OnGainFocus();
+ m_PreviousValue = GetString();
+}
+
+//=============================================================================
+/**
+ * Handles character input from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CStringEdit::OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags)
+{
+ return CTextEdit::OnChar(inChar, inFlags);
+}
diff --git a/src/Authoring/Studio/Controls/StringEdit.h b/src/Authoring/Studio/Controls/StringEdit.h
new file mode 100644
index 00000000..8f3c34b1
--- /dev/null
+++ b/src/Authoring/Studio/Controls/StringEdit.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STRING_EDIT_H
+#define INCLUDED_STRING_EDIT_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TextEdit.h"
+
+class CStringEdit : public CTextEdit
+{
+public:
+ CStringEdit();
+ virtual ~CStringEdit();
+ Q3DStudio::CString GetString() override;
+ void SetData(const Q3DStudio::CString &inValue, bool inFireEvent = true) override;
+ void OnLoseFocus() override;
+ void EnterText(bool inHighlight) override;
+ void RefreshDisplayFromData() override;
+ bool CanPaste() override;
+ virtual void ResetSize();
+ void AllowAutoSize(bool inAllow);
+ bool GetAllowAutoSize();
+
+ // overload functions
+ bool HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags) override;
+ void OnGainFocus() override;
+ bool OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ Q3DStudio::CString m_Value;
+ Q3DStudio::CString m_PreviousValue;
+ bool m_AutoSize;
+
+ virtual void RevertText();
+ void SetDirty(bool inDirty) override;
+};
+
+#endif // INCLUDED_STRING_EDIT_H
diff --git a/src/Authoring/Studio/Controls/TextButton.h b/src/Authoring/Studio/Controls/TextButton.h
new file mode 100644
index 00000000..2593a1d1
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextButton.h
@@ -0,0 +1,350 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TEXT_BUTTON_H
+#define INCLUDED_TEXT_BUTTON_H 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Renderer.h"
+#include "OffscreenRenderer.h"
+#include "CoreUtils.h"
+#include "StudioPreferences.h"
+#include "ButtonControl.h"
+#include "CColor.h"
+
+#include <QSize>
+
+const long MARGIN_X = 12;
+const long MARGIN_Y = 6;
+
+//=============================================================================
+/**
+ * Template class for making buttons that have text on them. The text can be
+ * displayed with or without the button's icon. In order to use this class,
+ * you must instantiate it with some sort of CButtonControl to act as the base
+ * class.
+ */
+template <class TButton>
+class CTextButton : public TButton
+{
+ // Enumerations
+public:
+ enum EAlignment {
+ ALIGNMENT_LEFT,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_RIGHT,
+ ALIGNMENT_VCENTER ///< Only align center vertically
+ };
+
+ // Constuction/Destruction
+public:
+ CTextButton();
+ virtual ~CTextButton();
+ // Access
+public:
+ void SetText(const Q3DStudio::CString &inText);
+ void SetBoldText(const Q3DStudio::CString &inText);
+ Q3DStudio::CString GetText();
+ void SetTextColorUp(const CColor &inColor);
+ void SetTextColorDown(const CColor &inColor);
+ CColor GetTextColorUp();
+ CColor GetTextColorDown();
+ void SetTextAlignment(EAlignment inAlignment);
+ long GetTextAlignment();
+
+ // Implementation
+protected:
+ virtual void Render(CRenderer *inRenderer);
+ virtual void Resize();
+ void SizeToFitTextLen();
+
+ // Field members
+protected:
+ Q3DStudio::CString m_Text; ///< Text
+ CColor m_TextColorUp; ///< Color of text
+ CColor m_TextColorDown; ///< Color of text
+ CPt m_TextPos; ///< Position of text
+ EAlignment m_TextAlign; ///< Alignment of text
+ BOOL m_BoldText;
+};
+
+//==============================================================================
+// Template implemenations
+//==============================================================================
+
+//=============================================================================
+/**
+ * Constructor
+ */
+template <class TButton>
+CTextButton<TButton>::CTextButton()
+ : TButton()
+ , m_TextColorUp(CStudioPreferences::GetNormalColor())
+ , m_TextColorDown(CStudioPreferences::GetNormalColor())
+ , m_TextPos(MARGIN_X, MARGIN_Y / 2)
+ , m_TextAlign(ALIGNMENT_LEFT)
+ , m_BoldText(FALSE)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+template <class TButton>
+CTextButton<TButton>::~CTextButton()
+{
+}
+
+//=============================================================================
+/**
+ * Sets the text displayed on this button to the specified string.
+ * @param inText new text to be displayed on this button
+ */
+template <class TButton>
+void CTextButton<TButton>::SetText(const Q3DStudio::CString &inText)
+{
+ if (m_Text != inText) {
+ m_Text = inText;
+ SizeToFitTextLen();
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the text bold to be displayed on this button to the specified string.
+ * @param inText new text to be displayed on this button
+ */
+template <class TButton>
+void CTextButton<TButton>::SetBoldText(const Q3DStudio::CString &inText)
+{
+ m_Text = inText;
+ m_BoldText = TRUE;
+ SizeToFitTextLen();
+ TButton::Invalidate();
+}
+
+//=============================================================================
+/**
+ * @return the text currently displayed on this button
+ */
+template <class TButton>
+Q3DStudio::CString CTextButton<TButton>::GetText()
+{
+ return m_Text;
+}
+
+//=============================================================================
+/**
+ * Sets the color of the text displayed on this button in the up state
+ * @param inColor new text color
+ */
+template <class TButton>
+void CTextButton<TButton>::SetTextColorUp(const CColor &inColor)
+{
+ if (m_TextColorUp != inColor) {
+ m_TextColorUp = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the color of the text displayed on this button in the down state
+ * @param inColor new text color
+ */
+template <class TButton>
+void CTextButton<TButton>::SetTextColorDown(const CColor &inColor)
+{
+ if (m_TextColorDown != inColor) {
+ m_TextColorDown = inColor;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * @return the color of the text displayed on this button in the up state
+ */
+template <class TButton>
+CColor CTextButton<TButton>::GetTextColorUp()
+{
+ return m_TextColorUp;
+}
+
+//=============================================================================
+/**
+ * @return the color of the text displayed on this button in the down state
+ */
+template <class TButton>
+CColor CTextButton<TButton>::GetTextColorDown()
+{
+ return m_TextColorDown;
+}
+
+//=============================================================================
+/**
+ * Set the alignment of the text.
+ */
+template <class TButton>
+void CTextButton<TButton>::SetTextAlignment(EAlignment inAlignment)
+{
+ if (inAlignment != m_TextAlign) {
+ m_TextAlign = inAlignment;
+ TButton::Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * @return the alignment of the text displayed on this button
+ */
+template <class TButton>
+long CTextButton<TButton>::GetTextAlignment()
+{
+ return m_TextAlign;
+}
+
+//=============================================================================
+/**
+ * Draws the button, then draws the specified text onto the button.
+ * @param inRenderer the renderer to draw to
+ */
+template <class TButton>
+void CTextButton<TButton>::Render(CRenderer *inRenderer)
+{
+ // Draw the button icon
+ TButton::Render(inRenderer);
+
+ // Detertmine position of text based off horizontal alignment
+ float theTextPosX(static_cast<float>(m_TextPos.x));
+ float theTextPosY(static_cast<float>(m_TextPos.y));
+
+ long theImageX = 0;
+ long theImageY = 0;
+ if (!TButton::GetCurrentImage().isNull()) {
+ theImageX += TButton::GetCurrentImage().size().width();
+ theImageY += TButton::GetCurrentImage().size().height();
+ }
+
+ switch (m_TextAlign) {
+ case ALIGNMENT_CENTER: {
+ const auto textSize = inRenderer->GetTextSize(m_Text.toQString());
+ theTextPosX = (TButton::GetSize().x - textSize.width() + theImageX) / 2;
+ theTextPosY = static_cast<float>(m_TextPos.y);
+ } break;
+
+ case ALIGNMENT_RIGHT: {
+ const auto textSize = inRenderer->GetTextSize(m_Text.toQString());
+ theTextPosX = TButton::GetSize().x - textSize.width();
+ theTextPosY = static_cast<float>(m_TextPos.y);
+ } break;
+
+ case ALIGNMENT_VCENTER: {
+ const auto textSize = inRenderer->GetTextSize(m_Text.toQString());
+ theTextPosY = (TButton::GetSize().y - textSize.height() + theImageY) / 2;
+ theTextPosX = static_cast<float>(m_TextPos.x);
+ }
+ // Default is LEFT justification
+ default:
+ case ALIGNMENT_LEFT:
+ theTextPosX += theImageX;
+ break;
+ }
+
+ typename TButton::EButtonState theState = TButton::GetButtonState();
+ ::CColor theTextColor = m_TextColorUp;
+ if (theState == CButtonControl::EBUTTONSTATE_DOWN)
+ theTextColor = m_TextColorDown;
+ if (TButton::IsEnabled() == false)
+ theTextColor = CStudioPreferences::GetDisabledTextColor();
+
+ inRenderer->PushPen(theTextColor);
+
+ // Draw the text
+ const auto buttonSize = TButton::GetSize();
+ const QRect rect(0, 0, buttonSize.x, buttonSize.y);
+ if (m_BoldText && !m_Text.IsEmpty()) {
+ inRenderer->DrawBoldText(theTextPosX, theTextPosY, m_Text.toQString(), rect, theTextColor);
+ } else {
+ if (!m_Text.IsEmpty())
+ inRenderer->DrawText(theTextPosX, theTextPosY, m_Text.toQString(), rect, theTextColor);
+ }
+
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * this function calculates the text length in the button control and sets the size of the
+ *button
+ */
+template <class TButton>
+void CTextButton<TButton>::Resize()
+{
+ TButton::Resize();
+ SizeToFitTextLen();
+}
+
+//=============================================================================
+/**
+ * this function calculates the text length in the button control and sets the size of the
+ *button
+ */
+template <class TButton>
+void CTextButton<TButton>::SizeToFitTextLen()
+{
+ // If auto-resizing of the text field is enabled
+ if (TButton::m_AutoSize) {
+ // Resize the control to fit the text, plus the buffer gap
+ COffscreenRenderer theRenderer(CRct(0, 0, 1, 1));
+ CPt theSize;
+ const auto textSize = theRenderer.GetTextSize(m_Text.toQString());
+ int theX = textSize.width();
+ int theY = textSize.height();
+
+ if (!TButton::GetCurrentImage().isNull())
+ theX += TButton::GetCurrentImage().size().width();
+
+ // MARGIN_LEN * 2 is just to add some space between the text and the button
+ theSize.x = ::dtol(theX) + (MARGIN_X * 2);
+ theSize.y = static_cast<long>(theY + (MARGIN_Y * 2));
+ TButton::SetSize(theSize);
+ TButton::SetMaximumSize(theSize);
+ TButton::Invalidate();
+ }
+}
+#endif // INCLUDED_TEXT_BUTTON_H
diff --git a/src/Authoring/Studio/Controls/TextEdit.cpp b/src/Authoring/Studio/Controls/TextEdit.cpp
new file mode 100644
index 00000000..76f925e1
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEdit.cpp
@@ -0,0 +1,1478 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TextEdit.h"
+#include "Renderer.h"
+#include "SystemPreferences.h"
+#include "HotKeys.h"
+#include "TextEditContextMenu.h"
+#include "StudioClipboard.h"
+#include "CoreUtils.h"
+#include "UICMath.h"
+#include "UICDMSignals.h"
+#include "MouseCursor.h"
+#include "StudioApp.h"
+#include "Core.h"
+
+#include <QApplication>
+
+using namespace Q3DStudio;
+
+//==============================================================================
+// Constants
+//==============================================================================
+const long BLINKSPEED =
+ 700; ///< Speed of the blinking cursor while editing, measured in milliseconds
+
+IMPLEMENT_OBJECT_COUNTER(CTextEdit)
+
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CTextEdit::CTextEdit()
+ : m_IsStringDirty(true)
+ , m_NeedsCommit(false)
+ , m_Alignment(RIGHT)
+ , m_TotalCharWidth(0)
+ , m_BufferLength(2)
+ , m_MaxLength(QT3DS_MAX_I32)
+ , m_IsReadOnly(false)
+ , m_MouseIsDown(false)
+ , m_FillBackground(true)
+ , m_TextColor(0, 0, 0)
+ , m_UseBGColorReadOnly(false)
+ , m_BackgroundColorNoFocus(CStudioPreferences::GetTextBoxBGColorNoFocus())
+ , m_BackgroundColorFocus(CStudioPreferences::GetTextBoxBGColorWithFocus())
+ , m_BoldText(false)
+ , m_ScrollAmount(0, 0)
+ , m_CommandHandler(g_StudioApp.GetCore()->GetHotKeys())
+{
+ ADDTO_OBJECT_COUNTER(CTextEdit)
+
+ m_Caret.color = ::CColor(0, 0, 0);
+ m_Caret.position = 0;
+ m_Caret.show = false;
+
+ m_StartDragPt = CPt(0, 0);
+ m_SelectionStart = 0;
+ m_SelectionEnd = 0;
+
+ m_TextColor = CStudioPreferences::GetRulerTickColor();
+
+ RegisterCommands();
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CTextEdit::~CTextEdit()
+{
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+
+ REMOVEFROM_OBJECT_COUNTER(CTextEdit)
+
+ // Added to help debug freeing memory allocated from inside a DLL
+ m_DisplayString.Clear();
+}
+
+//==============================================================================
+/**
+ * Sets text justification to left, right, or center.
+ *
+ * @param inAlignment Enumeration describing the t3xt justification
+ */
+void CTextEdit::SetAlignment(EAlignment inAlignment)
+{
+ m_Alignment = inAlignment;
+}
+
+//==============================================================================
+/**
+ * Draws the bounding rectangle and white edit box for the text edit class. Also
+ * draws the actual text in the box by calling GetString(), which should be
+ * implemented by any class that extends CTextEdit.
+ */
+void CTextEdit::Draw(CRenderer *inRenderer)
+{
+ inRenderer->PushClippingRect(QRect(0, 0, GetSize().x, GetSize().y));
+
+ // If the string has changed since we last drew it, cache the character widths
+ if (m_IsStringDirty) {
+ CalculateCharWidths(inRenderer);
+ SetDirty(false);
+ }
+
+ DoFillBackground(inRenderer);
+
+ // Draw the actual text
+ DrawText(inRenderer);
+
+ // Draw the caret last
+ DrawCaret(inRenderer);
+
+ inRenderer->PopClippingRect();
+}
+
+//==============================================================================
+/**
+ * Determines the bounding rectangle for the text based on the text justification
+ * setting.
+ *
+ * @return the bounding rectangle for the text being displayed.
+ */
+CRct CTextEdit::GetTextRect()
+{
+ CRct theTextRect;
+
+ switch (m_Alignment) {
+ case LEFT:
+ theTextRect = CRct(CPt(m_BufferLength, 0), CPt(::dtol(m_TotalCharWidth), GetSize().y));
+ break;
+
+ case CENTER:
+ theTextRect = CRct(CPt(GetSize().x / 2 - ::dtol(m_TotalCharWidth / 2), 0),
+ CPt(::dtol(m_TotalCharWidth), GetSize().y));
+ break;
+
+ case RIGHT:
+ // NO BREAK
+ default: {
+ theTextRect = CRct(CPt(GetSize().x - ::dtol(m_TotalCharWidth + m_BufferLength), 0),
+ CPt(::dtol(m_TotalCharWidth), GetSize().y));
+ } break;
+ }
+
+ return theTextRect;
+}
+
+//==============================================================================
+/**
+ * Draws the text out to the screen. If any selection is occurring, the
+ * appropriate text is rendered out to the screen using the system preferences
+ * for selection color.
+ *
+ * @param inRenderer Renderer to draw the text out to
+ */
+void CTextEdit::DrawText(CRenderer *inRenderer)
+{
+ Q3DStudio::CString theCompleteString = GetDisplayString();
+ Q3DStudio::CString theFirstPart = theCompleteString;
+ Q3DStudio::CString theHighlightedString;
+ Q3DStudio::CString theLastPart;
+
+ float theTextSizeX;
+ float theTextSizeY;
+
+ if (HasSelectedText()) {
+ theFirstPart = theCompleteString.Extract(0, GetSelectionLeft());
+ theHighlightedString =
+ theCompleteString.Extract(GetSelectionLeft(), GetSelectionRight() - GetSelectionLeft());
+ theLastPart = theCompleteString.Extract(GetSelectionRight(),
+ theCompleteString.Length() - GetSelectionRight());
+ }
+
+ // Calculate where the upper left corner where text begins (we'll move this point for each part
+ // of the text)
+ float theUpperLeftX = static_cast<float>(GetTextRect().position.x + m_ScrollAmount.x);
+ float theUpperLeftY = static_cast<float>(GetTextRect().position.y + m_ScrollAmount.y);
+
+ const QRect sizeRect(0, 0, GetSize().x, GetSize().y);
+ CRct theSelectionRect(0, 0, 0, 0);
+
+ if (theFirstPart.Length() > 0) {
+ if (!m_BoldText) {
+ inRenderer->DrawText(theUpperLeftX, theUpperLeftY, theFirstPart.toQString(),
+ CRct(GetSize()), m_TextColor);
+ } else {
+ inRenderer->DrawBoldText(theUpperLeftX, theUpperLeftY, theFirstPart.toQString(),
+ CRct(GetSize()), m_TextColor);
+ }
+
+ // Move the upper left corner
+ const auto textSize = inRenderer->GetTextSize(theFirstPart.toQString());
+ theTextSizeX = textSize.width();
+ theTextSizeY = textSize.height();
+ theUpperLeftX += theTextSizeX;
+ }
+
+ if (theHighlightedString.Length() > 0) {
+ // Draw the selection rectangle
+ const auto textSize = inRenderer->GetTextSize(theHighlightedString.toQString());
+ theTextSizeX = textSize.width();
+ theTextSizeY = textSize.height();
+ theSelectionRect.position.x = ::dtol(theUpperLeftX);
+ theSelectionRect.size = CPt(::dtol(theTextSizeX), ::dtol(theTextSizeY) + 2);
+
+ // If the caret is at the far right of the selection, shrink the highlight rect by one
+ // pixel. This is so that you can actually see the blinking cursor on the right edge.
+ if (static_cast<long>(m_Caret.position) == GetSelectionRight())
+ --theSelectionRect.size.x;
+
+ inRenderer->FillSolidRect(theSelectionRect, CSystemPreferences::GetSelectedItemColor());
+
+ // Draw the actual text in the highlight color
+ if (!m_BoldText) {
+ inRenderer->DrawText(theUpperLeftX, theUpperLeftY, theHighlightedString.toQString(),
+ sizeRect, CSystemPreferences::GetSelectedTextColor());
+ } else {
+ inRenderer->DrawBoldText(theUpperLeftX, theUpperLeftY, theHighlightedString.toQString(),
+ sizeRect, CSystemPreferences::GetSelectedTextColor());
+ }
+
+ // Move the upper left corner
+ theUpperLeftX += theTextSizeX;
+ }
+
+ if (theLastPart.Length() > 0) {
+ if (!m_BoldText) {
+ inRenderer->DrawText(theUpperLeftX, theUpperLeftY, theLastPart.toQString(), sizeRect,
+ m_TextColor);
+ } else {
+ inRenderer->DrawBoldText(theUpperLeftX, theUpperLeftY, theLastPart.toQString(), sizeRect,
+ m_TextColor);
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Draws the caret out to the screen.
+ *
+ * @param inRenderer Renderer to draw the text out to
+ */
+void CTextEdit::DrawCaret(CRenderer *inRenderer)
+{
+ if (m_Caret.show && m_Caret.visible) {
+ CRct theTextRect = GetTextRect();
+ CPt theCaretPosition(theTextRect.position.x, 0);
+
+ float theSizeX;
+ float theSizeY;
+
+ const auto textSize = inRenderer->GetTextSize(GetDisplayString().
+ Extract(0, m_Caret.position).toQString());
+ theSizeX = textSize.width();
+ theSizeY = textSize.height();
+
+ theCaretPosition.x = ::dtol(theSizeX) + theTextRect.position.x;
+
+ // Adjust the caret postion so that it draws just before the current character, instead of
+ // on the current character
+ theCaretPosition.x--;
+
+ theCaretPosition += m_ScrollAmount;
+
+ inRenderer->PushPen(::CColor(m_Caret.color));
+ theCaretPosition.y = theCaretPosition.y;
+ inRenderer->MoveTo(theCaretPosition);
+ inRenderer->LineTo(theCaretPosition.x, GetSize().y - 1);
+ inRenderer->PopPen();
+ }
+}
+
+//=============================================================================
+/**
+ * Inserts the specified character into the control
+ */
+bool CTextEdit::InsertChar(unsigned int inChar)
+{
+ bool theReturnValue = false;
+ Q3DStudio::CString theTempString = m_DisplayString;
+
+ if (!m_Caret.show)
+ SelectAllText();
+
+ long theLeftSelection = GetSelectionLeft();
+ long theRightSelection = GetSelectionRight();
+ long thePosition = m_Caret.position;
+
+ if (theLeftSelection != theRightSelection) {
+ Q3DStudio::CString theDisplayString = m_DisplayString;
+
+ theTempString = "";
+ theTempString = theDisplayString.Extract(0, theLeftSelection);
+ theTempString += theDisplayString.Extract(theRightSelection,
+ theDisplayString.Length() - theRightSelection);
+ thePosition = theLeftSelection;
+ }
+
+ if (CanAcceptChar(theTempString, inChar, thePosition)
+ && theTempString.Length() < GetMaxLength()) {
+ DeleteCurrentSelection(false);
+
+ Q3DStudio::CString theChar = static_cast<wchar_t>(inChar);
+ theTempString.Insert(m_Caret.position, theChar);
+ SetData(theTempString);
+
+ // Advance the caret past the character that we just typed
+ MoveCaretTo(m_Caret.position + 1);
+
+ ResetBlinkingCursor();
+ theReturnValue = true;
+ }
+
+ return theReturnValue;
+}
+
+//=============================================================================
+/**
+ * Handles character input from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CTextEdit::OnChar(const QString &inChar, Qt::KeyboardModifiers inFlags)
+{
+ // 0x7F is a character generated by the delete key
+ if (inChar.size() == 0 || inChar[0].unicode() == 0x7F)
+ return false;
+
+ bool theReturnValue = false;
+
+ if (!m_CommandHandler->OnChar(QKeySequence::fromString(inChar)[0], 1, inFlags)) {
+ // Do not process the character if control is down, this will allow
+ // app's hotkey to process application hotkeys.
+ if (!(inFlags & Qt::ControlModifier)) {
+ // Always return true whenever it captures focus and tries insert character.
+ // Though InsertChar fails, this is still getting the focus and have processed the
+ // event, return true so that no other controls will handle it
+ // Refer to Bug 897
+ InsertChar(inChar[0].unicode());
+ theReturnValue = true;
+ }
+ } else
+ theReturnValue = true;
+
+ return theReturnValue;
+}
+
+//=============================================================================
+/**
+ * Virtual function that determines if a key recieved via OnChar can actually be
+ * processed. Sub-classes should override this implementation and provide their
+ * own function for determining what characters they accept.
+ *
+ * @param inCheckString the string that is to be checked (in case a lenght requirement is exceeded)
+ * @param inChar the character that was pressed
+ * @param inPosition character index where we are attempting to insert inChar into inCheckString
+ * @return true if this control can accept the character into the string, otherwise false
+ */
+bool CTextEdit::CanAcceptChar(const Q3DStudio::CString &inCheckString, unsigned int inChar,
+ unsigned int inPosition)
+{
+ Q_UNUSED(inCheckString);
+ Q_UNUSED(inPosition);
+
+ bool theAcceptFlag = false;
+
+ if (inChar >= 32 && inChar <= 255 && !m_IsReadOnly)
+ theAcceptFlag = true;
+
+ return theAcceptFlag;
+}
+
+//=============================================================================
+/**
+ * Handles key presses. OnKeyDown events pass in non-printable characters
+ * such as the arrow keys or Enter. Some of these keys will be handled by this
+ * control.
+ *
+ * @param inChar the character that was pressed
+ * @return true if the character was consumed by this control, otherwise false
+ */
+bool CTextEdit::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool theHandledFlag = false;
+ if (!m_IsReadOnly) {
+ if (!m_CommandHandler->OnChar(inChar, 1, inFlags)) {
+ theHandledFlag = HandleSpecialChar(inChar, inFlags);
+
+ // Enter key commits the last change
+ if (inChar == Qt::Key_Enter)
+ FireCommitEvent();
+
+ ResetBlinkingCursor();
+ Invalidate();
+ } else {
+ theHandledFlag = true;
+ }
+ }
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * Handles any non-character keys that were pressed and need to be handled.
+ *
+ * @param inChar The key that was pressed
+ * @param inFlags Indicates which modifier keys were down when event occurred
+ * @return true if this function handled the character, otherwise false
+ */
+bool CTextEdit::HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool theHandledFlag = true;
+ bool isShiftDown = inFlags & CHotKeys::MODIFIER_SHIFT ? true : false;
+ // bool isControlDown = inFlags & CHotKeys::MODIFIER_CONTROL ? true : false;
+
+ switch (inChar) {
+ case Qt::Key_Left:
+ // If we are cancelling selection, then we need to change how the caret moves
+ if (HasSelectedText() && !isShiftDown)
+ MoveCaretTo(GetSelectionLeft(), isShiftDown);
+ // Otherwise, just move the caret left by one character
+ else
+ MoveCaretTo(m_Caret.position - 1, isShiftDown);
+ break;
+
+ case Qt::Key_Right:
+ // If we are cancelling selection, then we need to change how the caret moves
+ if (HasSelectedText() && !isShiftDown)
+ MoveCaretTo(GetSelectionRight(), isShiftDown);
+ // Otherwise, just move the caret right by one character
+ else
+ MoveCaretTo(m_Caret.position + 1, isShiftDown);
+ break;
+
+ case Qt::Key_Home:
+ MoveCaretTo(0, isShiftDown);
+ break;
+
+ case Qt::Key_End:
+ MoveCaretTo((long)m_CharWidths.size(), isShiftDown);
+ break;
+
+ case Qt::Key_Backspace:
+ if (HasSelectedText())
+ DeleteCurrentSelection();
+ else
+ DeleteCharacter(false);
+ break;
+
+ case Qt::Key_Delete: {
+ if (HasSelectedText())
+ DeleteCurrentSelection();
+ else
+ DeleteCharacter(true);
+ } break;
+ case Qt::Key_Return:
+ EnterText(true);
+ break;
+
+ default:
+ theHandledFlag = false;
+ break;
+ }
+
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * Handles mouse down events. Places the caret in the appropriate place based
+ * upon the mouse position.
+ *
+ * @param inLocation Location of the mouse at the time the click occurred
+ * @param inFlags Flags indicating various key states (not used)
+ * @return true (this control always handles mouse down events)
+ */
+void CTextEdit::OnMouseOver(CPt inLocation, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inLocation, inFlags);
+
+ if (!m_IsReadOnly)
+ setCursorIfNotSet(CMouseCursor::CURSOR_IBEAM);
+}
+
+//==============================================================================
+/**
+ * Handles mouse down events. Places the caret in the appropriate place based
+ * upon the mouse position.
+ *
+ * @param inLocation Location of the mouse at the time the click occurred
+ * @param inFlags Flags indicating various key states (not used)
+ * @return true (this control always handles mouse down events)
+ */
+bool CTextEdit::OnMouseDown(CPt inLocation, Qt::KeyboardModifiers inFlags)
+{
+ bool theReturnValue = CControl::OnMouseDown(inLocation, inFlags);
+
+ if (!m_IsReadOnly) {
+ // If the text is scrolled, we'll need to adjust for that
+ inLocation -= m_ScrollAmount;
+
+ m_StartDragPt = inLocation;
+ m_MouseIsDown = true;
+
+ ClearSelection();
+
+ MoveCaretTo(ConvertPositionToIndex(inLocation), false, false);
+
+ // Turn the caret on
+ m_Caret.show = true;
+ m_Caret.visible = true;
+
+ ResetBlinkingCursor();
+
+ m_TimerConnection =
+ ITickTock::GetInstance().AddTimer(1000, true, std::bind(&CTextEdit::OnTimer, this),
+ "CTextEdit::OnMouseDown::" + GetDisplayString());
+
+ theReturnValue = true;
+ }
+
+ return theReturnValue;
+}
+
+//==============================================================================
+/**
+ * Called when the mouse button is released. Disables any dragging that may
+ * have been occurring.
+ *
+ * @param inPoint location of the mouse when the event occurred
+ * @param inFlags various flags about other key states
+ */
+void CTextEdit::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_MouseIsDown = false;
+ m_StartDragPt = CPt(0, 0);
+ CControl::OnMouseUp(inPoint, inFlags);
+}
+
+//==============================================================================
+/**
+ * Handles mouse movement. If the left mouse button is down and the user is
+ * dragging the mouse over the text in the edit box, the appropriate text is
+ * selected.
+ *
+ * @param inPoint location of the mouse.
+ * @param inFlags not used
+ */
+void CTextEdit::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ if (!m_IsReadOnly)
+ setCursorIfNotSet(CMouseCursor::CURSOR_IBEAM);
+
+ if (m_MouseIsDown && !m_IsReadOnly) {
+ // The point was scrolled in OnMouseDown, so we have to scroll it when we drag as well so
+ // that it lines up
+ inPoint -= m_ScrollAmount;
+
+ // Figure out what character index inPoint.x translates to
+ long theCharOffset = ConvertPositionToIndex(inPoint);
+
+ MoveCaretTo(theCharOffset, true);
+ }
+}
+
+//==============================================================================
+/**
+ * Converts a given pixel position on the screen into an index into the display
+ * string.
+ *
+ * @param inPosition the point to be converted
+ * @return the character index derived from inPostion
+ */
+long CTextEdit::ConvertPositionToIndex(CPt inPosition)
+{
+ CRct theTextRect = GetTextRect();
+ long theIndex = 0;
+ long theTotalPixels = theTextRect.position.x;
+
+ // NOTE: We technically only care about the x-position of the point, so just ignore
+ // inPosition.y if it is not within the text area. This may need to be changed in
+ // the future (perhaps if vertical alignment is implemented).
+ CPt thePositionNoY = CPt(inPosition.x, theTextRect.position.y);
+
+ // If the point is within the text area
+ if (theTextRect.IsInRect(thePositionNoY)) {
+ // Figure out which characters it falls in between
+ for (TCharLengths::iterator thePos = m_CharWidths.begin(); thePos != m_CharWidths.end();
+ ++thePos) {
+ theTotalPixels += ::dtol(*thePos);
+ if (inPosition.x < theTotalPixels)
+ break;
+ theIndex++;
+ }
+ }
+ // If the point is to the left of the text, default the index to the beginning of the text
+ else if (theTextRect.position.x > thePositionNoY.x) {
+ theIndex = 0;
+ }
+ // Otherwise, the point must be to the right of the text, so move the index to the end of the
+ // text
+ else {
+ theIndex = (long)m_CharWidths.size();
+ }
+
+ return theIndex;
+}
+
+//==============================================================================
+/**
+ * Converts a given character index into a pixel postion on the screen. This
+ * function is the inverse of ConvertPositionToIndex.
+ * @param inIndex the character index to be converted
+ * @return the x-position (in pixels) of character at inIndex
+ */
+long CTextEdit::ConvertIndexToPosition(long inIndex)
+{
+ long theXPixelOffset = GetTextRect().position.x;
+ long theCurrentIndex = 0;
+
+ for (TCharLengths::iterator thePos = m_CharWidths.begin(); thePos != m_CharWidths.end();
+ ++thePos) {
+ theCurrentIndex++;
+
+ if (theCurrentIndex > inIndex) {
+ break;
+ }
+
+ theXPixelOffset += ::dtol(*thePos);
+ }
+
+ return theXPixelOffset;
+}
+
+//==============================================================================
+/**
+ * Handles double clicks on the control. Highlights all the text in the control.
+ *
+ * @param inPoint Not used
+ * @param inFlags Not used
+ * @return true (this control always handles double clicks)
+ */
+bool CTextEdit::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theHandledFlag = false;
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+ if (!m_IsReadOnly) {
+ SelectAllText();
+
+ if (GetParent())
+ GetParent()->GrabFocus(this);
+
+ theHandledFlag = true;
+ }
+
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * Selects all the text in the display string
+ */
+void CTextEdit::SelectAllText()
+{
+ if (!m_IsReadOnly) {
+ MoveCaretTo(GetDisplayString().Length());
+ SetSelection(0, GetDisplayString().Length());
+ // Turn the caret on
+ m_Caret.show = true;
+ }
+}
+
+//==============================================================================
+/**
+ * Marks the string contained by this control as dirty so that it is reevaluated
+ * the next time that the control draws itself. Also clears out any selection
+ * that may have been present, resetting the selection to the caret position.
+ *
+ * @param inDirty true to mark this control as dirty, which causes the string to be redrawn
+ * false to mark the control as not needing to reevaluate its text during next draw cycle
+ */
+void CTextEdit::SetDirty(bool inDirty)
+{
+ m_IsStringDirty = inDirty;
+ if (m_IsStringDirty) {
+ m_NeedsCommit = true;
+ ClearSelection();
+ Invalidate();
+ }
+}
+
+//==============================================================================
+/**
+ * @return true if the string is currently dirty and needs to be reevalutated.
+ */
+bool CTextEdit::IsDirty() const
+{
+ return m_IsStringDirty;
+}
+
+//==============================================================================
+/**
+ * Determines the width in pixels of each letter in the display string.
+ *
+ * @param inRenderer The current renderer being drawn to (since only the renderer
+ * knows how to calculate the pixel width of the text in the current font).
+ * @return The total width in pixels of the entire display string
+ */
+float CTextEdit::CalculateCharWidths(CRenderer *inRenderer)
+{
+ Q3DStudio::CString theString = GetDisplayString();
+
+ m_CharWidths.clear();
+ m_TotalCharWidth = 0;
+
+ for (long theCharIndex = 0; theCharIndex < theString.Length(); ++theCharIndex) {
+ const auto textSize = inRenderer->GetTextSize(theString.Extract(theCharIndex, 1).toQString());
+ m_CharWidths.push_back(textSize.width());
+ m_TotalCharWidth += textSize.width();
+ }
+
+ return m_TotalCharWidth;
+}
+
+//==============================================================================
+/**
+ * Sets the number of pixels worth of buffer space to place at the left and
+ * right ends of the edit box.
+ */
+void CTextEdit::SetBufferLength(long inLength)
+{
+ m_BufferLength = inLength;
+}
+
+//==============================================================================
+/**
+ * Sets the maximum length of text
+ */
+void CTextEdit::SetMaxLength(long inLength)
+{
+ m_MaxLength = inLength;
+}
+
+//==============================================================================
+/**
+ * Allows you to turn the background fill on and off.
+ * @param inFillBackground true to fill the background color when drawing, false to turn it off
+ */
+void CTextEdit::SetFillBackground(bool inFillBackground)
+{
+ if (m_FillBackground == inFillBackground)
+ return;
+ m_FillBackground = inFillBackground;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Called when this control loses focus. Hides the caret, clears the current
+ * selection and invalidates the control so that it will get redrawn.
+ */
+void CTextEdit::OnLoseFocus()
+{
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+ m_Caret.show = false;
+ m_ScrollAmount.x = 0;
+ ClearSelection();
+ m_IsStringDirty = true;
+ FireCommitEvent();
+ resetCursor();
+ CControl::OnLoseFocus();
+}
+
+//==============================================================================
+/**
+ * Called when this control gains focus. Shows the caret, clears the current
+ * selection and invalidates the control so that it will get redrawn.
+ */
+void CTextEdit::OnGainFocus()
+{
+ if (!m_MouseIsDown) {
+ m_Caret.position = 0;
+ m_Caret.show = !m_IsReadOnly;
+ if (!m_IsReadOnly)
+ SelectAllText();
+
+ if (m_Caret.show) {
+ ResetBlinkingCursor();
+ m_TimerConnection = ITickTock::GetInstance().AddTimer(
+ 1000, true, std::bind(&CTextEdit::OnTimer, this),
+ "CTextEdit::OnGainFocus::" + GetDisplayString());
+ }
+ }
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Override to set whether or not this can gain focus.
+ */
+bool CTextEdit::CanGainFocus()
+{
+ return !m_IsReadOnly;
+}
+
+//==============================================================================
+/**
+ * If this control has focus, it's display may have a different string then the
+ * actual underlying string. This enables us to revert back to the original
+ * string if the user presses the Escape while editing the text. So this function
+ * should be used to obtain the proper string to work with. If this control has
+ * focus, the internally stored string is returned, otherwise the CTextEdit asks
+ * its child class to reevaluate the string.
+ *
+ * @return the string being displayed in the edit box
+ */
+Q3DStudio::CString CTextEdit::GetDisplayString()
+{
+ // If this control does not have focus, we should just reevaluate the string
+ if (GetParent()) {
+ if (!GetParent()->HasFocus(this))
+ SetDisplayString(GetString(), false);
+ }
+
+ // If the edit box is read only, then add the pre and postfix,
+ // otherwise, just the display string is drawn while editing
+ Q3DStudio::CString theCompleteString = m_DisplayString;
+ if (m_IsReadOnly) {
+ theCompleteString = m_Prefix + m_DisplayString + m_Postfix;
+ }
+
+ return theCompleteString;
+}
+
+//==============================================================================
+/**
+ * Invokied in a Draw call. Derived class can override to do their own background fill
+ */
+void CTextEdit::DoFillBackground(CRenderer *inRenderer)
+{
+ if ((m_FillBackground && m_UseBGColorReadOnly) || !m_IsReadOnly) {
+ // Fill the interior of the edit box
+ ::CColor theFillColor = m_BackgroundColorNoFocus;
+ // If this control has focus
+ if (GetParent()) {
+ // Lighten the fill color
+ if (GetParent()->HasFocus(this))
+ theFillColor = m_BackgroundColorFocus;
+ }
+ inRenderer->FillSolidRect(QRect(0, 0, GetSize().x, GetSize().y), theFillColor);
+ }
+}
+
+//==============================================================================
+/**
+ * Selects the specified text within the edit box. If inFirstIndex == inLastIndex
+ * no text is selected. If the indices specified do not fall within the size of
+ * the actual text, no selection occurs.
+ *
+ * @param inFirstIndex Index of the first letter to be selected
+ * @param inLastIndex Index of the last letter to be selected
+ */
+void CTextEdit::SetSelection(long inFirstIndex, long inLastIndex)
+{
+ if (inFirstIndex < 0 || inLastIndex > GetDisplayString().Length()) {
+ ClearSelection();
+ } else {
+ if (m_SelectionStart != inFirstIndex || m_SelectionEnd != inLastIndex)
+ Invalidate();
+
+ m_SelectionStart = inFirstIndex;
+ m_SelectionEnd = inLastIndex;
+ }
+
+ m_Caret.position = m_SelectionEnd;
+}
+
+//==============================================================================
+/**
+ * Sets this CTextEdit as either read-only or read/write. When a CTextEdit
+ * control is set to read-only, users cannot input data via the keyboard,
+ * and the text cannot be selected.
+ *
+ * @param inReadOnly true to specify static, non-editable, non-selectable text
+ */
+void CTextEdit::SetReadOnly(bool inReadOnly)
+{
+ if (inReadOnly != m_IsReadOnly)
+ Invalidate();
+
+ m_IsReadOnly = inReadOnly;
+}
+
+//==============================================================================
+/**
+ * @return true if this control should not allow the string to be edited
+ */
+bool CTextEdit::IsReadOnly()
+{
+ return m_IsReadOnly;
+}
+
+//==============================================================================
+/**
+ * Sets the color of the text dispalyed by this control.
+ * @param inColor Color of the text to be displayed
+ */
+void CTextEdit::SetTextColor(::CColor inColor)
+{
+ if (m_TextColor == inColor)
+ return;
+
+ m_TextColor = inColor;
+ m_Caret.color = inColor;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * @return The current text color
+ */
+::CColor CTextEdit::GetTextColor()
+{
+ return m_TextColor;
+}
+
+//==============================================================================
+/**
+ * Sets the flag that says whether or not to use the m_BackgroundColorFocus
+ * and m_BackgroundColorNoFocus user colors to draw the background color
+ * of the text box.
+ */
+void CTextEdit::SetUseBGColorReadOnly(bool inUseBGColorReadOnly)
+{
+ m_UseBGColorReadOnly = inUseBGColorReadOnly;
+}
+
+//==============================================================================
+/**
+ * Sets the color of the background of this control when it does not have focus.
+ * Note that this color only has an effect when SetFillBackground has been
+ * called with true.
+ * @param inColor Color of the background when the control does not have focus
+ * @return The previous background color
+ */
+::CColor CTextEdit::SetBGColorNoFocus(::CColor inColor)
+{
+ ::CColor theOldColor = m_BackgroundColorNoFocus;
+ m_BackgroundColorNoFocus = inColor;
+ return theOldColor;
+}
+
+//==============================================================================
+/**
+ * Sets the color of the background of this control when it has focus.
+ * Note that this color only has an effect when SetFillBackground has been
+ * called with true.
+ * @param inColor Color of the background when the control has focus
+ * @return The previous background color
+ */
+::CColor CTextEdit::SetBGColorFocus(::CColor inColor)
+{
+ ::CColor theOldColor = m_BackgroundColorFocus;
+ m_BackgroundColorFocus = inColor;
+ return theOldColor;
+}
+
+void CTextEdit::AddCommitListener(CCommitDataListener *inListener)
+{
+ m_CommitListeners.AddListener(inListener);
+}
+
+void CTextEdit::RemoveCommitListener(CCommitDataListener *inListener)
+{
+ m_CommitListeners.RemoveListener(inListener);
+}
+
+//==============================================================================
+/**
+ * Fires commit events on Listeners. Only fires a commit event if it has not
+ * been fired previously.
+ */
+void CTextEdit::FireCommitEvent()
+{
+ // If we are not currently processing a command to fire commit events to listeners
+ if (m_NeedsCommit) {
+ // Fire the event and undirty the text
+ m_CommitListeners.FireEvent(&CCommitDataListener::OnSetData, this);
+ m_NeedsCommit = false;
+ }
+}
+
+void CTextEdit::AddChangeListener(IChangeDataListener *inListener)
+{
+ m_ChangeListeners.AddListener(inListener);
+}
+
+void CTextEdit::RemoveChangeListener(IChangeDataListener *inListener)
+{
+ m_ChangeListeners.RemoveListener(inListener);
+}
+
+//==============================================================================
+/**
+ * Fires change events on Listeners.
+ */
+void CTextEdit::FireChangeEvent(const Q3DStudio::CString &inOldString,
+ const Q3DStudio::CString &inNewString)
+{
+ m_ChangeListeners.FireEvent(&IChangeDataListener::OnChangeData, inOldString, inNewString);
+}
+
+void CTextEdit::AddEnterListener(ITextEditEnterListener *inListener)
+{
+ m_EnterListeners.AddListener(inListener);
+}
+
+void CTextEdit::RemoveEnterListener(ITextEditEnterListener *inListener)
+{
+ m_EnterListeners.RemoveListener(inListener);
+}
+
+//==============================================================================
+/**
+ * Fires enter events on listeners.
+ */
+void CTextEdit::FireEnterEvent()
+{
+ m_EnterListeners.FireEvent(&ITextEditEnterListener::OnEnter, this);
+}
+
+//==============================================================================
+/**
+ * Delete any text that is currently selected.
+ * If there is no text selected this will do nothing.
+ */
+void CTextEdit::DeleteCurrentSelection(bool inFireEvent /*=true*/)
+{
+ if (HasSelectedText()) {
+ Q3DStudio::CString theTempString = m_DisplayString;
+ theTempString.Delete(GetSelectionLeft(), GetSelectionRight() - GetSelectionLeft());
+
+ m_Caret.position = GetSelectionLeft();
+
+ ClearSelection();
+
+ // @see DeleteCharacter( )
+ SetData(theTempString, inFireEvent);
+ SetDisplayString(theTempString, inFireEvent);
+ SetDirty(true);
+ }
+}
+
+//==============================================================================
+/**
+ * Delete a character to the left or right of the cursor.
+ * This will delete a single character on the specified side of the cursor.
+ * @param inOnRight true if the character to be deleted is on the right of the cursor.
+ */
+void CTextEdit::DeleteCharacter(bool inOnRight)
+{
+ if (!inOnRight) {
+ if (m_Caret.position == 0)
+ return;
+ else
+ m_Caret.position--;
+ }
+
+ Q3DStudio::CString theTempString = m_DisplayString;
+ theTempString.Delete(m_Caret.position, 1);
+ // Since SetData is overloaded by other types of text edit controls,
+ // it has to be called first so that subclasses have a chance to set their internal values
+ // before calling SetDisplayString, which fires a DataChanged event.
+ // @see DeleteCurrentSelection( ). EnterText got it right.
+ SetData(theTempString);
+ SetDisplayString(theTempString);
+ SetDirty(true);
+}
+
+//==============================================================================
+/**
+ * Move the caret to the specified position in the string.
+ * If inSelect then this will select the text from the selection start position
+ * to the new position. If there is no current selection start then this will
+ * select from the old position to the new position. If select is false and there
+ * is selected text the selection will be cleared.
+ * @param inPosition the position to move to in the string.
+ * @param inSelect true if the selection should be changed when moving.
+ * @param inScroll true if moving the caret should allow the text to scroll within the box
+ */
+void CTextEdit::MoveCaretTo(long inPosition, bool inSelect /*=false*/, bool inScroll /*=true*/)
+{
+ if (inPosition < 0)
+ inPosition = 0;
+ if (inPosition > GetDisplayString().Length())
+ inPosition = GetDisplayString().Length();
+
+ // Only perform the operation if the caret is actually going to move, or the selection is going
+ // to change
+ if ((static_cast<unsigned long>(inPosition) != m_Caret.position)
+ || (HasSelectedText() && !inSelect)) {
+ if (inSelect) {
+ if (!HasSelectedText())
+ m_SelectionStart = m_Caret.position;
+ m_SelectionEnd = inPosition;
+ } else
+ ClearSelection();
+
+ m_Caret.position = inPosition;
+
+ // Scroll if necessary
+ if (inScroll) {
+ long theCaretPixelPos = ConvertIndexToPosition(m_Caret.position);
+ if (theCaretPixelPos + m_ScrollAmount.x <= 0)
+ Scroll(m_ScrollAmount.x - (theCaretPixelPos + m_ScrollAmount.x) + m_BufferLength);
+ else if (theCaretPixelPos + m_ScrollAmount.x > GetSize().x)
+ Scroll(-(theCaretPixelPos - GetSize().x + m_BufferLength));
+ }
+
+ Invalidate();
+ }
+}
+
+//==============================================================================
+/**
+ * Effectively scrolls the text in this edit box.
+ * Enables you to set the number of pixels to offset the text and caret by when
+ * they are drawn to the screen. If inPixel = 0, no scrolling takes place. If
+ * inPixel < 0, text is scrolled to the left. If inPixel > 0, text is scrolled
+ * to the right.
+ * @param inPixel specifies the horizontal scroll in pixels
+ */
+void CTextEdit::Scroll(long inPixel)
+{
+ m_ScrollAmount.x = inPixel;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Pushes the display string back down to the control and optionally highlights
+ * the entire string. Called when the user clicks the enter button in an edit
+ * box.
+ * @param inHighlight true to highlight the string
+ */
+void CTextEdit::EnterText(bool inHighlight)
+{
+ SetData(m_DisplayString);
+ SetDisplayString(GetString());
+
+ if (inHighlight)
+ SetSelection(0, m_DisplayString.Length());
+
+ Invalidate();
+
+ FireEnterEvent();
+}
+
+//==============================================================================
+/**
+ * Check to see if there is any text selected.
+ * @return true if there is any text selected.
+ */
+bool CTextEdit::HasSelectedText()
+{
+ return m_SelectionStart != m_SelectionEnd;
+}
+
+//==============================================================================
+/**
+ * Get the index of the leftmost selected character.
+ * @return the index, invalid if no text is selected.
+ */
+long CTextEdit::GetSelectionLeft()
+{
+ return m_SelectionStart < m_SelectionEnd ? m_SelectionStart : m_SelectionEnd;
+}
+
+//==============================================================================
+/**
+ * Get the index of the rightmost selected character.
+ * @return the index, invalid if no text is selected.
+ */
+long CTextEdit::GetSelectionRight()
+{
+ return m_SelectionStart > m_SelectionEnd ? m_SelectionStart : m_SelectionEnd;
+}
+
+//==============================================================================
+/**
+ * Clear any text from being selected, this will not delete the text, only deselect it.
+ */
+void CTextEdit::ClearSelection()
+{
+ m_SelectionStart = 0;
+ m_SelectionEnd = 0;
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Re-get the display string.
+ */
+void CTextEdit::ReloadData()
+{
+ SetDisplayString(GetString());
+}
+
+//==============================================================================
+/**
+ * Supply a prefix to be displayed when the string is not being edited (e.g. "#").
+ */
+void CTextEdit::SetPrefix(const Q3DStudio::CString &inPrefix)
+{
+ m_Prefix = inPrefix;
+}
+
+//==============================================================================
+/**
+ * Supply a postfix to be displayed when the string is not being edited (e.g. " inches").
+ */
+void CTextEdit::SetPostfix(const Q3DStudio::CString &inPostfix)
+{
+ m_Postfix = inPostfix;
+}
+
+//==============================================================================
+/**
+ * Handles the hotkey commands
+ */
+
+void CTextEdit::RegisterCommands()
+{
+ m_CommandHandler->RegisterKeyEvent(new CDynHotKeyConsumer<CTextEdit>(this, &CTextEdit::CopyText),
+ Qt::ControlModifier, Qt::Key_C);
+ m_CommandHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CTextEdit>(this, &CTextEdit::PasteText), Qt::ControlModifier,
+ Qt::Key_V);
+ m_CommandHandler->RegisterKeyEvent(new CDynHotKeyConsumer<CTextEdit>(this, &CTextEdit::CutText),
+ Qt::ControlModifier, Qt::Key_X);
+ m_CommandHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CTextEdit>(this, &CTextEdit::SelectAllText),
+ Qt::ControlModifier, Qt::Key_A);
+}
+
+//==============================================================================
+/**
+ * Copies selected text to clipboard. If nothing is selected, everything is copied.
+ * @return true if text was copied
+ */
+bool CTextEdit::CopyText()
+{
+ if (!m_IsReadOnly) {
+ if (HasSelectedText())
+ CStudioClipboard::CopyTextToClipboard(m_DisplayString.Extract(
+ GetSelectionLeft(), GetSelectionRight() - GetSelectionLeft()).toQString());
+ else
+ CStudioClipboard::CopyTextToClipboard(m_DisplayString.toQString());
+ }
+ return !m_IsReadOnly;
+}
+
+//==============================================================================
+/**
+ * Pastes text to clipboard
+ */
+
+bool CTextEdit::PasteText()
+{
+ Q3DStudio::CString theText = Q3DStudio::CString
+ ::fromQString(CStudioClipboard::GetTextFromClipboard());
+ bool theHandledFlag = !m_IsReadOnly && CanPaste() && !theText.IsEmpty();
+
+ theHandledFlag &= theText.Length() <= GetMaxLength();
+ if (HasSelectedText()) {
+ theHandledFlag &=
+ m_DisplayString.Length() - (GetSelectionRight() - GetSelectionLeft()) + theText.Length()
+ <= GetMaxLength();
+ } else {
+ theHandledFlag &= m_DisplayString.Length() + theText.Length() <= GetMaxLength();
+ }
+
+ if (theHandledFlag) {
+ if (!m_Caret.show) {
+ SetData(theText);
+ } else {
+ Q3DStudio::CString theNewString(m_DisplayString);
+
+ if (HasSelectedText()) {
+ theNewString.Delete(GetSelectionLeft(), GetSelectionRight() - GetSelectionLeft());
+ m_Caret.position = Q3DStudio::MIN(GetSelectionLeft(), GetSelectionRight());
+ }
+ theNewString.Insert(m_Caret.position, theText);
+
+ SetData(theNewString);
+ m_Caret.position += theText.Length();
+ }
+ SetDirty(true);
+ }
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * Cuts text to clipboard
+ */
+
+bool CTextEdit::CutText()
+{
+ bool theReturn = false;
+
+ if (CopyText()) {
+ DeleteCurrentSelection();
+ theReturn = true;
+ }
+
+ return theReturn;
+}
+
+//==============================================================================
+/**
+ * Reloads the display string with the given string
+ * @param inString The string to be set
+ */
+void CTextEdit::SetDisplayString(const Q3DStudio::CString &inString, bool inFireEvent /*= true*/)
+{
+ if (m_DisplayString != inString) {
+ Q3DStudio::CString theOldString = m_DisplayString;
+ m_DisplayString = inString;
+
+ if (inFireEvent)
+ FireChangeEvent(theOldString, m_DisplayString);
+
+ m_IsStringDirty = true;
+ }
+}
+
+//==============================================================================
+/**
+ * The buffer length is the number of pixels between where the control starts
+ * and where the text begins drawing (in the case of left alignment). In the
+ * case of right alignment it's the number of pixels on the right side of the
+ * control where the text ends before the actual edge of the control. Provided
+ * for sub classes.
+ */
+long CTextEdit::GetBufferLength()
+{
+ return m_BufferLength;
+}
+
+//==============================================================================
+/**
+ * The maximum allowed length of text
+ */
+long CTextEdit::GetMaxLength()
+{
+ return m_MaxLength;
+}
+
+//==============================================================================
+/**
+ * Displays the Copy/Cut/Paste context menu on right mouse down
+ */
+void CTextEdit::DisplayContextMenu(CPt inPt)
+{
+ CTextEditContextMenu theMenu(this);
+ DoPopup(&theMenu, inPt);
+}
+
+//==============================================================================
+/**
+ * Handles the Right mouse down event. Displays a cut/copy/paste context menu.
+ * @param inPoint location of the mouse at time of the event (context menu's upper left corner will
+ * be here)
+ * @param inFlags state of the modifier keys at the time of the event
+ * @return true - this control always consumes the event
+ */
+bool CTextEdit::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Give children the chance to handle the event, otherwise display the context menu
+ if (!CControl::OnMouseRDown(inPoint, inFlags)) {
+ if (!IsReadOnly()) {
+ DisplayContextMenu(CPt(inPoint.x, GetSize().y));
+ return true;
+ }
+ }
+ return false;
+}
+
+//==============================================================================
+/**
+ * Periodic callback to make the caret blink.
+ */
+void CTextEdit::OnTimer()
+{
+ m_Caret.visible = !m_Caret.visible;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Cancels the current timer and adds a new timer so that the cursor continues
+ * to blink. If the cursor is not currently visible, it is made visible.
+ */
+void CTextEdit::ResetBlinkingCursor()
+{
+ // Cancel the current timer
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+
+ // If the cursor is not visible, make it visible
+ if (!m_Caret.visible) {
+ m_Caret.visible = true;
+ Invalidate();
+ }
+
+ // Add the timer so that the cursor will blink back off after BLINKSPEED amount of time
+ m_TimerConnection =
+ ITickTock::GetInstance().AddTimer(BLINKSPEED, true, std::bind(&CTextEdit::OnTimer, this),
+ "CTextEdit::ResetBlinkingCursor::" + m_DisplayString);
+}
+
+//==============================================================================
+/**
+ * Set to true to indicate text should be drawn bold.
+ * @param inBold true to set text to bold
+ */
+void CTextEdit::SetBoldText(bool inBold)
+{
+ m_BoldText = inBold;
+}
diff --git a/src/Authoring/Studio/Controls/TextEdit.h b/src/Authoring/Studio/Controls/TextEdit.h
new file mode 100644
index 00000000..b34dd75b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEdit.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TEXT_EDIT_H
+#define INCLUDED_TEXT_EDIT_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "Control.h"
+#include "HotKeys.h"
+#include "StudioPreferences.h"
+#include "ITickTock.h"
+#include "Multicaster.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+class CRenderer;
+
+//==============================================================================
+/**
+ * @class CTextEdit Base class for textual edit fields in Studio (timeline, inspector).
+ */
+
+// Functors
+GENERIC_FUNCTOR_1(CCommitDataListener, OnSetData, CControl *);
+GENERIC_FUNCTOR_1(ITextEditEnterListener, OnEnter, CControl *);
+GENERIC_FUNCTOR_2(IChangeDataListener, OnChangeData, const Q3DStudio::CString &,
+ const Q3DStudio::CString &);
+
+class CTextEdit : public CControl
+{
+ struct SCaret
+ {
+ unsigned long position; ///< index of the character that the cursor is currently in front of
+ ///(zero is first character)
+ ::CColor color; ///< color of the cursor
+ bool show; ///< must be true in order for the cursor to show up (outside of blink state)
+ bool visible; ///< blinking state of the cursor (true if currently visible)
+ };
+
+ typedef std::vector<float> TCharLengths;
+
+public:
+ enum EAlignment { LEFT = 0, CENTER, RIGHT };
+
+ CTextEdit();
+ virtual ~CTextEdit();
+
+ DEFINE_OBJECT_COUNTER(CTextEdit)
+
+ virtual Q3DStudio::CString
+ GetString() = 0; ///< Returns the string to be displayed in the edit box
+
+ void Draw(CRenderer *inRenderer) override;
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inLocation, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnChar(const QString &inChar, Qt::KeyboardModifiers inModifiers) override;
+ bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inModifiers) override;
+ void OnGainFocus() override;
+ void OnLoseFocus() override;
+ bool CanGainFocus() override;
+ virtual bool CanAcceptChar(const Q3DStudio::CString &inCheckString, unsigned int inChar,
+ unsigned int inPosition);
+ void SetAlignment(EAlignment inAlignment);
+ void SetBufferLength(long inLength);
+ void SetMaxLength(long inLength);
+ void SetFillBackground(bool inFillBackground);
+ void SelectAllText();
+ void DisplayContextMenu(CPt inPt);
+ virtual bool CanPaste() = 0;
+
+ virtual void SetData(const Q3DStudio::CString &inData, bool inFireEvent = true) = 0;
+ virtual void ReloadData();
+ virtual void SetPrefix(const Q3DStudio::CString &inPrefix);
+ virtual void SetPostfix(const Q3DStudio::CString &inPostfix);
+
+ void SetReadOnly(bool inReadOnly);
+ bool IsReadOnly();
+ void SetTextColor(::CColor inColor);
+ ::CColor GetTextColor();
+ void SetUseBGColorReadOnly(bool inUseBGColorReadOnly);
+ ::CColor SetBGColorNoFocus(::CColor inColor);
+ ::CColor SetBGColorFocus(::CColor inColor);
+ void SetBoldText(bool inBold);
+
+ void AddCommitListener(CCommitDataListener *inListener);
+ void RemoveCommitListener(CCommitDataListener *inListener);
+ void FireCommitEvent();
+
+ void AddChangeListener(IChangeDataListener *inListener);
+ void RemoveChangeListener(IChangeDataListener *inListener);
+ void FireChangeEvent(const Q3DStudio::CString &inOldString,
+ const Q3DStudio::CString &inNewString);
+
+ void AddEnterListener(ITextEditEnterListener *inListener);
+ void RemoveEnterListener(ITextEditEnterListener *inListener);
+ void FireEnterEvent();
+
+ bool HasSelectedText();
+ long GetSelectionLeft();
+ long GetSelectionRight();
+ void ClearSelection();
+ virtual void SetSelection(long inFirstIndex, long inLastIndex);
+
+ void MoveCaretTo(long inPosition, bool inSelect = false, bool inScroll = true);
+ void Scroll(long inPixel);
+ virtual void EnterText(bool inHighlight);
+
+ virtual void RefreshDisplayFromData() = 0;
+
+ bool CopyText();
+ bool PasteText();
+ bool CutText();
+ bool IsDirty() const;
+
+ virtual void OnTimer();
+
+ // Public so that derived objects can query the value on change events
+ Q3DStudio::CString GetDisplayString();
+
+protected:
+ virtual void DoFillBackground(CRenderer *inRenderer);
+
+protected:
+ bool m_IsStringDirty;
+ bool m_NeedsCommit;
+ TCharLengths m_CharWidths;
+ EAlignment m_Alignment;
+ float m_TotalCharWidth;
+ SCaret m_Caret;
+ long m_BufferLength;
+ long m_MaxLength;
+ bool m_IsReadOnly;
+ bool m_MouseIsDown;
+ CPt m_StartDragPt;
+ CHotKeys *m_CommandHandler;
+ bool m_FillBackground;
+ ::CColor m_TextColor;
+ bool m_UseBGColorReadOnly; ///< Flag indicating that we should use the background colors in read
+ ///only boxes as well as read/write
+ ::CColor m_BackgroundColorNoFocus;
+ ::CColor m_BackgroundColorFocus;
+ Q3DStudio::CString m_DisplayString;
+ Q3DStudio::CString m_Prefix;
+ Q3DStudio::CString m_Postfix;
+ bool m_BoldText;
+
+ virtual void DeleteCurrentSelection(bool inFireEvent = true);
+ virtual void DeleteCharacter(bool inIsOnRight = false);
+ void RegisterCommands();
+ virtual void SetDirty(bool inDirty);
+ float CalculateCharWidths(CRenderer *inRenderer);
+ virtual bool HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags);
+ void DrawText(CRenderer *inRenderer);
+ void DrawCaret(CRenderer *inRenderer);
+ CRct GetTextRect();
+ long ConvertIndexToPosition(long inIndex);
+ long ConvertPositionToIndex(CPt inPosition);
+ void SetDisplayString(const Q3DStudio::CString &inString, bool inFireEvent = true);
+ long GetBufferLength();
+ long GetMaxLength();
+ void ResetBlinkingCursor();
+ bool InsertChar(unsigned int inChar);
+
+private:
+ CMulticaster<CCommitDataListener *> m_CommitListeners;
+ CMulticaster<IChangeDataListener *> m_ChangeListeners;
+ CMulticaster<ITextEditEnterListener *> m_EnterListeners;
+ long m_SelectionStart;
+ long m_SelectionEnd;
+ CPt m_ScrollAmount; ///< Pixel offset to scroll the text and caret by
+ std::shared_ptr<UICDM::ISignalConnection> m_TimerConnection;
+};
+
+#endif // INCLUDED_TEXT_EDIT_H
diff --git a/src/Authoring/Studio/Controls/TextEditContextMenu.cpp b/src/Authoring/Studio/Controls/TextEditContextMenu.cpp
new file mode 100644
index 00000000..12d6ede9
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEditContextMenu.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TextEditContextMenu.h"
+#include "TextEdit.h"
+#include "StudioClipboard.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+
+CTextEditContextMenu::CTextEditContextMenu(CTextEdit *inEditControl, QWidget *parent) : QMenu(parent)
+{
+ m_TextEditControl = inEditControl;
+
+ m_cutAction = new QAction(tr("Cut"), this);
+ connect(m_cutAction, &QAction::triggered, this, &CTextEditContextMenu::CutText);
+ addAction(m_cutAction);
+
+ m_copyAction = new QAction(tr("Copy"), this);
+ connect(m_copyAction, &QAction::triggered, this, &CTextEditContextMenu::CopyText);
+ addAction(m_copyAction);
+
+ m_pasteAction = new QAction(tr("Paste"), this);
+ connect(m_pasteAction, &QAction::triggered, this, &CTextEditContextMenu::PasteText);
+ addAction(m_pasteAction);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+
+CTextEditContextMenu::~CTextEditContextMenu()
+{
+}
+
+//==============================================================================
+/**
+ * returns true if 'cut' can be applied to the control
+ */
+bool CTextEditContextMenu::CanCutText()
+{
+ return true;
+}
+
+//==============================================================================
+/**
+ * Returns true if you can copy the text in the control
+ */
+bool CTextEditContextMenu::CanCopyText()
+{
+ return true;
+}
+
+//==============================================================================
+/**
+ * Returns true if you may paste text from the clipboard into the control
+ */
+bool CTextEditContextMenu::CanPasteText()
+{
+ return CStudioClipboard::CanPasteText();
+}
+
+//==============================================================================
+/**
+ * Calls the controls cuttext function
+ */
+void CTextEditContextMenu::CutText()
+{
+ m_TextEditControl->CutText();
+}
+
+//==============================================================================
+/**
+ * Calls the controls copy text function
+ */
+void CTextEditContextMenu::CopyText()
+{
+ m_TextEditControl->CopyText();
+}
+
+//==============================================================================
+/**
+ * Calls the controls paste text function
+ */
+void CTextEditContextMenu::PasteText()
+{
+ m_TextEditControl->PasteText();
+}
+
+void CTextEditContextMenu::showEvent(QShowEvent *event)
+{
+ m_cutAction->setEnabled(CanCutText());
+ m_copyAction->setEnabled(CanCopyText());
+ m_pasteAction->setEnabled(CanPasteText());
+
+ QMenu::showEvent(event);
+}
diff --git a/src/Authoring/Studio/Controls/TextEditContextMenu.h b/src/Authoring/Studio/Controls/TextEditContextMenu.h
new file mode 100644
index 00000000..981253be
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEditContextMenu.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TEXTEDITCONTEXTMENU_H
+#define INCLUDED_TEXTEDITCONTEXTMENU_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <QMenu>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CTextEdit;
+
+//==============================================================================
+/**
+ * @class CTextEditContextMenu
+ */
+
+class CTextEditContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ CTextEditContextMenu(CTextEdit *inEditControl, QWidget *parent = nullptr);
+ ~CTextEditContextMenu();
+
+protected Q_SLOTS:
+ void CutText();
+ void CopyText();
+ void PasteText();
+
+protected:
+ void showEvent(QShowEvent *event) override;
+
+ bool CanCutText();
+ bool CanPasteText();
+ bool CanCopyText();
+
+ CTextEdit *m_TextEditControl;
+ QAction *m_cutAction;
+ QAction *m_copyAction;
+ QAction *m_pasteAction;
+};
+
+#endif // INCLUDED_TEXTEDITCONTEXTMENU_H
diff --git a/src/Authoring/Studio/Controls/TextEditInPlace.cpp b/src/Authoring/Studio/Controls/TextEditInPlace.cpp
new file mode 100644
index 00000000..145df140
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEditInPlace.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TextEditInPlace.h"
+#include "Renderer.h"
+
+//==============================================================================
+// Static class constants
+//==============================================================================
+const long CTextEditInPlace::s_RightBuffer =
+ 6; ///< Extra space allotted on the right side of the text
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CTextEditInPlace::CTextEditInPlace()
+ : CStringEdit()
+ , m_IsInEditMode(false)
+ , m_IsEditable(true)
+ , m_AllowSingleClickEdit(true)
+{
+ SetAlignment(LEFT);
+ SetReadOnly(true);
+ SetFillBackground(false);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CTextEditInPlace::~CTextEditInPlace()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the mouse double-click event. Enters the text into edit mode so that
+ * the user can change its value.
+ * @param inPoint location of the mouse
+ * @param inFlags modifier flags for the mouse
+ * @return true if this message should not be passed to any other children, otherwise false
+ */
+bool CTextEditInPlace::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theMessageWasHandled = false;
+ if (m_IsEditable) {
+ SetEditMode(true);
+ SetSelection(0, GetString().Length());
+ m_Caret.show = true;
+ theMessageWasHandled = true;
+ } else
+ theMessageWasHandled = CStringEdit::OnMouseDoubleClick(inPoint, inFlags);
+ return theMessageWasHandled;
+}
+
+void CTextEditInPlace::OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CStringEdit::OnMouseClick(inPoint, inFlags);
+ if (IsInFocus() && !(inFlags & CHotKeys::MOUSE_RBUTTON) && !m_IsInEditMode && m_IsEditable
+ && m_AllowSingleClickEdit) {
+ SetEditMode(true);
+ SelectAllText();
+ }
+}
+
+//==============================================================================
+/**
+ * Called when this control loses focus. Turns off edit mode and redraws the
+ * control.
+ */
+void CTextEditInPlace::OnLoseFocus()
+{
+ SetEditMode(false);
+ CStringEdit::OnLoseFocus();
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Handles most of the drawing, with some help from the parent class.
+ * @param inRenderer Renderer to draw to
+ */
+void CTextEditInPlace::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize()); // CRct( CPt( ::dtol( CalculateCharWidths( inRenderer ) + s_RightBuffer
+ // ), GetSize( ).y ) );
+
+ inRenderer->PushClippingRect(theRect);
+ CStringEdit::Draw(inRenderer);
+ inRenderer->PopClippingRect();
+}
+
+//==============================================================================
+/**
+ * Enables or disables this control from being able to enter "Edit Mode" when
+ * SetEditMode() is called.
+ * @param inIsEditable true if you want the control to be editable when double-clicked, otherwise
+ * false
+ */
+void CTextEditInPlace::SetEditable(bool inIsEditable)
+{
+ m_IsEditable = inIsEditable;
+
+ if (!m_IsEditable && m_IsInEditMode)
+ SetEditMode(false);
+}
+
+//==============================================================================
+/**
+ * Starts or stops "Edit Mode". While in Edit Mode, the user can change the
+ * text, move the caret, and highlight the text. An edit box is also drawn
+ * around the text.
+ * @param inEditMode true to turn on Edit Mode, false to turn off Edit Mode
+ */
+void CTextEditInPlace::SetEditMode(bool inEditMode)
+{
+ if (m_IsEditable && (inEditMode != m_IsInEditMode)) {
+ m_IsInEditMode = inEditMode;
+ SetReadOnly(!m_IsInEditMode);
+
+ // If we are in edit mode
+ if (m_IsInEditMode) {
+ m_PreviousValue = GetString();
+ }
+ // If we are not in edit mode
+ else {
+ // Restore the text color and commit the changes
+ FireCommitEvent();
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Overriden from the parent to cause this control to lose focus when the Enter
+ * button is pressed on the keyboard.
+ * @param inHighlight true to highlight all of the text, false to just change the string
+ */
+void CTextEditInPlace::EnterText(bool inHighlight)
+{
+ CStringEdit::EnterText(inHighlight);
+ OnLoseFocus();
+}
+
+//==============================================================================
+/**
+ * Overriden to also invalidate/validate the parent.
+ * @param inInvalidate true to invalidate this control and the parent control
+ */
+void CTextEditInPlace::Invalidate(bool inInvalidate)
+{
+ if (inInvalidate && GetParent())
+ GetParent()->Invalidate(inInvalidate);
+
+ CStringEdit::Invalidate(inInvalidate);
+}
+
+//==============================================================================
+/**
+ * Override to avoid selecting all the text unless in edit mode.
+ */
+void CTextEditInPlace::OnGainFocus()
+{
+ if (m_IsInEditMode && m_IsEditable)
+ CStringEdit::OnGainFocus();
+}
+
+bool CTextEditInPlace::CanGainFocus()
+{
+ return true;
+}
+
+//==============================================================================
+/**
+ * Returns the edit mode.
+ * @return true if the control is currently in edit mode meaning that the user
+ * can enter text into it, otherwise false
+ */
+bool CTextEditInPlace::GetEditMode()
+{
+ return m_IsInEditMode;
+}
+
+long CTextEditInPlace::GetRightBuffer()
+{
+ return s_RightBuffer;
+}
+
+//==============================================================================
+/**
+ * Handles the Right mouse button down command. Only shows the cut/copy/paste
+ * context menu if the control is currently being edited. You can't change a
+ * string that's not currently being edited
+ * @param inPoint The mouse position at the time of the event
+ * @param inFlags The state of the modifier keys at the time of the event
+ * @return true if this function handled the character, otherwise false
+ */
+
+bool CTextEditInPlace::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theMessageWasHandled = false;
+
+ // The base class shows the cut/copy/paste menu, so only do it if we are in edit mode
+ if (m_IsInEditMode)
+ theMessageWasHandled = CStringEdit::OnMouseRDown(inPoint, inFlags);
+
+ return theMessageWasHandled;
+}
+
+//=============================================================================
+/**
+ * Handles character input from the keyboard.
+ *
+ * @param inChar Character that was pressed
+ * @return true if the character was handled, false if this control does not
+ * care about the character that was pressed
+ */
+bool CTextEditInPlace::OnChar(const QString &inChar, Qt::KeyboardModifiers inModifiers)
+{
+ bool theMessageWasHandled = false;
+ if (GetEditMode())
+ theMessageWasHandled = CStringEdit::OnChar(inChar, inModifiers);
+ return theMessageWasHandled;
+}
+
+//==============================================================================
+/**
+ * By default, clicking on a CTextEditInPlace control that already has focus, will
+ * cause the control to enter edit mode (if SetEditable( true ) has been called).
+ * This is referred to as single-click editing. This function allows you to
+ * enable or disable this functionality. Even if single-click editing is
+ * disabled, the user can still edit the text control by double-clicking on it.
+ * @param inAllow true to enable single-click editing, false to prevent it
+ */
+void CTextEditInPlace::SetSingleClickEdit(bool inAllow)
+{
+ m_AllowSingleClickEdit = inAllow;
+}
+
+//==============================================================================
+/**
+ * Reverts the displayed text to the previous text.
+ */
+void CTextEditInPlace::RevertText()
+{
+ if (m_IsInEditMode)
+ CStringEdit::RevertText();
+}
+
+//==============================================================================
+/**
+ * Override the base class's version
+ */
+void CTextEditInPlace::DoFillBackground(CRenderer *inRenderer)
+{
+ if (m_FillBackground && !m_IsInEditMode)
+ inRenderer->FillSolidRect(QRect(0, 0, GetSize().x, GetSize().y), m_BackgroundColorNoFocus);
+}
+
+//==============================================================================
+/**
+ * Handles any non-character keys that were pressed and need to be handled.
+ *
+ * @param inChar The key that was pressed
+ * @param inFlags Indicates which modifier keys were down when event occurred
+ * @return true if this function handled the character, otherwise false
+ */
+bool CTextEditInPlace::HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool theMessageWasHandled = false;
+
+ switch (inChar) {
+ // We're overwriting the Escape hotkey sequence from StringEdit because we need to lose focus
+ // in TextEditInPlace, StringEdit basically does the same thing for Escape and Ctrl + Z.
+ case Qt::Key_Escape:
+ RevertText();
+ OnLoseFocus();
+ theMessageWasHandled = true;
+ break;
+
+ default:
+ theMessageWasHandled = CStringEdit::HandleSpecialChar(inChar, inFlags);
+ }
+
+ return theMessageWasHandled;
+}
diff --git a/src/Authoring/Studio/Controls/TextEditInPlace.h b/src/Authoring/Studio/Controls/TextEditInPlace.h
new file mode 100644
index 00000000..7239e6fc
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TextEditInPlace.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TEXT_EDIT_IN_PLACE_H
+#define INCLUDED_TEXT_EDIT_IN_PLACE_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StringEdit.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//==============================================================================
+/**
+ * @class CTextEditInPlace
+ */
+class CTextEditInPlace : public CStringEdit
+{
+public:
+ CTextEditInPlace();
+ virtual ~CTextEditInPlace();
+
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnLoseFocus() override;
+ void OnGainFocus() override;
+ bool CanGainFocus() override;
+ void Draw(CRenderer *inRenderer) override;
+ void SetEditable(bool inIsEditable);
+ void SetEditMode(bool inEditMode);
+ void EnterText(bool inHighlight) override;
+ void Invalidate(bool inInvalidate = true) override;
+ bool GetEditMode();
+ static long GetRightBuffer();
+ bool OnChar(const QString &inChar, Qt::KeyboardModifiers inModifiers) override;
+ bool HandleSpecialChar(unsigned int inChar, Qt::KeyboardModifiers inFlags) override;
+ virtual void SetSingleClickEdit(bool inAllow);
+
+protected:
+ void DoFillBackground(CRenderer *inRenderer) override;
+
+protected:
+ bool m_IsInEditMode; ///< If in edit mode, the user is currently editing the control
+ bool m_IsEditable; ///< Can only enter edit mode when the control is editable
+ bool m_AllowSingleClickEdit; ///< True if clicking on the control while it has focus should
+ ///cause it to enter edit mode. False to force a double-click for
+ ///entering edit mode.
+
+ void RevertText() override;
+
+private:
+ static const long s_RightBuffer;
+};
+
+#endif // INCLUDED_TEXT_EDIT_IN_PLACE_H
diff --git a/src/Authoring/Studio/Controls/TimeEdit.cpp b/src/Authoring/Studio/Controls/TimeEdit.cpp
new file mode 100644
index 00000000..3859721f
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TimeEdit.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "CoreUtils.h"
+#include "TimeEdit.h"
+#include "Renderer.h"
+#include "IDoc.h"
+#include "TimeEditDlg.h"
+
+long DELIMITER_SIZE = 2;
+long MINUTES_SIZE = 28;
+long SECONDS_SIZE = 20;
+long MILLIS_SIZE = 28;
+
+CTimeEdit::CTimeEdit(IDoc *inDoc)
+ : m_Time(0)
+ , m_MinimumTime(0)
+ , m_MaximumTime(LONG_MAX)
+{
+ AddChild(&m_Minutes);
+ AddChild(&m_Seconds);
+ AddChild(&m_Millis);
+ m_Doc = inDoc;
+}
+
+CTimeEdit::~CTimeEdit()
+{
+}
+long CTimeEdit::GetTime()
+{
+ return m_Time;
+}
+
+//=============================================================================
+/**
+ * OnMouseDown: Displays a time edit dialog, when the user clicks on the time edit box.
+ * @param inPoint stores the point clicked
+ * @param inFlags stores the control keys pressed
+ */
+bool CTimeEdit::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.ShowDialog(m_Time, 0, m_Doc, PLAYHEAD);
+ return true;
+}
+
+void CTimeEdit::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+
+ inSize.x -= DELIMITER_SIZE * 2;
+
+ m_Minutes.SetPosition(CPt(0, 0));
+ m_Minutes.SetAbsoluteSize(CPt(MINUTES_SIZE, inSize.y));
+ m_Minutes.SetAlignment(CTextEdit::RIGHT);
+ m_Minutes.SetNumDecimalPlaces(0);
+ m_Minutes.AddCommitListener(this);
+ m_Minutes.SetMin(0);
+
+ m_Seconds.SetPosition(CPt(MINUTES_SIZE + DELIMITER_SIZE, 0));
+ m_Seconds.SetAbsoluteSize(CPt(SECONDS_SIZE, inSize.y));
+ m_Seconds.SetAlignment(CTextEdit::LEFT);
+ m_Seconds.SetFixedPlaces(2);
+ m_Seconds.SetNumDecimalPlaces(0);
+ m_Seconds.AddCommitListener(this);
+ m_Seconds.SetMin(0);
+
+ m_Millis.SetPosition(CPt(MINUTES_SIZE + SECONDS_SIZE + DELIMITER_SIZE * 2, 0));
+ m_Millis.SetAbsoluteSize(CPt(MILLIS_SIZE, inSize.y));
+ m_Millis.SetAlignment(CTextEdit::LEFT);
+ m_Millis.SetFixedPlaces(3);
+ m_Millis.SetNumDecimalPlaces(0);
+ m_Millis.AddCommitListener(this);
+ m_Millis.SetMin(0);
+}
+
+//=============================================================================
+/**
+ * Draw this time.
+ * @param inRenderer the renderer to draw to.
+ */
+
+void CTimeEdit::Draw(CRenderer *inRenderer)
+{
+ // Fill in the background, otherwise we'll have junk left over from before
+ CRct theRect(GetSize());
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+ const float yPos = GetSize().y - 3;
+ inRenderer->PushPen(CStudioPreferences::GetRulerTickColor());
+ inRenderer->DrawText(static_cast<float>(m_Seconds.GetPosition().x - DELIMITER_SIZE - 1), yPos,
+ ":");
+ inRenderer->DrawText(static_cast<float>(m_Millis.GetPosition().x - DELIMITER_SIZE - 1), yPos,
+ ".");
+}
+
+//=============================================================================
+/**
+ * Set the text background color for this component.
+ * @param inColor the background color for this.
+ */
+void CTimeEdit::SetBackgroundColor(const CColor &inColor)
+{
+ m_BackgroundColor = inColor;
+
+ m_Minutes.SetBGColorNoFocus(inColor);
+ m_Seconds.SetBGColorNoFocus(inColor);
+ m_Millis.SetBGColorNoFocus(inColor);
+}
+
+//=============================================================================
+/**
+ * Lame, yes. Basically gets the estimated width of this control.
+ */
+long CTimeEdit::GetWidth()
+{
+ return MINUTES_SIZE + SECONDS_SIZE + MILLIS_SIZE + DELIMITER_SIZE * 2;
+}
+
+//=============================================================================
+/**
+ * Set the time displayed by this control.
+ * This will make sure the time is in the proper ranges then set it to the
+ * value.
+ * @param inTime the new time value for this control.
+ */
+void CTimeEdit::SetTime(long inTime)
+{
+ m_Time = inTime;
+
+ m_Minutes.SetData((float)(m_Time / (1000 * 60)));
+ m_Seconds.SetData((float)((m_Time / 1000) % 60));
+ m_Millis.SetData((float)(m_Time % 1000));
+ Invalidate();
+}
+
+void CTimeEdit::OnSetData(CControl *inControl)
+{
+ Q_UNUSED(inControl);
+
+ long theTime = ::dtol(m_Millis.GetData()) + ::dtol(m_Seconds.GetData() * 1000)
+ + ::dtol(m_Minutes.GetData() * 60 * 1000);
+
+ m_TimeListeners.FireEvent(&CTimeEditChangeListener::OnTimeChanged, theTime);
+}
+
+//=============================================================================
+/**
+ * Add a listener to this control for when the time changes.
+ * @param inListener the listener to be added to this control.
+ */
+void CTimeEdit::AddTimeChangeListener(CTimeEditChangeListener *inListener)
+{
+ m_TimeListeners.AddListener(inListener);
+}
+
+//=============================================================================
+/**
+ * Remove a time change listener from this control.
+ * @param inListener the listener to be removed from this control.
+ */
+void CTimeEdit::RemoveTimeChangeListener(CTimeEditChangeListener *inListener)
+{
+ m_TimeListeners.RemoveListener(inListener);
+}
diff --git a/src/Authoring/Studio/Controls/TimeEdit.h b/src/Authoring/Studio/Controls/TimeEdit.h
new file mode 100644
index 00000000..39c8cd3b
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TimeEdit.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIME_EDIT_H
+#define INCLUDED_TIME_EDIT_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "GenericFunctor.h"
+#include "Multicaster.h"
+#include "FloatEdit.h"
+#include "EditInPlace.h"
+#include "StringEdit.h"
+
+#include "IDoc.h"
+
+GENERIC_FUNCTOR_1(CTimeEditChangeListener, OnTimeChanged, long);
+
+class CTimeEdit : public CControl, public CCommitDataListener
+{
+public:
+ CTimeEdit(IDoc *inDoc);
+ virtual ~CTimeEdit();
+
+ void SetMinimumTime(long inMinimumTime);
+ void SetMaximumTime(long inMaximumTime);
+ void SetBackgroundColor(const CColor &inColor);
+ void SetTime(long inTime);
+ virtual void Draw(CRenderer *inRenderer);
+ long GetWidth();
+ void AddTimeChangeListener(CTimeEditChangeListener *inListener);
+ void RemoveTimeChangeListener(CTimeEditChangeListener *inListener);
+
+ virtual void SetSize(CPt inSize);
+
+ virtual void OnSetData(CControl *inControl);
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ long GetTime();
+
+protected:
+ long m_Time;
+ IDoc *m_Doc;
+ CEditInPlace<CFloatEdit> m_Minutes;
+ CEditInPlace<CFloatEdit> m_Seconds;
+ CEditInPlace<CFloatEdit> m_Millis;
+
+ CColor m_BackgroundColor;
+
+ long m_MinimumTime;
+ long m_MaximumTime;
+
+ CMulticaster<CTimeEditChangeListener *> m_TimeListeners;
+};
+#endif // INCLUDED_TIME_EDIT_H
diff --git a/src/Authoring/Studio/Controls/ToggleButton.cpp b/src/Authoring/Studio/Controls/ToggleButton.cpp
new file mode 100644
index 00000000..3271a949
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ToggleButton.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ToggleButton.h"
+
+IMPLEMENT_OBJECT_COUNTER(CToggleButton)
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CToggleButton::CToggleButton()
+ : m_IsToggleDown(false)
+{
+ ADDTO_OBJECT_COUNTER(CToggleButton)
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CToggleButton::~CToggleButton()
+{
+ REMOVEFROM_OBJECT_COUNTER(CToggleButton)
+}
+
+//=============================================================================
+/**
+ * Handles mouse click events (full mouse down and mouse up combo). Causes the
+ * button to change states.
+ * @param inListener toggle listener to be removed
+ */
+void CToggleButton::OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CButtonControl::OnMouseClick(inPoint, inFlags);
+ Toggle();
+}
+
+//=============================================================================
+/**
+ * Handles mouse down on the button. Allows a button down event to be fired
+ * by the base class, but then returns the button to the proper state, meaning
+ * that toggle buttons should not actually change states on mouse down messages
+ * even though that's what the base class does.
+ * @param inPoint location of the mouse when this event occurred
+ * @param inFlags state of modifier keys at the time that this event occurred
+ */
+bool CToggleButton::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theReturn = CButtonControl::OnMouseDown(inPoint, inFlags);
+
+ if (m_IsToggleDown)
+ SetButtonState(EBUTTONSTATE_DOWN);
+ else
+ SetButtonState(EBUTTONSTATE_UP);
+ Invalidate();
+
+ return theReturn;
+}
+
+//=============================================================================
+/**
+ * Handles mouse up on the button. Fires a mouse up event, but not a toggle
+ * event, as this is handled by OnMouseClick. Button state is maintained for
+ * the toggle button instead of changing immediately like on the base class.
+ * @param inPoint location of the mouse when this event occurred
+ * @param inFlags state of modifier keys at the time that this event occurred
+ */
+void CToggleButton::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CButtonControl::OnMouseUp(inPoint, inFlags);
+
+ if (m_IsToggleDown)
+ SetButtonState(EBUTTONSTATE_DOWN);
+ else
+ SetButtonState(EBUTTONSTATE_UP);
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Changes the state of the button. If the button is currently down, it will
+ * be changed to up and vice versa. An event is fired to notify interested
+ * listeners that the button was toggled.
+ */
+void CToggleButton::Toggle()
+{
+ m_IsToggleDown = !m_IsToggleDown;
+ if (m_IsToggleDown)
+ SetButtonState(EBUTTONSTATE_DOWN);
+ else
+ SetButtonState(EBUTTONSTATE_UP);
+
+ SigToggle(this, GetButtonState());
+
+ Invalidate();
+}
+
+void CToggleButton::SetToggleState(bool inIsDown)
+{
+ if (inIsDown != m_IsToggleDown) {
+ m_IsToggleDown = inIsDown;
+ SetButtonState(inIsDown ? EBUTTONSTATE_DOWN : EBUTTONSTATE_UP);
+ }
+}
diff --git a/src/Authoring/Studio/Controls/ToggleButton.h b/src/Authoring/Studio/Controls/ToggleButton.h
new file mode 100644
index 00000000..59feafb5
--- /dev/null
+++ b/src/Authoring/Studio/Controls/ToggleButton.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TOGGLE_BUTTON_H
+#define INCLUDED_TOGGLE_BUTTON_H 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ButtonControl.h"
+
+//==============================================================================
+// Functors
+//==============================================================================
+
+//=============================================================================
+/**
+ * Class for making toggle buttons. A toggle button changes state each time a
+ * full mouse click is received. When you click the mouse on a toggle button,
+ * it will enter the "down" state. When you click it again, the toggle button
+ * will enter the "up" state. This differs from a regular button, which only
+ * enters the "down" state while the mouse is being held down, then immediately
+ * returns to the "up" state when the mouse is released.
+ */
+class CToggleButton : public CButtonControl
+{
+public:
+ CToggleButton();
+ virtual ~CToggleButton();
+
+ DEFINE_OBJECT_COUNTER(CToggleButton)
+
+ void OnMouseClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ virtual void Toggle();
+
+ virtual void SetToggleState(bool inIsDown);
+ bool GetToggleState() const { return m_IsToggleDown; }
+
+ //=============================================================================
+ /**
+ * Functor for handling button toggle messages. The message will be fired to
+ * interested listeners when a full button click occurs on this button. If you
+ * want to be a listener for button toggle events, you must define an
+ * OnButtonToggle function with the following parameters.
+ * @param CToggleButton* pointer to the button that generated the event
+ * @param EButtonState the state of the button as a result of this event
+ */
+ boost::signal2<void, CToggleButton *, CButtonControl::EButtonState> SigToggle;
+
+protected:
+ // protected to make fools use SetToggleState instead.
+ void SetButtonState(EButtonState inState) override { CButtonControl::SetButtonState(inState); }
+
+ bool m_IsToggleDown;
+};
+
+#endif // INCLUDED_TOGGLE_BUTTON_H
diff --git a/src/Authoring/Studio/Controls/TreeControl.cpp b/src/Authoring/Studio/Controls/TreeControl.cpp
new file mode 100644
index 00000000..892c2086
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TreeControl.cpp
@@ -0,0 +1,749 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TreeControl.h"
+#include "Renderer.h"
+#include "HotKeys.h"
+#include "MasterP.h"
+#include "StudioPreferences.h"
+#include "StudioErrorIDs.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTreeControl::CTreeControl()
+ : m_AllowMultipleSelection(false)
+ , m_Refresh(true)
+ , m_SortFlag(false)
+{
+
+ m_BGColor = CStudioPreferences::GetBaseColor();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTreeControl::~CTreeControl()
+{
+ // Just clear out our lists, deletion is handled by whoever created the items.
+ m_ItemList.clear();
+ m_SelectedItemList.clear();
+}
+
+//=============================================================================
+/**
+ * Sets the background color of this control.
+ * @param inColor new background color
+ */
+void CTreeControl::SetBackgroundColor(const CColor &inColor)
+{
+ m_BGColor = inColor;
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Draws the control.
+ */
+void CTreeControl::Draw(CRenderer *inRenderer)
+{
+ inRenderer->FillSolidRect(QRect(QPoint(0, 0), GetSize()), m_BGColor);
+}
+
+//=============================================================================
+/**
+ * Find the item that this item should be added after to maintian sort order.
+ */
+CTreeItem *CTreeControl::FindPrevSortSibling(CTreeItem *, CTreeItem *)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ UIC_THROW(E_FAIL); // this is ass
+#endif
+ return NULL;
+}
+
+//=============================================================================
+/**
+ * Find the item that this item should be before after to maintian sort order.
+ */
+CTreeItem *CTreeControl::FindNextSortSibling(CTreeItem *inParent, CTreeItem *inChild)
+{
+ CTreeItem *theSortItem = nullptr;
+
+ if (inParent) {
+ theSortItem = inParent->FindNextSortSibling(inChild);
+ } else {
+ // No items in the list - return nullptr (because we don't care)
+ if (m_ItemList.size()) {
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ // Item in the list
+ theSortItem = *thePos;
+ if (IsItemLess(inChild, theSortItem))
+ break;
+ }
+
+ // Last item in the list
+ if (thePos == m_ItemList.end())
+ theSortItem = nullptr;
+ }
+ }
+
+ return theSortItem;
+}
+
+//=============================================================================
+/**
+ * Adds a tree item to the tree control. The child is automatically added to
+ * the end of the list under the specified parent. Once you add a tree item,
+ * to this tree control. You must call the remove functions to take it out of
+ * the tree control, after which point, it is safe for you to delete it.
+ * @param inParent parent of the tree item or nullptr if the item is to be a root
+ * @param inChild the child item to be added
+ */
+void CTreeControl::AddItem(CTreeItem *inParent, CTreeItem *inChild)
+{
+ CTreeItem *theBeforeItem = nullptr;
+
+ if (IsSorting())
+ theBeforeItem = FindNextSortSibling(inParent, inChild);
+
+ AddItemBefore(inParent, inChild, theBeforeItem);
+}
+
+//=============================================================================
+/**
+ * Same as AddItem( ), except that you can specify where to put the child in relation
+ * to its siblings.
+ * @param inParent parent of the new item, or nullptr if adding a root level item
+ * @param inChild new child to be added
+ * @param inBefore should be a child of inParent, in which case, inChild is added before it;
+ * can be nullptr to indicate that inChild needs to go at the end of the inParent's children.
+ */
+void CTreeControl::AddItemBefore(CTreeItem *inParent, CTreeItem *inChild, CTreeItem *inBefore)
+{
+ if (inParent)
+ inParent->AddItem(inChild, inBefore);
+ else
+ AddToRootItemList(inChild, inBefore);
+
+ CTreeItem *theControlBefore = inBefore;
+ if (inParent && !inBefore)
+ theControlBefore = RecurseGetControlAfter(inParent);
+
+ AddToHierarchy(inChild, theControlBefore, true);
+}
+
+//=============================================================================
+/**
+ * Same as AddItem( ), except that you can specify where to put the child in relation
+ * to its siblings.
+ * @param inParent parent of the new item, or nullptr if adding a root level item
+ * @param inChild new child to be added
+ * @param inAfter should be a child of inParent, in which case, inChild is added after it
+ */
+void CTreeControl::AddItemAfter(CTreeItem *inParent, CTreeItem *inChild, CTreeItem *inAfter)
+{
+ CTreeItem *theItemBefore = nullptr;
+ CTreeItem *theControlBefore = nullptr;
+
+ theItemBefore = GetSiblingAfter(inAfter);
+ theControlBefore = RecurseGetControlAfter(inAfter);
+
+ if (inParent)
+ inParent->AddItem(inChild, theItemBefore);
+ else
+ AddToRootItemList(inChild, theItemBefore);
+
+ AddToHierarchy(inChild, theControlBefore, true);
+}
+
+//=============================================================================
+/**
+ * Simply detaches inItem from the tree. Provided so that subclasses can
+ * override this function to actually perform item deletion if necessary.
+ * @param inItem Item to be removed
+ */
+void CTreeControl::RemoveItem(CTreeItem *inItem)
+{
+ Detach(inItem);
+}
+
+//=============================================================================
+/**
+ * Removes all currently selected items.
+ * @param inItem Item to be removed
+ */
+void CTreeControl::RemoveSelectedItems()
+{
+ // Iterate through the selected items, removing each one
+ CTreeItem::TItemList::iterator thePos = m_SelectedItemList.begin();
+ for (; thePos != m_SelectedItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ RemoveItem(theItem);
+ }
+ m_SelectedItemList.clear();
+}
+
+//=============================================================================
+/**
+ * Removes all currently selected items.
+ * @param inItem Item to be removed
+ */
+void CTreeControl::RemoveAllItems()
+{
+ // Iterate through the selected items, removing each one
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+
+ while (thePos != m_ItemList.end()) {
+ CTreeItem *theItem = *thePos;
+ RemoveItem(theItem);
+ thePos = m_ItemList.begin();
+ }
+ ASSERT(m_ItemList.empty() == true);
+}
+
+//=============================================================================
+/**
+ * Creates a break in the tree. The item being detached is disconnected from
+ * it's parent (or from the tree control itself if it has no parent). The
+ * releations between branches below inItem are maintained internally by
+ * each child item. However, inItem and all of it's children are removed
+ * from the control hierarchy (they won't be drawn). This is useful if you
+ * want to move sections of the tree around without deleting anything.
+ * @param inItem item to detach
+ */
+void CTreeControl::Detach(CTreeItem *inItem)
+{
+ inItem->Detach();
+
+ if (inItem->IsRootItem())
+ RemoveFromRootItemList(inItem);
+
+ RemoveFromHierarchy(inItem, true);
+}
+
+//=============================================================================
+/**
+ * Allows you to enable or disable multiple selection for this tree control.
+ * @param inAllow true to allow multiple selection, false to only allow single selection
+ */
+void CTreeControl::SetAllowMultipleSelection(bool inAllow)
+{
+ m_AllowMultipleSelection = inAllow;
+}
+
+//=============================================================================
+/**
+ * @return true if multiple selection is enabled, false if only single
+ * selection is enabled.
+ */
+bool CTreeControl::GetAllowMultipleSelection()
+{
+ return m_AllowMultipleSelection;
+}
+
+//=============================================================================
+/**
+ * @return the number of items at the root level of this tree control
+ */
+long CTreeControl::GetNumRootItems()
+{
+ return (long)m_ItemList.size();
+}
+
+//=============================================================================
+/**
+ * Get the root item at the specified index
+ */
+CTreeItem *CTreeControl::GetRootItem(long inIndex)
+{
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ std::advance(thePos, inIndex);
+
+ if (thePos != m_ItemList.end())
+ return (*thePos);
+
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Gets iterators for traversing the list of selected tree items.
+ * @param outListBegin returns an iterator to the beginning of the selected item list
+ * @param outListEnd returns an iterator to the end of the selected item list
+ */
+void CTreeControl::GetSelectionIterators(CTreeItem::TItemList::iterator &outListBegin,
+ CTreeItem::TItemList::iterator &outListEnd)
+{
+ outListBegin = m_SelectedItemList.begin();
+ outListEnd = m_SelectedItemList.end();
+}
+
+//=============================================================================
+/**
+ * @return number of selected items in this tree control
+ */
+long CTreeControl::GetNumSelectedItems()
+{
+ return (long)m_SelectedItemList.size();
+}
+
+//=============================================================================
+/**
+ * @return the first selected item in the tree control
+ */
+CTreeItem *CTreeControl::GetFirstSelectedItem()
+{
+ CTreeItem::TItemList::iterator theBegin = m_SelectedItemList.begin();
+ if (theBegin != m_SelectedItemList.end())
+ return (*theBegin);
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Selects the specified tree item and adds the item to the selection list.
+ * Multiple selection is handled via inKeyModifiers and setting of the multiple
+ * selection flag on this class.
+ * @param inItem item to be selected
+ * @param inKeyModifiers optionally indicates the state of shift, ctrl, etc. for multiple selection
+ */
+void CTreeControl::Select(CTreeItem *inItem, long inKeyModifiers /*=0*/)
+{
+ ASSERT(inItem != nullptr);
+
+ bool theCtrlKeyIsDown =
+ (inKeyModifiers & CHotKeys::MODIFIER_CONTROL) == CHotKeys::MODIFIER_CONTROL;
+ bool theShiftKeyIsDown =
+ (inKeyModifiers & CHotKeys::MODIFIER_SHIFT) == CHotKeys::MODIFIER_SHIFT;
+
+ // clears all existing selection if multiple selection is not allowed
+ // If multiple selection is not enabled or neither control nor shift is down
+ if (!m_AllowMultipleSelection || (!theCtrlKeyIsDown && !theShiftKeyIsDown)) {
+ CTreeItem::TItemList::iterator thePos = m_SelectedItemList.begin();
+ // Iterate through all the selected items and deselect them
+ for (; thePos != m_SelectedItemList.end();) {
+ CTreeItem *theItem = *thePos;
+ if (theItem->IsSelected() && inItem != theItem) {
+ theItem->SetSelected(false);
+ thePos = m_SelectedItemList.erase(thePos); // remove unselected item from list
+ } else
+ ++thePos;
+ }
+ }
+
+ // Don't bother selecting the item if it is already selected
+ if (!inItem->IsSelected()) {
+ inItem->SetSelected(true);
+ m_SelectedItemList.push_back(inItem);
+
+ // Tell all TreeItemSelectionListeners that the item was selected
+ FireItemSelected(inItem);
+ }
+
+ if (theShiftKeyIsDown) {
+ // TODO: advanced selection
+ }
+}
+
+//=============================================================================
+/**
+ * Deselects the specified item, if it is selected.
+ * @param inItem item to be deselected
+ */
+void CTreeControl::Deselect(CTreeItem *inItem)
+{
+ // Run through the list and look for the specified item
+ bool theItemWasFound = false;
+ CTreeItem::TItemList::iterator thePos = m_SelectedItemList.begin();
+ CTreeItem::TItemList::iterator theEnd = m_SelectedItemList.end();
+ for (; thePos != theEnd; ++thePos) {
+ CTreeItem *theItem = *thePos;
+ if (theItem == inItem) {
+ theItemWasFound = true;
+ break;
+ }
+ }
+
+ // If the item was found, remove it from the selected item list
+ if (theItemWasFound) {
+ m_SelectedItemList.erase(thePos);
+ // Tell the item it is no longer selected
+ inItem->SetSelected(false);
+ }
+}
+
+//=============================================================================
+/**
+ * Deselects all currently selected items.
+ */
+void CTreeControl::DeselectAll()
+{
+ CTreeItem::TItemList::iterator thePos = m_SelectedItemList.begin();
+ CTreeItem::TItemList::iterator theEnd = m_SelectedItemList.end();
+ for (; thePos != theEnd; ++thePos) {
+ CTreeItem *theItem = *thePos;
+ theItem->SetSelected(false);
+ }
+ m_SelectedItemList.clear();
+}
+
+//=============================================================================
+/**
+ * Expand all nodes to the specified item.
+ */
+void CTreeControl::ExpandTo(CTreeItem *inItem)
+{
+ // Turn off tree refreshing
+ bool theAllowRefresh = GetAllowRefresh();
+ SetAllowRefresh(false);
+
+ // Expand the specified item
+ inItem->Expand();
+
+ // Expand the parents
+ CTreeItem *theParent = inItem->GetParentItem();
+ if (theParent)
+ ExpandTo(theParent);
+
+ // Restore tree refreshing
+ SetAllowRefresh(theAllowRefresh);
+}
+
+//=============================================================================
+/**
+ * Expand all nodes from this item onwards.
+ */
+void CTreeControl::ExpandFrom(CTreeItem *inItem)
+{
+ // Turn off tree refreshing
+ bool theAllowRefresh = GetAllowRefresh();
+ SetAllowRefresh(false);
+
+ // Expand the item's immediate children
+ inItem->Expand();
+
+ CTreeItem::TItemList::iterator theBegin;
+ CTreeItem::TItemList::iterator theEnd;
+
+ inItem->GetItemIterator(theBegin, theEnd);
+
+ // Expand all children
+ std::for_each(theBegin, theEnd, std::bind1st(std::mem_fun(&CTreeControl::ExpandFrom), this));
+
+ // Restore refresh state
+ SetAllowRefresh(theAllowRefresh);
+}
+
+//=============================================================================
+/**
+ * Collapse all items in the tree.
+ */
+void CTreeControl::CollapseAll()
+{
+ // Not implemented
+ UIC_THROW(STUDIO_E_FAIL);
+}
+
+//=============================================================================
+/**
+ * Allows or disallows calls to RecalcLayout. This is useful if you are adding
+ * a bulk amount of items to the tree control at once. You can simply call
+ * this function with false, add your items, the call this function again with
+ * true. In this way, you don't incur a call to RecalcLayout for each item
+ * added, just one call at the end when all the items are added.
+ * @param inAllowRefresh true to allow RecalcLayout calls, false to prevent them
+ * from doing anything.
+ */
+void CTreeControl::SetAllowRefresh(bool inAllowRefresh)
+{
+ m_Refresh = inAllowRefresh;
+}
+
+//=============================================================================
+/**
+ * Tells you whether or not a call to RecalcLayout will actually result in
+ * resizing and repositioning the items in the tree.
+ * @return true if the tree control is currently allowing recalc layout calls
+ * to process, otherwise false.
+ */
+bool CTreeControl::GetAllowRefresh()
+{
+ return m_Refresh;
+}
+
+//=============================================================================
+/**
+ * Helper function for finding the tree item that is the next sibling after
+ * the specified item. If inItem has a parent, the parent's list is searched,
+ * otherwise, we just search the root item list. If there is no sibling, this
+ * function recurses on the parent (if there is one) all the way up the tree
+ * until we reach the top. If there is not a control after the specified one,
+ * this function returns nullptr.
+ * @param inItem Item whose sibling we are searching for
+ * @return the sibling that occurs after inItem in the tree hierarchy
+ */
+CTreeItem *CTreeControl::GetSiblingAfter(CTreeItem *inItem)
+{
+ UICPROFILE(GetSiblingAfter);
+
+ ASSERT(inItem);
+
+ CTreeItem *theSibling = nullptr;
+ CTreeItem *theParent = inItem->GetParentItem();
+
+ // Declare the iterators, then use them on either list
+ CTreeItem::TItemList::reverse_iterator thePos;
+ CTreeItem::TItemList::reverse_iterator theListEnd;
+
+ // If we have a parent, get the iterators from the parent
+ if (theParent) {
+ theParent->GetReverseItemIterator(thePos, theListEnd);
+ }
+ // If we don't have a parent, we must be searching for a root-level item, so search the tree
+ // control's list
+ else {
+ thePos = m_ItemList.rbegin();
+ theListEnd = m_ItemList.rend();
+ }
+
+ // Iterate through the list
+ bool theItemWasFound = false;
+ CTreeItem *thePreviousItem = nullptr;
+ for (; thePos != theListEnd; ++thePos) {
+ CTreeItem *theCurrentItem = *thePos;
+
+ // If we found the item we are looking for, break out of the loop
+ if (theCurrentItem == inItem) {
+ theItemWasFound = true;
+ break;
+ }
+
+ thePreviousItem = theCurrentItem;
+ }
+
+ // If the item we were looking for was found, the sibling is probably contained in
+ // thePreviousItem
+ if (theItemWasFound)
+ theSibling = thePreviousItem;
+
+ return theSibling;
+}
+
+//=============================================================================
+/**
+ * Helper function for fetching the control after inItem. If inItem has siblings,
+ * those are searched to determine if there is a sibling after inItem. If no
+ * sibling is found, this function recurses, this time searching for a sibling
+ * of inItem->GetParentItem( ); assuming that inItem has a parent. In this
+ * way, we determine the control that occurs after inItem in the Control
+ * hierarchy.
+ * @param inItem we are searching for the item after this one
+ * @return the item after inItem in the control hierarchy or nullptr if there is no item after inItem.
+ */
+CTreeItem *CTreeControl::RecurseGetControlAfter(CTreeItem *inItem)
+{
+ UICPROFILE(RecurseGetControlAfter);
+
+ ASSERT(inItem);
+
+ CTreeItem *theControlAfter = GetSiblingAfter(inItem);
+ CTreeItem *theParent = inItem->GetParentItem();
+
+ // If we still haven't found a sibling, and we have a valid parent, recursively search for a
+ // sibling of the parent
+ if (!theControlAfter && theParent)
+ theControlAfter = RecurseGetControlAfter(theParent);
+
+ return theControlAfter;
+}
+
+//=============================================================================
+/**
+ * Protected function for adding items to the root level of this control.
+ * @param inItem new item to be added
+ * @param inInsertBefore item to insert inItem before in the list, or
+ * nullptr to insert the item at the end of the list
+ */
+void CTreeControl::AddToRootItemList(CTreeItem *inItem, CTreeItem *inInsertBefore)
+{
+ // If an item to insert before is specified
+ if (inInsertBefore) {
+ // Go through the list and insert inItem before inInsertBefore
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theCurrentItem = *thePos;
+ if (theCurrentItem == inInsertBefore) {
+ m_ItemList.insert(thePos, inItem);
+ break;
+ }
+ }
+ }
+ // If no item is specified for inserting before, add inItem to the end of the list
+ else {
+ m_ItemList.push_back(inItem);
+ }
+}
+
+//=============================================================================
+/**
+ * Protected function for removing items from the root level of this control.
+ * The item is not deleted and nothing is done to the children of the item.
+ * @param inItem item to be removed from the list
+ */
+void CTreeControl::RemoveFromRootItemList(CTreeItem *inItem)
+{
+ bool theItemWasFound = false;
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ if (theItem == inItem) {
+ theItemWasFound = true;
+ break;
+ }
+ }
+
+ if (theItemWasFound) {
+ m_ItemList.erase(thePos);
+ }
+}
+
+//=============================================================================
+/**
+ * Protected function for adding items to the root level of this control. No
+ * lists are updated, this is merely for updating the control hierarchy.
+ * @param inItem item to be added to the control hierarchy
+ * @param inBefore item to insert inItem before in the control hierarchy or nullptr
+ * to insert at the end of the control hierarchy.
+ * @param inIsRecursive true to recursively add all children of inItem to hierarchy
+ */
+void CTreeControl::AddToHierarchy(CTreeItem *inItem, CTreeItem *inBefore, bool inIsRecursive)
+{
+ AddChild(inItem, inBefore);
+
+ if (inIsRecursive) {
+ bool theAllowRefresh = GetAllowRefresh();
+ SetAllowRefresh(false);
+
+ if (inItem->IsContainer()) {
+ CTreeItem::TItemList::iterator thePos;
+ CTreeItem::TItemList::iterator theEnd;
+ inItem->GetItemIterator(thePos, theEnd);
+ for (; thePos != theEnd; ++thePos) {
+ CTreeItem *theChild = *thePos;
+ AddToHierarchy(theChild, inBefore, inIsRecursive);
+ }
+ }
+
+ SetAllowRefresh(theAllowRefresh);
+ }
+}
+
+//=============================================================================
+/**
+ * Protected function for removing an item from the control hierarchy. The
+ * item is not deleted, but it will no longer be drawn.
+ * @param inItem Item to remove
+ * @param inIsRecursive true to recursively call this function on all of the item's children
+ */
+void CTreeControl::RemoveFromHierarchy(CTreeItem *inItem, bool inIsRecursive)
+{
+ if (inIsRecursive) {
+ bool theAllowRefresh = GetAllowRefresh();
+ SetAllowRefresh(false);
+
+ if (inItem->IsContainer()) {
+ CTreeItem::TItemList::iterator thePos;
+ CTreeItem::TItemList::iterator theEnd;
+ inItem->GetItemIterator(thePos, theEnd);
+ for (; thePos != theEnd; ++thePos) {
+ CTreeItem *theChild = *thePos;
+ RemoveFromHierarchy(theChild, inIsRecursive);
+ }
+ }
+
+ SetAllowRefresh(theAllowRefresh);
+ }
+
+ Deselect(inItem);
+ RemoveChild(inItem);
+}
+
+//=============================================================================
+/**
+ * Private function for adding children to the Control hierarchy. Only this
+ * class should call this function. Everyone else needs to go through AddItem( ).
+ * @param inControl new control to be added
+ * @param inInsertBefore control to insert the new one before in the hierarchy
+ */
+void CTreeControl::AddChild(CControl *inControl, CControl *inInsertBefore)
+{
+ Q3DStudio::Control::SVerticalLazyFlow::AddChild(inControl, inInsertBefore);
+}
+
+//==============================================================================
+/**
+ * Add a CTreeItemSelectionListener to this control's list of
+ * CTreeItemSelectionListeners.
+ *
+ * @param inListener CTreeItemSelectionListener to add to m_SelectionListeners
+ */
+void CTreeControl::AddSelectionListener(CTreeItemSelectionListener *inListener)
+{
+ m_SelectionListeners.AddListener(inListener);
+}
+
+//==============================================================================
+/**
+ * Remove a CTreeItemSelectionListener from this control's list of
+ * CTreeItemSelectionListeners.
+ *
+ * @param inListener CTreeItemSelectionListener to remove from m_SelectionListeners
+ */
+void CTreeControl::RemoveSelectionListener(CTreeItemSelectionListener *inListener)
+{
+ m_SelectionListeners.RemoveListener(inListener);
+}
+
+void CTreeControl::FireItemSelected(CTreeItem *inItem)
+{
+ OnTreeItemSelected(inItem);
+ m_SelectionListeners.FireEvent(&CTreeItemSelectionListener::OnTreeItemSelected, inItem);
+}
diff --git a/src/Authoring/Studio/Controls/TreeControl.h b/src/Authoring/Studio/Controls/TreeControl.h
new file mode 100644
index 00000000..e9958322
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TreeControl.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TREE_CONTROL_H
+#define INCLUDED_TREE_CONTROL_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "LazyFlow.h"
+#include "TreeItem.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CTest_TreeControl;
+
+//==============================================================================
+// Functor
+//==============================================================================
+/// This means that any class that wants be informed when TreeItems are selected
+/// needs to implement the CTreeItemSelectionListener interface, and the
+/// void OnTreeItemSelected( CTreeItem* ) method
+/// The CTreeItem is the item that was selected
+GENERIC_FUNCTOR_1(CTreeItemSelectionListener, OnTreeItemSelected, CTreeItem *);
+
+//=============================================================================
+/**
+ * Custom tree control class. You need to call one of the AddItem*( ) functions
+ * to add tree items to this control. Multiple selection can be enabled using
+ * the provided functions. The tree control maintains a list of all the root
+ * level items. It is also responsible for managing the separate "Control
+ * hierarchy."
+ */
+class CTreeControl : public Q3DStudio::Control::SVerticalLazyFlow
+{
+public:
+ CTreeControl();
+ virtual ~CTreeControl();
+ void SetBackgroundColor(const ::CColor &inColor);
+ void Draw(CRenderer *inRenderer) override;
+ virtual void AddItem(CTreeItem *inParent, CTreeItem *inChild);
+ virtual void AddItemBefore(CTreeItem *inParent, CTreeItem *inChild, CTreeItem *inBefore);
+ virtual void AddItemAfter(CTreeItem *inParent, CTreeItem *inChild, CTreeItem *inAfter);
+ virtual void RemoveItem(CTreeItem *inItem);
+ virtual void RemoveSelectedItems();
+ virtual void RemoveAllItems();
+ virtual void Detach(CTreeItem *inItem);
+ void SetAllowMultipleSelection(bool inAllow);
+ bool GetAllowMultipleSelection();
+ void GetSelectionIterators(CTreeItem::TItemList::iterator &outListBegin,
+ CTreeItem::TItemList::iterator &outListEnd);
+ long GetNumSelectedItems();
+ CTreeItem *GetFirstSelectedItem();
+ virtual void Select(CTreeItem *inItem, long inKeyModifiers = 0);
+ virtual void Deselect(CTreeItem *inItem);
+ virtual void DeselectAll();
+ void SetAllowRefresh(bool inAllowRefresh);
+ bool GetAllowRefresh();
+ long GetNumRootItems();
+ CTreeItem *GetRootItem(long inIndex);
+ virtual void ExpandTo(CTreeItem *inItem);
+ virtual void ExpandFrom(CTreeItem *inItem);
+ virtual void CollapseAll();
+ virtual void SetSorting(bool inSort) { m_SortFlag = inSort; }
+ virtual bool IsSorting() { return m_SortFlag; }
+
+ virtual bool IsItemGreater(CTreeItem *, CTreeItem *) { return true; }
+ virtual bool IsItemLess(CTreeItem *, CTreeItem *) { return false; }
+
+ // SelectionListener methods
+ void AddSelectionListener(CTreeItemSelectionListener *inListener);
+ void RemoveSelectionListener(CTreeItemSelectionListener *inListener);
+ void FireItemSelected(CTreeItem *inItem);
+
+ // Override to hear select
+ virtual void OnTreeItemSelected(CTreeItem *) {}
+ virtual void OnItemExpanding(CTreeItem *) {}
+ virtual void OnItemExpanded(CTreeItem *) { NotifyParentNeedsLayout(); }
+ virtual void OnItemCollapsing(CTreeItem *) {}
+ virtual void OnItemCollapsed(CTreeItem *) { NotifyParentNeedsLayout(); }
+
+protected:
+ // Member Vars
+ ::CColor m_BGColor; ///< Specifies the background color of this control
+ bool m_AllowMultipleSelection; ///< If true, multiple selection is enabled for this control
+ CTreeItem::TItemList m_ItemList; ///< List of all root level tree items
+ CTreeItem::TItemList m_SelectedItemList; ///< List of all selected tree items, regardless of
+ ///whether or not they are root level
+ bool m_Refresh;
+ bool m_SortFlag;
+
+ // Member Functions
+ CTreeItem *GetSiblingAfter(CTreeItem *inItem);
+ CTreeItem *RecurseGetControlAfter(CTreeItem *inItem);
+ CTreeItem *FindPrevSortSibling(CTreeItem *inParent, CTreeItem *inChild);
+ CTreeItem *FindNextSortSibling(CTreeItem *inParent, CTreeItem *inChild);
+ void AddToRootItemList(CTreeItem *inItem, CTreeItem *inInsertBefore);
+ void RemoveFromRootItemList(CTreeItem *inItem);
+ void AddToHierarchy(CTreeItem *inItem, CTreeItem *inBefore, bool inIsRecursive);
+ void RemoveFromHierarchy(CTreeItem *inItem, bool inIsRecursive);
+
+private:
+ /// Private because you can only add CTreeItems to this control with AddItem( ).
+ void AddChild(CControl *inControl, CControl *inInsertBefore = nullptr) override;
+
+ CMulticaster<CTreeItemSelectionListener *>
+ m_SelectionListeners; ///< List of listeners who want to know when a TreeItem is selected
+};
+#endif // INCLUDED_TREE_CONTROL_H
diff --git a/src/Authoring/Studio/Controls/TreeItem.cpp b/src/Authoring/Studio/Controls/TreeItem.cpp
new file mode 100644
index 00000000..03a41530
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TreeItem.cpp
@@ -0,0 +1,939 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TreeItem.h"
+#include "TreeControl.h"
+#include "Renderer.h"
+#include "SIcon.h"
+#include "ResourceCache.h"
+#include "HotKeys.h"
+#include "MasterP.h"
+#include "StudioPreferences.h"
+
+IMPLEMENT_OBJECT_COUNTER(CTreeItem)
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inTreeControl the tree control that this item will belong to
+ */
+CTreeItem::CTreeItem(CTreeControl *inTreeControl)
+ : m_TreeControl(inTreeControl)
+ , m_Parent(NULL)
+ , m_Toggle(NULL)
+ , m_Name(NULL)
+ , m_Icon(NULL)
+ , m_ControlGap(2)
+ , m_IndentLevel(0)
+ , m_IndentSize(0)
+ , m_IsExpanded(false)
+ , m_IsSelected(false)
+ , m_ShowVisibleSelection(true)
+ , m_Color(CStudioPreferences::GetBaseColor())
+ , m_TextColor(CStudioPreferences::GetNormalColor())
+{
+ ADDTO_OBJECT_COUNTER(CTreeItem)
+
+ // Set up some of our flow layout properties
+ SetFlowDirection(FLOW_HORIZONTAL);
+ SetAlignment(ALIGN_VERT_NEITHER, ALIGN_LEFT);
+ SetGapBetweenChildren(m_ControlGap);
+ SetMaximumSize(CPt(LONG_MAX, CStudioPreferences::GetDefaultTextEditSize()));
+ SetMinimumSize(CPt(0, CStudioPreferences::GetDefaultTextEditSize()));
+
+ // Create the toggle
+ CreateToggle();
+
+ // Icon control
+ m_Icon = new CSIcon();
+
+ // Name control
+ m_Name = new CTextEditInPlace();
+ m_Name->SetAlignment(CTextEditInPlace::LEFT);
+ m_Name->SetTextColor(m_TextColor);
+ m_Name->SetBGColorNoFocus(CStudioPreferences::GetSelectColor());
+ m_Name->SetFillBackground(false);
+ m_Name->AllowAutoSize(true);
+
+ // Name change listener
+ m_NameChangeListener =
+ new CSpecificCCommitDataListener<CTreeItem>(this, &CTreeItem::OnNameChanged);
+ m_Name->AddCommitListener(m_NameChangeListener);
+
+ // Toggle listener
+ m_Toggle->SigToggle.connect(std::bind(&CTreeItem::OnToggleExpansion, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ // Add all the controls to the flow
+ AddChild(m_Toggle);
+ AddChild(m_Icon);
+ AddChild(m_Name);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTreeItem::~CTreeItem()
+{
+ m_Name->RemoveCommitListener(m_NameChangeListener);
+ delete m_NameChangeListener;
+
+ // Clear out our list of subitems, but don't delete anything (whoever called new should do the
+ // delete)
+ m_ItemList.clear();
+ delete m_Toggle;
+ delete m_Icon;
+ delete m_Name;
+
+ REMOVEFROM_OBJECT_COUNTER(CTreeItem)
+}
+
+//=============================================================================
+/**
+ * Fills in the background color of this control.
+ * @param inRenderer renderer to draw to
+ */
+void CTreeItem::Draw(CRenderer *inRenderer)
+{
+ inRenderer->FillSolidRect(QRect({}, GetSize()), m_Color);
+}
+
+//=============================================================================
+/**
+ * Allows you to set the background color of this control.
+ * @param inColor new background color for this control
+ */
+void CTreeItem::SetBackgroundColor(const CColor &inColor)
+{
+ m_Color = inColor;
+ Invalidate();
+}
+
+void CTreeItem::SetTextColor(const CColor &inColor)
+{
+ m_Name->SetTextColor(inColor);
+ m_TextColor = inColor;
+ Invalidate();
+}
+
+void CTreeItem::SetTextBGFocusColor(const ::CColor &inColor)
+{
+ m_Name->SetBGColorNoFocus(inColor);
+ Invalidate();
+}
+//=============================================================================
+/**
+ * @return the current background color of this control
+ */
+CColor CTreeItem::GetBackgroundColor()
+{
+ return m_Color;
+}
+
+//=============================================================================
+/**
+ * Sets the parent of this tree item. The parent is the tree item that can
+ * be toggled to hide or show this item. You can set this value to nullptr to
+ * specify that this item is a root item, thus making it always be visible.
+ * @param inParent parent of this item
+ */
+void CTreeItem::SetParentItem(CTreeItem *inParent)
+{
+ m_Parent = inParent;
+}
+
+//=============================================================================
+/**
+ * Allows you to access the parent of this tree item. Note that this parent is
+ * not the same as parenting in the Control hierarchy. The parent is the tree
+ * item that can be toggled to hide or show this item.
+ * @return the parent of this tree item, or nullptr if this is a root level item.
+ */
+CTreeItem *CTreeItem::GetParentItem()
+{
+ return m_Parent;
+}
+
+//=============================================================================
+/**
+ * Allows you to change the icon associated with this tree item.
+ * @param inIcon name of the icon in normal state
+ * @param inSelectedIcon name of the icon to be displayed when this item is selected
+ */
+void CTreeItem::SetIcon(const QString &inIcon, const QString &inSelectedIcon)
+{
+ SetIcon(CResourceCache::GetInstance()->GetBitmap(inIcon),
+ CResourceCache::GetInstance()->GetBitmap(inSelectedIcon));
+}
+
+//=============================================================================
+/**
+ * Allows you to change the icon associated with this tree item.
+ */
+void CTreeItem::SetIcon(const QPixmap &inNormalIcon, const QPixmap &inSelectedIcon)
+{
+ m_NormalIcon = inNormalIcon;
+ m_SelectedIcon = inSelectedIcon;
+
+ m_Icon->SetImage(m_NormalIcon);
+}
+
+//=============================================================================
+/**
+ * Allows you to specifiy whether or not the name of this tree item can be edited.
+ * If set to true, the user can edit an item's name by double-clicking on it.
+ * @param inIsEditable true to specify that the user is allowed to edit this
+ * item, otherwise false to prevent double-click editing.
+ */
+void CTreeItem::SetIsNameEditable(bool inIsEditable)
+{
+ m_Name->SetEditable(inIsEditable);
+}
+
+//=============================================================================
+/**
+ * Sets the text displayed by this tree item. This does not trigger a name
+ * change event.
+ * @param inText text to be displayed
+ */
+void CTreeItem::SetText(const Q3DStudio::CString &inText)
+{
+ m_Name->SetData(inText);
+}
+
+//=============================================================================
+/**
+ * Gets the text displayed by this tree item.
+ * @return the text that is displayed.
+ */
+Q3DStudio::CString CTreeItem::GetText()
+{
+ return m_Name->GetString();
+}
+
+//=============================================================================
+/**
+ * Called when the name of the tree item is changed. Notifies any interested
+ * listeners. This is only for when the name control is edited directly. If
+ * you call SetText directly or change the text by other means, no listeners
+ * are notified.
+ * @param inControl control that generated the name change message (not used)
+ */
+void CTreeItem::OnNameChanged(CControl *inControl)
+{
+ Q_UNUSED(inControl);
+
+ // Maintain the color scheme based upon whether or not this item is selected
+ if (m_ShowVisibleSelection) {
+ if (m_IsSelected) {
+ m_Name->SetFillBackground(true);
+ m_Name->SetTextColor(CColor(255, 255, 255));
+ } else {
+ m_Name->SetFillBackground(false);
+ m_Name->SetTextColor(m_TextColor);
+ }
+ }
+
+ // TODO: notify listeners
+}
+
+//=============================================================================
+/**
+ * Allows you to specify the indent level of this tree item. Generally, this
+ * is called automatically when adding a child item. An item at level 0 is a
+ * root item, an item at level 1 is a child of a root item, and so on. The
+ * indent level is multiplied by a fixed amount to cause the control to appear
+ * indented.
+ * @param inAmount number indicating how many children deep this item is
+ * @param inIsRecursive set to true to recursively set the indent level of all
+ * children in addition to this item. Each child is set at one more indent
+ * level than its parent.
+ */
+void CTreeItem::SetIndent(long inAmount, bool inIsRecursive)
+{
+ m_IndentLevel = inAmount;
+
+ SetLeftMargin((m_IndentLevel * m_IndentSize) + m_ControlGap);
+
+ if (inIsRecursive) {
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theChild = *thePos;
+ theChild->SetIndent(m_IndentLevel + 1, inIsRecursive);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Handles clicking on this item and selects or deselects it appropriately.
+ * @param inPoint location of the mouse
+ * @param inFlags key modifier states at time of mouse event
+ */
+bool CTreeItem::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CFlowLayout::OnMouseDown(inPoint, inFlags)) {
+ // If the name is being edited, but was not clicked on, force it to lose focus
+ if (m_Name->GetEditMode() && !m_Name->HitTest(inPoint))
+ m_Name->OnLoseFocus();
+
+ // The item needs to see if the control key is down, because that might mean that it needs
+ // to deselect itself
+ bool theControlKeyIsDown =
+ (inFlags & CHotKeys::MODIFIER_CONTROL) == CHotKeys::MODIFIER_CONTROL;
+
+ // If the control key is down and we are currently selected, deselect ourselves
+ if (theControlKeyIsDown && IsSelected())
+ m_TreeControl->Deselect(this);
+ // Otherwise, go ahead and perform a normal select command
+ else
+ m_TreeControl->Select(this, inFlags);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * If this item is a container, double-click will expand it or collapse it.
+ * @param inPoint location of mouse at time of event
+ * @param inFlags modifier flags at time of event
+ */
+bool CTreeItem::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Let the children have the chance to handle the message
+ bool theMessageWasHandled = CFlowLayout::OnMouseDoubleClick(inPoint, inFlags);
+
+ // If no one else handled it, we'll just go ahead and expand
+ if (!theMessageWasHandled) {
+ // If this item is a container, toggle whether or not it's expanded
+ if (IsContainer())
+ ToggleExpansion();
+ }
+
+ return theMessageWasHandled;
+}
+
+//=============================================================================
+/**
+ * Handles right clicking on this item and selects or deselects it appropriately.
+ * @param inPoint location of the mouse
+ * @param inFlags key modifier states at time of mouse event
+ * @return true if the event was consumed, false if it should continue to propagate
+ */
+bool CTreeItem::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theMessageWasHandled = CFlowLayout::OnMouseRDown(inPoint, inFlags);
+ if (!theMessageWasHandled) {
+ // If the name is being edited, but was not clicked on, force it to lose focus
+ if (m_Name->GetEditMode() && !m_Name->HitTest(inPoint))
+ m_Name->OnLoseFocus();
+
+ // If this item is not already selected
+ if (!IsSelected()) {
+ // Select this tree item
+ m_TreeControl->Select(this, inFlags);
+ }
+ }
+
+ return theMessageWasHandled;
+}
+
+//=============================================================================
+/**
+ * @return true if this row is currently expanded (showing it's children).
+ */
+bool CTreeItem::IsExpanded()
+{
+ return m_IsExpanded;
+}
+
+//=============================================================================
+/**
+ * If the row is currently expanded, this call will collapse it, or vice-versa.
+ */
+void CTreeItem::ToggleExpansion()
+{
+ if (IsExpanded())
+ Collapse();
+ else
+ Expand();
+}
+
+//=============================================================================
+/**
+ * Expands this item to reveal all of its children.
+ */
+void CTreeItem::Expand()
+{
+ UICPROFILE(Expand);
+
+ if (!m_IsExpanded) {
+ m_TreeControl->OnItemExpanding(this);
+
+ bool theAllowRefresh = m_TreeControl->GetAllowRefresh();
+ m_TreeControl->SetAllowRefresh(false);
+
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ theItem->ShowChildItems();
+ }
+
+ m_IsExpanded = true;
+
+ m_TreeControl->SetAllowRefresh(theAllowRefresh);
+
+ // Expand/collapse might come from somewhere other than the button itself (see
+ // CTreeItem::OnMouseDoubleClick),
+ // so make sure we toggle it just in case
+ m_Toggle->SetToggleState(true);
+
+ m_TreeControl->OnItemExpanded(this);
+ }
+}
+
+//=============================================================================
+/**
+ * Collapses this row, hiding all of its children
+ */
+void CTreeItem::Collapse()
+{
+ if (m_IsExpanded) {
+ m_TreeControl->OnItemCollapsing(this);
+
+ bool theAllowRefresh = m_TreeControl->GetAllowRefresh();
+ m_TreeControl->SetAllowRefresh(false);
+
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ theItem->HideChildItems();
+ }
+
+ m_IsExpanded = false;
+
+ // Deselect all children if they are being hidden and see if any children were in fact
+ // selected
+ // If there were children that were selected, we need to add this item to the selection
+ // list, without
+ // interfering with the selection state of other items
+ if (DeselectChildren()) {
+ long theFlags = 0;
+
+ // If multiple selection is enabled, we need to make sure that we don't disrupt any
+ // other selection,
+ // so we simulate a control click on this item (selects this item in addition to
+ // whatever is already
+ // selected).
+ if (m_TreeControl->GetAllowMultipleSelection())
+ theFlags = CHotKeys::MODIFIER_CONTROL;
+
+ m_TreeControl->Select(this, theFlags);
+ }
+
+ m_TreeControl->SetAllowRefresh(theAllowRefresh);
+
+ // Expand/collapse might come from somewhere other than the button itself (see
+ // CTreeItem::OnMouseDoubleClick),
+ // so make sure we toggle it just in case
+ m_Toggle->SetToggleState(false);
+
+ m_TreeControl->OnItemCollapsed(this);
+ }
+}
+
+//=============================================================================
+/**
+ * Adds a tree item as a child of this one.
+ * @param inItem the new item to be added.
+ * @param inInsertBefore the item to insert the new one before, or nullptr to
+ * insert at the end of the list
+ */
+void CTreeItem::AddItem(CTreeItem *inItem, CTreeItem *inInsertBefore /*=nullptr*/)
+{
+ inItem->SetIndent(m_IndentLevel + 1, true);
+
+ inItem->SetVisible(IsExpanded());
+
+ if (!inInsertBefore) {
+ m_ItemList.push_back(inItem);
+ } else {
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ if (theItem == inInsertBefore) {
+ m_ItemList.insert(thePos, inItem);
+ break;
+ }
+ }
+ }
+
+ inItem->SetParentItem(this);
+
+ UpdateToggle();
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * The number of children this item has. Note that this is different than
+ * the GetChildCount( ) function on the base class CControl. The "children"
+ * counted by this function are the ones that have been added to this control
+ * to form a tree by calls to AddItem.
+ * @return the number of items added a "child tree items" to this one.
+ */
+long CTreeItem::GetNumChildItems()
+{
+ return (long)m_ItemList.size();
+}
+
+//=============================================================================
+/**
+ * Retrieve the child tree item based on it's position within m_ItemList
+ * @param inIndex position of the child tree item
+ * @return CTreeItem* ptr to child tree item or nullptr if inIndex is invalid.
+ */
+CTreeItem *CTreeItem::GetChildItem(long inIndex)
+{
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ std::advance(thePos, inIndex);
+
+ if (thePos != m_ItemList.end())
+ return (*thePos);
+
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Retrieve the index of a child tree item based on it's position within
+ * m_ItemList.
+ * @param inChildItem ptr to the child tree item
+ * @return long index to the child tree item or -1 if inChildItem is invalid.
+ */
+long CTreeItem::GetChildTreeItemIndex(const CTreeItem *inChildItem) const
+{
+ TItemList::const_iterator thePos = m_ItemList.begin();
+ TItemList::const_iterator theEnd = m_ItemList.end();
+
+ long theItemIndex(-1);
+ bool theIsFound(false);
+ for (; thePos != theEnd && !theIsFound; ++thePos) {
+ ++theItemIndex;
+
+ if ((*thePos) == inChildItem)
+ theIsFound = true;
+ }
+
+ if (theIsFound)
+ return theItemIndex;
+ else
+ return -1;
+}
+
+//=============================================================================
+/**
+ * Retrieves the necessary iterators for searching through this item's list
+ * of children. You should not need to access this directly. This is provided
+ * for use by the CTreeControl class.
+ * @param outListBegin returns an iterator pointing to the first element in the list
+ * @param outListEnd returns an iterator pointing to the last element
+ */
+void CTreeItem::GetItemIterator(TItemList::iterator &outListBegin, TItemList::iterator &outListEnd)
+{
+ outListBegin = m_ItemList.begin();
+ outListEnd = m_ItemList.end();
+}
+
+//=============================================================================
+/**
+ * Retrieves the necessary iterators for searching through this item's list
+ * of children (in reverse order). You should not need to access this directly.
+ * This is provided for use by the CTreeControl class.
+ * @param outListBegin returns a reverse iterator pointing to the last element in the list
+ * @param outListEnd returns a reverse iterator pointing to the first element
+ */
+void CTreeItem::GetReverseItemIterator(TItemList::reverse_iterator &outListBegin,
+ TItemList::reverse_iterator &outListEnd)
+{
+ outListBegin = m_ItemList.rbegin();
+ outListEnd = m_ItemList.rend();
+}
+
+//=============================================================================
+/**
+ * An item that is a container has a visible toggle that can be clicked to
+ * hide or show its children.
+ * @return true if this item is a parent and has children, otherwise false
+ */
+bool CTreeItem::IsContainer()
+{
+ return m_ItemList.size() > 0;
+}
+
+//=============================================================================
+/**
+ * @return true if this item is a root level item, false if it has a parent
+ */
+bool CTreeItem::IsRootItem()
+{
+ return (!m_Parent);
+}
+
+//=============================================================================
+/**
+ * Removes this tree item and all its children from the tree control. Recursively
+ * goes through all children and asks them to detach themselves as children of
+ * the tree control. No items are actually deleted, as this is deferred to
+ * the class that created the items.
+ */
+void CTreeItem::RemoveItem()
+{
+ // Enable or disable the toggle on the parent accordingly
+ if (m_Parent)
+ m_Parent->UpdateToggle();
+
+ // Recursively remove all children
+ RemoveChildren();
+
+ // If this item has a parent, ask the parent to remove us from it's list of children
+ if (m_Parent)
+ m_Parent->RemoveFromList(this);
+
+ // Remove this item from the control hierarchy
+ m_TreeControl->RemoveChild(this);
+}
+
+//=============================================================================
+/**
+ * Updates the visibility of the expand toggle. If the the item is a container,
+ * the toggle is shown, otherwise it is hidden.
+ */
+void CTreeItem::UpdateToggle()
+{
+ UICPROFILE(UpdateToggle);
+
+ bool theEnableToggle = IsContainer();
+ bool theToggleIsEnabled = m_Toggle->IsEnabled();
+
+ if (theEnableToggle != theToggleIsEnabled) {
+ m_Toggle->SetEnabled(theEnableToggle);
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Recursively removes all children from the child list and from the control
+ * hierarchy maintained by the tree control.
+ */
+void CTreeItem::RemoveChildren()
+{
+ // If this item has children
+ if (IsContainer()) {
+ // Iterate through each child
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ // And recursively remove the child's children
+ CTreeItem *theItem = *thePos;
+ theItem->RemoveChildren();
+
+ // Ask the tree control to remove this child from the control hierarchy
+ m_TreeControl->RemoveChild(theItem);
+ }
+
+ // Clear the child list since we deleted all the children
+ m_ItemList.clear();
+ }
+}
+
+//=============================================================================
+/**
+ * Helper function for item removal. Goes through the list of child items that
+ * belongs to this control and removes the specified item from the list.
+ * @param inItem item to be removed from list
+ */
+void CTreeItem::RemoveFromList(CTreeItem *inItem)
+{
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ if (theItem == inItem) {
+ m_ItemList.erase(thePos);
+ break;
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Detaches this item from its parent.
+ */
+void CTreeItem::Detach()
+{
+ if (m_Parent)
+ m_Parent->RemoveFromList(this);
+}
+
+//=============================================================================
+/**
+ * @return true if this item is currently selected, otherwise false.
+ */
+bool CTreeItem::IsSelected()
+{
+ return m_IsSelected;
+}
+
+//=============================================================================
+/**
+ * Selects or deselects this row. Color of the selected item changes, as well
+ * as the item's icon.
+ * @param inIsSelected true to select this item, false to deselect it
+ */
+void CTreeItem::SetSelected(bool inIsSelected)
+{
+ // If this command is actually resulting in a change of state
+ if (m_IsSelected != inIsSelected) {
+ m_IsSelected = inIsSelected;
+
+ if (m_ShowVisibleSelection) {
+ // If the item is to be selected
+ if (m_IsSelected) {
+ // Change to the selection color
+ m_Name->SetFillBackground(true);
+
+ if (!m_SelectedIcon.isNull())
+ m_Icon->SetImage(m_SelectedIcon);
+ }
+ // Otherwise, the item is to be deselected
+ else {
+ // Change to the normal color scheme
+ m_Name->SetFillBackground(false);
+ m_Name->SetTextColor(m_TextColor);
+
+ if (!m_NormalIcon.isNull())
+ m_Icon->SetImage(m_NormalIcon);
+ }
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Deselects all children, grandchildren, and so on who are currently selected.
+ * @return true if there were any selected children who were deselected
+ */
+bool CTreeItem::DeselectChildren()
+{
+ bool theChildSelection = false;
+
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theChild = *thePos;
+
+ if (theChild->IsContainer()) {
+ bool theRecursiveCheck = theChild->DeselectChildren();
+ if (!theChildSelection)
+ theChildSelection = theRecursiveCheck;
+ }
+
+ if (theChild->IsSelected()) {
+ m_TreeControl->Deselect(theChild);
+ theChildSelection = true;
+ }
+ }
+
+ return theChildSelection;
+}
+
+//=============================================================================
+/**
+ * Creates the toggle control used by this tree item.
+ */
+void CTreeItem::CreateToggle()
+{
+ m_Toggle = new CToggleButton();
+
+ // Make sure these sizes are accurate
+ QSize theImageSize = CResourceCache::GetInstance()->GetBitmap("arrow.png").size();
+ m_Toggle->SetUpImage("arrow.png");
+
+ m_Toggle->SetDownImage("arrow_down.png");
+ m_Toggle->SetDisabledImage("empty-pixel.png");
+ m_Toggle->SetAbsoluteSize({theImageSize.width(), theImageSize.height()});
+ m_Toggle->SetEnabled(false);
+
+ // This is to get around a problem in SetIndent when the parent control is 0
+ m_IndentSize = theImageSize.width();
+}
+
+//=============================================================================
+/**
+ * Called when the toggle button for this item is clicked on. Expands the item
+ * if is currently collapsed, otherwise, collapses the row.
+ * @param inButton button that generated this event
+ * @param inState state of the button after the toggle event
+ */
+void CTreeItem::OnToggleExpansion(CToggleButton *inButton, CButtonControl::EButtonState inState)
+{
+ Q_UNUSED(inState);
+
+ if (inButton == m_Toggle)
+ ToggleExpansion();
+}
+
+//=============================================================================
+/**
+ * Recursive function to help change the visiblity of all children of this item.
+ */
+void CTreeItem::HideChildItems()
+{
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ theItem->HideChildItems();
+ }
+
+ SetVisible(false);
+}
+
+//=============================================================================
+/**
+ * Recursive function to help change the visiblity of all children of this item.
+ * If a child is expanded, this function is called recursively to reveal all of
+ * the grandchildren, and so on.
+ */
+void CTreeItem::ShowChildItems()
+{
+ if (m_Parent)
+ SetVisible(m_Parent->IsVisible());
+ else
+ SetVisible(true);
+
+ TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ CTreeItem *theItem = *thePos;
+ if (IsExpanded())
+ theItem->ShowChildItems();
+ }
+}
+
+//=============================================================================
+/**
+ * Private function for adding children to this control. The only children
+ * should be a toggle, an icon, and a text control for the name. If you
+ * need to add another tree item as a "child" of this one, you should use
+ * AddItem( ).
+ * @param inControl Control to be added as a child to this one
+ * @param inInsertBefore Control to insert before in the hierarchy or nullptr to
+ * insert at the end of the list.
+ */
+void CTreeItem::AddChild(CControl *inControl, CControl *inInsertBefore /*= nullptr*/)
+{
+ CFlowLayout::AddChild(inControl, inInsertBefore);
+}
+
+//=============================================================================
+/**
+ * Find the item that this item should be added after to maintian sort order.
+ */
+CTreeItem *CTreeItem::FindPrevSortSibling(CTreeItem *)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ UIC_THROW(E_FAIL);
+#endif
+ return NULL;
+}
+
+//=============================================================================
+/**
+ * Find the item that this item should be added before to maintian sort order.
+ */
+CTreeItem *CTreeItem::FindNextSortSibling(CTreeItem *inChild)
+{
+ CTreeItem *theSortItem = nullptr;
+
+ // No items in the list - return nullptr (because we don't care)
+ if (m_ItemList.size()) {
+ CTreeItem::TItemList::iterator thePos = m_ItemList.begin();
+ for (; thePos != m_ItemList.end(); ++thePos) {
+ // Item in the list
+ theSortItem = *thePos;
+ if (m_TreeControl->IsItemLess(inChild, theSortItem))
+ break;
+ }
+
+ // Last item in the list
+ if (thePos == m_ItemList.end())
+ theSortItem = nullptr;
+ }
+
+ return theSortItem;
+}
+
+/**
+ * Retrieve the edit state of the text edit control associated with this tree item
+ */
+bool CTreeItem::GetEditMode()
+{
+ if (m_Name) {
+ return m_Name->GetEditMode();
+ }
+
+ return false;
+}
+
+//=============================================================================
+/**
+ * Sets whether item is 'visibly' selected
+ */
+void CTreeItem::SetVisibleSelection(bool inValue)
+{
+ m_ShowVisibleSelection = inValue;
+}
+
+//=============================================================================
+/**
+ * Gets whether item is 'visibly' selected
+ */
+bool CTreeItem::GetVisibleSelection() const
+{
+ return m_ShowVisibleSelection;
+}
diff --git a/src/Authoring/Studio/Controls/TreeItem.h b/src/Authoring/Studio/Controls/TreeItem.h
new file mode 100644
index 00000000..78cb54bb
--- /dev/null
+++ b/src/Authoring/Studio/Controls/TreeItem.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TREE_ITEM_H
+#define INCLUDED_TREE_ITEM_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FlowLayout.h"
+#include "ToggleButton.h"
+#include "TextEditInPlace.h"
+
+#include <QPixmap>
+//==============================================================================
+// Forwards
+//==============================================================================
+class CTreeControl;
+class CRenderer;
+class CSIcon;
+class CTreeItem;
+class CTest_TreeControl;
+
+//=============================================================================
+/**
+ * Class encapsulating a single row in a tree control. Manages the toggle, the
+ * icon, and the name of the tree item. When you create a tree item you must
+ * associate it with a tree control. Then you can specify it's parent when you
+ * call AddItem( ).
+ */
+class CTreeItem : public CFlowLayout
+{
+public:
+ typedef std::vector<CTreeItem *> TItemList;
+
+public:
+ CTreeItem(CTreeControl *inTreeControl);
+ virtual ~CTreeItem();
+
+ DEFINE_OBJECT_COUNTER(CTreeItem)
+
+ void Draw(CRenderer *inRenderer) override;
+
+ void SetBackgroundColor(const ::CColor &inColor);
+ ::CColor GetBackgroundColor();
+ void SetTextColor(const ::CColor &inColor);
+ void SetTextBGFocusColor(const ::CColor &inColor);
+
+ void SetParentItem(CTreeItem *inParent);
+ CTreeItem *GetParentItem();
+ bool GetEditMode();
+
+ virtual void SetIcon(const QString &inIcon,
+ const QString &inSelectedIcon);
+ virtual void SetIcon(const QPixmap &inNormalIcon, const QPixmap &inSelectedIcon);
+ void SetIsNameEditable(bool inIsEditable);
+ virtual void SetText(const Q3DStudio::CString &inText);
+ virtual Q3DStudio::CString GetText();
+ virtual void OnNameChanged(CControl *inControl);
+ void SetIndent(long inAmount, bool inIsRecursive);
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ bool IsExpanded();
+ virtual void ToggleExpansion();
+ virtual void Expand();
+ virtual void Collapse();
+
+ virtual void AddItem(CTreeItem *inItem, CTreeItem *inInsertBefore = nullptr);
+ long GetNumChildItems();
+ CTreeItem *GetChildItem(long inIndex);
+ long GetChildTreeItemIndex(const CTreeItem *inChildItem) const;
+ void GetItemIterator(TItemList::iterator &outListBegin, TItemList::iterator &outListEnd);
+ void GetReverseItemIterator(TItemList::reverse_iterator &outListBegin,
+ TItemList::reverse_iterator &outListEnd);
+ virtual bool IsContainer();
+ bool IsRootItem();
+ virtual void RemoveItem();
+ void UpdateToggle();
+ virtual void RemoveChildren();
+ void RemoveFromList(CTreeItem *inItem);
+ virtual void Detach();
+ virtual CTreeItem *FindPrevSortSibling(CTreeItem *inChild);
+ virtual CTreeItem *FindNextSortSibling(CTreeItem *inChild);
+
+ bool IsSelected();
+ virtual void SetSelected(bool inIsSelected);
+ bool DeselectChildren();
+
+ void SetVisibleSelection(bool inValue);
+ bool GetVisibleSelection() const;
+
+ boost::signal1<void, CControl *> SigToggle;
+
+protected:
+ // Member Vars
+ CTreeControl *m_TreeControl;
+ CTreeItem *m_Parent;
+ CToggleButton *m_Toggle;
+ CTextEditInPlace *m_Name;
+ CCommitDataListener *m_NameChangeListener;
+ CSIcon *m_Icon;
+ QPixmap m_NormalIcon;
+ QPixmap m_SelectedIcon;
+ TItemList m_ItemList;
+ long m_ControlGap;
+ long m_IndentLevel;
+ long m_IndentSize;
+ bool m_IsExpanded;
+ bool m_IsSelected;
+ bool m_ShowVisibleSelection;
+ ::CColor m_Color;
+ ::CColor m_TextColor;
+
+ // Member Functions
+ void CreateToggle();
+ void OnToggleExpansion(CToggleButton *inButton, CButtonControl::EButtonState inState);
+ void HideChildItems();
+ void ShowChildItems();
+
+private:
+ /// Private because no one outside this class should be calling it; use AddItem( ) instead.
+ void AddChild(CControl *inControl, CControl *inInsertBefore = nullptr) override;
+};
+#endif // INCLUDED_TREE_ITEM_H
diff --git a/src/Authoring/Studio/Controls/WidgetControl.cpp b/src/Authoring/Studio/Controls/WidgetControl.cpp
new file mode 100644
index 00000000..ae45468a
--- /dev/null
+++ b/src/Authoring/Studio/Controls/WidgetControl.cpp
@@ -0,0 +1,439 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "WidgetControl.h"
+
+#include "Control.h"
+#include "DropSource.h"
+#include "IDragable.h"
+#include "OffscreenRenderer.h"
+#include "Pt.h"
+#include "Rct.h"
+#include "UICFile.h"
+
+#include <QDrag>
+#include <QFocusEvent>
+#include <QKeyEvent>
+#include <QMenu>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QShowEvent>
+#include <QWheelEvent>
+
+WidgetControl::WidgetControl(CControl *control, QWidget *parent)
+ : QWidget(parent)
+ , m_control(control)
+ , m_controlListener(this)
+{
+ Q_ASSERT(control);
+ control->SetWindowListener(&m_controlListener);
+ setControlSize(sizeHint());
+}
+
+bool WidgetControl::event(QEvent *event)
+{
+ if (event->type() == QEvent::ShortcutOverride) {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ m_control->OnKeyDown(ke->key(), ke->modifiers());
+ }
+ return QWidget::event(event);
+}
+
+void WidgetControl::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+}
+
+void WidgetControl::paintEvent(QPaintEvent *event)
+{
+ QPainter painter(this);
+ const auto boundRect = QRect(QPoint(0,0), size());
+ CWinRenderer renderer(&painter, boundRect);
+ CRct rect(event->rect());
+ m_control->OnDraw(&renderer, rect, true);
+
+ QWidget::paintEvent(event);
+}
+
+void WidgetControl::resizeEvent(QResizeEvent *event)
+{
+ setControlSize(event->size());
+ QWidget::resizeEvent(event);
+}
+
+void WidgetControl::keyPressEvent(QKeyEvent *event)
+{
+ m_control->OnKeyDown(event->key(), event->modifiers());
+ m_control->OnChar(event->text(), event->modifiers());
+ QWidget::keyPressEvent(event);
+}
+
+void WidgetControl::keyReleaseEvent(QKeyEvent *event)
+{
+ m_control->OnKeyUp(event->key(), event->modifiers());
+ QWidget::keyReleaseEvent(event);
+}
+
+void WidgetControl::mousePressEvent(QMouseEvent *event)
+{
+ const auto pos = CPt(event->pos());
+ if (m_isLeftMouseDown)
+ m_control->OnMouseUp(pos, event->modifiers());
+
+ m_isLeftMouseDown = (event->button() == Qt::LeftButton);
+ if (m_isLeftMouseDown)
+ m_control->OnMouseDown(pos, event->modifiers());
+ else
+ m_control->OnMouseRDown(pos, event->modifiers());
+
+ setFocus();
+ QWidget::mousePressEvent(event);
+}
+
+void WidgetControl::mouseReleaseEvent(QMouseEvent *event)
+{
+ const auto pos = CPt(event->pos());
+ if (event->button() == Qt::LeftButton) {
+ m_isLeftMouseDown = false;
+ m_control->OnMouseUp(pos, event->modifiers());
+ } else {
+ m_control->OnMouseRUp(pos, event->modifiers());
+ }
+
+ QWidget::mouseReleaseEvent(event);
+}
+
+void WidgetControl::mouseMoveEvent(QMouseEvent *event)
+{
+ m_control->OnMouseMove(event->pos(), event->modifiers());
+ QWidget::mouseMoveEvent(event);
+}
+
+void WidgetControl::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ // call QWidget handler first to not deliver OnMouseDown after OnMouseDoubleClick
+ QWidget::mouseDoubleClickEvent(event);
+ m_control->OnMouseDoubleClick(event->pos(), event->modifiers());
+}
+
+void WidgetControl::wheelEvent(QWheelEvent *event)
+{
+ m_control->OnMouseWheel(event->pos(), event->angleDelta().y(), event->modifiers());
+ QWidget::wheelEvent(event);
+}
+
+void WidgetControl::enterEvent(QEvent *event)
+{
+ setMouseTracking(true);
+ m_control->OnMouseHover(mapFromGlobal(QCursor::pos()), {});
+ QWidget::enterEvent(event);
+}
+
+void WidgetControl::leaveEvent(QEvent *event)
+{
+ setMouseTracking(false);
+ m_control->OnMouseOut(mapFromGlobal(QCursor::pos()), {});
+ QWidget::leaveEvent(event);
+}
+
+void WidgetControl::focusInEvent(QFocusEvent *event)
+{
+ m_control->OnGainFocus();
+ QWidget::focusInEvent(event);
+}
+
+void WidgetControl::focusOutEvent(QFocusEvent *event)
+{
+ if (!m_isContextMenuShown)
+ m_control->OnLoseFocus();
+ QWidget::focusOutEvent(event);
+}
+
+QSize WidgetControl::sizeHint() const
+{
+ const auto preferredSize = m_control->GetPreferredSize();
+ return QSize(preferredSize.x, preferredSize.y);
+}
+
+/*
+ * CPaletteManager::GetTimelineControl() needs a way of accessing
+ * the CControl inside the widget
+ */
+CControl *WidgetControl::getControl() const
+{
+ return m_control;
+}
+
+void WidgetControl::setControlSize(const QSize &size)
+{
+ m_control->SetSize(size.width(), size.height());
+}
+
+void WidgetControl::DoStartDrag(IDragable *inDragable)
+{
+ if (m_isDragging || !m_isLeftMouseDown)
+ return;
+
+ QDrag drag(this);
+ m_isDragging = true;
+
+ drag.setMimeData(CDropSourceFactory::Create(inDragable->GetFlavor(), inDragable));
+ drag.exec();
+ m_isLeftMouseDown = false;
+
+ m_isDragging = false;
+}
+
+void WidgetControl::DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList)
+{
+ if (m_isDragging || !m_isLeftMouseDown)
+ return;
+
+ QDrag drag(this);
+ m_isDragging = true;
+
+ try {
+ auto thePos = inDragFileNameList.begin();
+ auto theEndPos = inDragFileNameList.end();
+
+ Q3DStudio::CAutoMemPtr<CUICFile> theDragFile;
+ for (; thePos != theEndPos; ++thePos) {
+ Q3DStudio::CString theDragFileName = *thePos;
+ if (theDragFileName.Length() > 0) {
+ theDragFile = new CUICFile(theDragFileName);
+ CDropSource *theDropSource = CDropSourceFactory::Create(
+ EUIC_FLAVOR_ASSET_UICFILE, (void *)theDragFile, sizeof(theDragFile));
+ // Add the UIC_GESTURE_FLAVOR. This will allow us to drag to StudioControls.
+ drag.setMimeData(theDropSource);
+ break;
+ }
+ }
+ drag.exec();
+ m_isLeftMouseDown = false;
+ } catch (...) { // if there are any errors that throws an exception, there
+ // there will be no more drag and drop, since the flag will not be reset.
+ }
+
+ m_isDragging = false;
+}
+
+bool WidgetControl::OnDragWithin(CDropSource &inSource)
+{
+ bool theReturn = false;
+ CPt thePoint = inSource.GetCurrentPoint();
+ Qt::KeyboardModifiers theFlags = inSource.GetCurrentFlags();
+ CDropTarget *theDropTarget = m_control->FindDropCandidate(thePoint, theFlags);
+
+ if (theDropTarget) {
+ theReturn = theDropTarget->Accept(inSource);
+ delete theDropTarget;
+ }
+ return theReturn;
+}
+
+bool WidgetControl::OnDragReceive(CDropSource &inSource)
+{
+ bool theReturn = false;
+ CPt thePoint = inSource.GetCurrentPoint();
+ Qt::KeyboardModifiers theFlags = inSource.GetCurrentFlags();
+
+ CDropTarget *theDropTarget = m_control->FindDropCandidate(thePoint, theFlags);
+
+ if (theDropTarget) {
+ theReturn = theDropTarget->Drop(inSource);
+ delete theDropTarget;
+ }
+ return theReturn;
+}
+
+void WidgetControl::OnDragLeave()
+{
+ m_control->OnMouseMove(CPt(-1, -1), 0);
+}
+
+void WidgetControl::OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Notify the control that the mouse moved
+ m_control->OnMouseMove(inPoint, inFlags /*CHotKeys::GetCurrentKeyModifiers( )*/);
+
+ // If the control invalidated because of a mouse event then we want to do an immediate redraw.
+ // this ensures consistent visible feedback.
+ if (m_control->IsChildInvalidated())
+ repaint();
+}
+
+//=============================================================================
+/**
+ * Creates the pass thru class for the wnd control.
+ */
+WidgetControlControlListener::WidgetControlControlListener(WidgetControl *inParent)
+{
+ m_Parent = inParent;
+}
+
+WidgetControlControlListener::~WidgetControlControlListener()
+{
+}
+
+//=============================================================================
+/**
+ * Notification from the control that the window was invalidated.
+ */
+void WidgetControlControlListener::OnControlInvalidated()
+{
+ m_Parent->update();
+}
+
+//=============================================================================
+/**
+ * Notification from the control to do a popup at the specified location.
+ */
+long WidgetControlControlListener::DoPopup(QMenu *inMenu, CPt inPoint)
+{
+ long selectedIndex = -1;
+ if (inMenu) {
+ m_Parent->setContextMenuShown(true);
+ auto action = inMenu->exec(m_Parent->mapToGlobal(inPoint));
+ m_Parent->setContextMenuShown(false);
+ if (action)
+ selectedIndex = inMenu->actions().indexOf(action);
+ }
+
+ return selectedIndex;
+}
+
+//=============================================================================
+/**
+ * Get the location of the point in Screen coordinates.
+ */
+CPt WidgetControlControlListener::ClientToScreen(CPt inPoint)
+{
+ return m_Parent->mapToGlobal(inPoint);
+}
+
+//=============================================================================
+/**
+ * Get the location of the point into client coordinates.
+ */
+CPt WidgetControlControlListener::ScreenToClient(CPt inPoint)
+{
+ return m_Parent->mapFromGlobal(inPoint);
+}
+
+//=============================================================================
+/**
+ * Get the platform dependent view that this is embedding.
+ * Used when platform dependent controls need to be embedded into the Controls.
+ */
+TPlatformView WidgetControlControlListener::GetPlatformView()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ return m_Parent->m_hWnd;
+#else
+ return nullptr;
+#endif
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's SetIsDragging function.
+ * @param inIsDragging true to specify that a drag is occurring or false to cancel a drag
+ */
+void WidgetControlControlListener::SetIsDragging(bool inIsDragging)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ m_Parent->SetIsDragging(inIsDragging);
+#endif
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's ShowTooltips function.
+ * @param inLocation mid-point of the tooltip in global coordinates
+ * @param inText text to display as a tooltip
+ */
+void WidgetControlControlListener::ShowTooltips(CPt inLocation, const Q3DStudio::CString &inText)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ m_Parent->ShowTooltips(inLocation, inText);
+#endif
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's HideTooltips function.
+ */
+void WidgetControlControlListener::HideTooltips()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ m_Parent->HideTooltips();
+#endif
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's ShowMoveableTooltips function.
+ * @param inLocation mid-point of the tooltip in global coordinates
+ * @param inText text to display as a tooltip
+ */
+void WidgetControlControlListener::ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText,
+ CRct inBoundingRct)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ m_Parent->ShowMoveableWindow(inLocation, inText, inBoundingRct);
+#endif
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's HideMoveableTooltips function.
+ */
+void WidgetControlControlListener::HideMoveableWindow()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ m_Parent->HideMoveableWindow();
+#endif
+}
+
+void WidgetControlControlListener::DoStartDrag(IDragable *inDragable)
+{
+ m_Parent->DoStartDrag(inDragable);
+}
+
+//===============================================================================
+/**
+* performs a drag operation on a file
+*/
+void WidgetControlControlListener::DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList)
+{
+ m_Parent->DoStartDrag(inDragFileNameList);
+}
+
+
+
diff --git a/src/Authoring/Studio/Controls/WidgetControl.h b/src/Authoring/Studio/Controls/WidgetControl.h
new file mode 100644
index 00000000..8c30f1bb
--- /dev/null
+++ b/src/Authoring/Studio/Controls/WidgetControl.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WIDGETCONTROL_H
+#define WIDGETCONTROL_H
+
+#include <QWidget>
+
+#include "Control.h"
+#include "DropContainer.h"
+
+class CRenderer;
+class WidgetControl;
+
+class WidgetControlControlListener : public CControlWindowListener
+{
+public:
+ WidgetControlControlListener(WidgetControl *inParent);
+ virtual ~WidgetControlControlListener();
+
+ void OnControlInvalidated() override;
+ long DoPopup(QMenu *inMenu, CPt inLocation) override;
+ CPt ClientToScreen(CPt inPoint) override;
+ CPt ScreenToClient(CPt inPoint) override;
+ TPlatformView GetPlatformView() override;
+ void SetIsDragging(bool inIsDragging) override;
+ void ShowTooltips(CPt inLocation, const Q3DStudio::CString &inText) override;
+ void HideTooltips() override;
+ void DoStartDrag(IDragable *inDragable) override;
+ void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList) override;
+ void ShowMoveableWindow(CPt inLocation, const Q3DStudio::CString &inText, CRct inBoundingRct) override;
+ void HideMoveableWindow() override;
+
+protected:
+ WidgetControl *m_Parent;
+};
+class WidgetControl : public QWidget, public CWinDropContainer
+{
+ Q_OBJECT
+ friend class ::WidgetControlControlListener;
+public:
+ explicit WidgetControl(CControl *control, QWidget *parent = nullptr);
+ void setContextMenuShown(bool shown) { m_isContextMenuShown = shown; }
+
+protected:
+ bool event(QEvent *event) override;
+ void showEvent(QShowEvent *event) override;
+ void paintEvent(QPaintEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+ void keyReleaseEvent(QKeyEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+ void wheelEvent(QWheelEvent *event) override;
+ void enterEvent(QEvent *event) override;
+ void leaveEvent(QEvent *event) override;
+ void focusInEvent(QFocusEvent *event) override;
+ void focusOutEvent(QFocusEvent *event) override;
+
+public:
+ QSize sizeHint() const override;
+ CControl *getControl() const;
+
+protected:
+ void DoStartDrag(IDragable *inDragable);
+ void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList);
+
+ bool OnDragWithin(CDropSource &inSource) override;
+ bool OnDragReceive(CDropSource &inSource) override;
+ void OnDragLeave() override;
+ void OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags) override;
+
+private:
+ void setControlSize(const QSize &size);
+
+ CControl *m_control;
+ bool m_isLeftMouseDown = false;
+ bool m_isDragging = false;
+ bool m_isContextMenuShown = false;
+ WidgetControlControlListener m_controlListener;
+};
+
+#endif // WIDGETCONTROL_H
diff --git a/src/Authoring/Studio/Docs/CmdLineOptions.txt b/src/Authoring/Studio/Docs/CmdLineOptions.txt
new file mode 100644
index 00000000..42610e8a
--- /dev/null
+++ b/src/Authoring/Studio/Docs/CmdLineOptions.txt
@@ -0,0 +1,27 @@
+-t, -test <Test Path> - Runs the specified Unit Test(s) (TEST_CMD_LINE)
+-n <File Name> - Normal operation (NORMAL)
+-replay - Replay File
+-export - Export the file. Usage: Studio.exe -export AMWFilename AMCFilename DOMPath [ slide index(optional and defaults to 1) ]
+-silent - Run silent run deep
+-refresh - Refresh resources
+
+Unused:
+-u, -xml <File Name> - TEST_XML
+-i, -testInfo - TEST_INFO
+
+It is possible to stack command line options, with left to right operation precedence. With the exception of
+-test and -silent which take effect regardless of ordering. Any failed operations would result in the
+aborting of subsequent operations, and a -1 (failure) value will be returned.
+
+Example 1:
+StudioC -silent -refresh myFile.uip -export myOutput.amc Scene.Layer.Component
+Refreshes the myFile.uip, then exports to myOutput.amc the component found at the indicated DOM path. No dialogs are
+displayed. Error messages are piped to stdout.
+
+Example 2:
+Studio -refresh myFile.uip myOutput.xgf -export
+Refreshes myFile.uip then exports to myOutput.xgf using the XGF exporter.
+
+Example 3:
+StudioC -silent -export myFile.uip -refresh myOutput.avi
+Exports myFile.uip to myOutput.avi THEN refreshes myFile.uip. Error messages are piped to stdout. \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.cpp b/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.cpp
new file mode 100644
index 00000000..089a9c97
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BasicObjectDropSource.h"
+#include "Doc.h"
+#include "DropTarget.h"
+
+#include "Dialogs.h"
+#include "Dispatch.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMDataCore.h"
+#include "IDocumentEditor.h"
+#include "ImportUtils.h"
+#include "BasicObjectsModel.h"
+#include "IDragable.h"
+#include "IDocSceneGraph.h"
+#include "UICTextRenderer.h"
+#include "HotKeys.h"
+#include "StudioUtils.h"
+
+#include <QDir>
+
+//===============================================================================
+/**
+ *
+ */
+CBasicObjectDropSource::CBasicObjectDropSource(long inFlavor, IDragable *inDragable)
+ : CDropSource(inFlavor, 0)
+ , m_IsIndependent(false)
+{
+ auto item = dynamic_cast<BasicObjectItem *>(inDragable);
+ if (item) {
+ m_ObjectType = item->objectType();
+ m_PrimitiveType = item->primitiveType();
+ }
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CBasicObjectDropSource::ValidateTarget(CDropTarget *inTarget)
+{
+ using namespace Q3DStudio;
+
+ EStudioObjectType targetType = (EStudioObjectType)inTarget->GetObjectType();
+ bool theValidTarget = false;
+
+ // the only thing we want to do from here is check the type.
+ theValidTarget =
+ CStudioObjectTypes::AcceptableParent((EStudioObjectType)m_ObjectType, targetType);
+
+ if (!theValidTarget) {
+ SetHasValidTarget(theValidTarget);
+ return theValidTarget;
+ } else {
+ if (CHotKeys::IsKeyDown(Qt::AltModifier) && targetType != OBJTYPE_SCENE
+ && targetType != OBJTYPE_COMPONENT) {
+ UICDM::CUICDMInstanceHandle theTarget = inTarget->GetInstance();
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ IDocumentReader &theReader(theDoc->GetDocumentReader());
+ UICDM::CUICDMSlideHandle toSlide = theReader.GetAssociatedSlide(theTarget);
+ ;
+
+ if (!theReader.IsMasterSlide(toSlide))
+ theValidTarget = false;
+ }
+
+ SetHasValidTarget(theValidTarget);
+ return theValidTarget;
+ }
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CBasicObjectDropSource::CanMove()
+{
+ return true;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CBasicObjectDropSource::CanCopy()
+{
+ return true;
+}
+
+CCmd *CBasicObjectDropSource::GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide)
+{
+ using namespace Q3DStudio;
+ using UICDM::ComposerObjectTypes;
+ using namespace std;
+
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ CPt thePoint;
+ // if ( CHotKeys::IsKeyDown( CHotKeys::KEY_MENU ) )
+ // thePoint = GetCurrentPoint();
+
+ long theStartTime = -1;
+ if (CHotKeys::IsKeyDown(Qt::ControlModifier))
+ theStartTime = theDoc->GetCurrentViewTime();
+
+ DocumentEditorInsertType::Enum theInsertType(ImportUtils::GetInsertTypeForDropType(inDestType));
+ ComposerObjectTypes::Enum theComposerType;
+ switch (m_ObjectType) {
+ case OBJTYPE_SCENE:
+ theComposerType = ComposerObjectTypes::Scene;
+ break;
+ case OBJTYPE_LAYER:
+ theComposerType = ComposerObjectTypes::Layer;
+ break;
+ case OBJTYPE_BEHAVIOR:
+ theComposerType = ComposerObjectTypes::Behavior;
+ break;
+ case OBJTYPE_MATERIAL:
+ theComposerType = ComposerObjectTypes::Material;
+ break;
+ case OBJTYPE_CAMERA:
+ theComposerType = ComposerObjectTypes::Camera;
+ break;
+ case OBJTYPE_LIGHT:
+ theComposerType = ComposerObjectTypes::Light;
+ break;
+ case OBJTYPE_MODEL:
+ theComposerType = ComposerObjectTypes::Model;
+ break;
+ case OBJTYPE_GROUP:
+ theComposerType = ComposerObjectTypes::Group;
+ break;
+ case OBJTYPE_IMAGE:
+ theComposerType = ComposerObjectTypes::Image;
+ break;
+ case OBJTYPE_TEXT:
+ theComposerType = ComposerObjectTypes::Text;
+ break;
+ case OBJTYPE_COMPONENT:
+ theComposerType = ComposerObjectTypes::Component;
+ break;
+ case OBJTYPE_ALIAS:
+ theComposerType = ComposerObjectTypes::Alias;
+ break;
+ case OBJTYPE_PATH:
+ theComposerType = ComposerObjectTypes::Path;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ theComposerType = ComposerObjectTypes::Unknown;
+ break;
+ }
+ if (theComposerType != ComposerObjectTypes::Unknown) {
+ if (theComposerType == ComposerObjectTypes::Text) {
+ // For Text, we need to check if user already has font file inside fonts folder
+ CFilePath theFontFile;
+ CFilePath theFontDir = CFilePath::CombineBaseAndRelative(theDoc->GetDocumentDirectory(),
+ CFilePath(L"fonts"));
+ if (!theFontDir.Exists()) {
+ // Create font dir if necessary
+ theFontDir.CreateDir(true);
+ } else {
+ // Recursively find the first font file in font dir
+ vector<CFilePath> theFiles;
+ theFontDir.RecursivelyFindFilesOfType(nullptr, theFiles, false);
+ for (size_t i = 0; i < theFiles.size(); ++i) {
+ if (CDialogs::IsFontFileExtension(theFiles[i].GetExtension())) {
+ // Reuse the font in fonts subdirectory
+ theFontFile = theFiles[i];
+ break;
+ }
+ }
+ }
+
+ if (theFontFile == L"") {
+ // If user doesn't have any font file, copy the default font file from Studio's res
+ // folder
+
+ CFilePath theResFontFile;
+
+ QDir theResFontDir(resourcePath() + "/Font");
+ Q_FOREACH (QFileInfo fontFile, theResFontDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot)) {
+ CString ext = CString::fromQString(fontFile.suffix());
+ if (CDialogs::IsFontFileExtension(ext)) {
+ theResFontFile = CString::fromQString(fontFile.absoluteFilePath());
+ theFontFile = CFilePath::CombineBaseAndRelative(theFontDir, CString::fromQString(fontFile.fileName()));
+ break;
+ }
+ }
+
+ if (theResFontFile == L"") {
+ QT3DS_ASSERT(false);
+ std::shared_ptr<IImportFailedHandler> theHandler(
+ theDoc->GetImportFailedHandler());
+ if (theHandler)
+ theHandler->DisplayImportFailed(
+ theResFontDir.absolutePath(),
+ QObject::tr("Default Font File Doesn't Exist in the Directory"),
+ false);
+ return nullptr;
+ }
+
+ // Copy the file to project's fonts folder
+ SFileTools::Copy(theFontFile,
+ Q3DStudio::FileOpenFlags(Q3DStudio::FileOpenFlagValues::Create),
+ theResFontFile);
+ // Force the text renderer to refresh
+ if (theDoc->GetSceneGraph() && theDoc->GetSceneGraph()->GetTextRenderer())
+ theDoc->GetSceneGraph()->GetTextRenderer()->ReloadFonts();
+ }
+
+ // Lastly, we use the font file to create the Text object. This is similar to drag-drop
+ // the font file from Project Palette to Scene.
+ SCOPED_DOCUMENT_EDITOR(*theDoc, QObject::tr("Add Text"))
+ ->ImportFile(DocumentEditorFileType::Font, theFontFile, inTarget, inSlide,
+ CDialogs::GetImportFileExtension(),
+ Q3DStudio::ImportUtils::GetInsertTypeForDropType(inDestType), thePoint,
+ theStartTime);
+ } else {
+ SCOPED_DOCUMENT_EDITOR(*theDoc, QObject::tr("Add Instance"))
+ ->CreateSceneGraphInstance(theComposerType, inTarget, inSlide, theInsertType,
+ thePoint, (EPrimitiveType)m_PrimitiveType, theStartTime);
+ }
+ }
+ return nullptr;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.h b/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.h
new file mode 100644
index 00000000..454fce9e
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/BasicObjectDropSource.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __BasicObjectDropSource_H__
+#define __BasicObjectDropSource_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropSource.h"
+#include "StudioObjectTypes.h"
+
+class CStudioApp;
+class CDropTarget;
+
+class CBasicObjectDropSource : public CDropSource
+{
+public:
+ CBasicObjectDropSource(long inFlavor, IDragable *inDragable);
+
+ // CDropSource
+ bool CanMove() override;
+ bool CanCopy() override;
+ bool ValidateTarget(CDropTarget *inTarget) override;
+
+ CCmd *GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide) override;
+
+protected:
+ EPrimitiveType m_PrimitiveType;
+ bool m_IsIndependent;
+};
+
+#endif // #ifndef __BasicObjectDropSource_H__
diff --git a/src/Authoring/Studio/DragAndDrop/DropContainer.cpp b/src/Authoring/Studio/DragAndDrop/DropContainer.cpp
new file mode 100644
index 00000000..b1ed7a6c
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropContainer.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "StudioApp.h"
+#include "DropContainer.h"
+#include "HotKeys.h"
+#include "MouseCursor.h"
+#include "FileDropSource.h"
+#include "ResourceCache.h"
+
+//===============================================================================
+/**
+ * A derived object will call this to subscribe to Drop Flavors.
+ * @param inMainFlavor the Flavor to add.
+ * @see CDropSource.h
+ */
+void CDropContainer::AddMainFlavor(long inMainFlavor)
+{
+ m_Flavors.push_back(inMainFlavor);
+}
+
+//===============================================================================
+/**
+ * This an accessor to get the begining of the iterator.
+ * @return the Iterator
+ */
+CDropContainer::TFlavorItr CDropContainer::GetFlavorBegin()
+{
+ return m_Flavors.begin();
+}
+
+//===============================================================================
+/**
+ * This is an iterator to the end.
+ * @return the Iterator
+ */
+CDropContainer::TFlavorItr CDropContainer::GetFlavorEnd()
+{
+ return m_Flavors.end();
+}
+
+//===============================================================================
+/**
+ * Constructor to build the container.
+ * This also sets up the DropProxy.
+ * @see CDropProxy
+ */
+CWinDropContainer::CWinDropContainer()
+ : m_DropProxy(this)
+{
+}
+//===============================================================================
+/**
+ * Destructor
+ */
+CWinDropContainer::~CWinDropContainer()
+{
+}
+
+//===============================================================================
+/**
+ * This is so the Window that is derived from CWinContainer can receive drags.
+ * @param inWindow the outer to drag.
+ */
+void CWinDropContainer::RegiserForDnd(QWidget *inWindow)
+{
+ // This passes the inWindow down to the COLEDropSource.
+ m_DropProxy.Register(inWindow);
+}
diff --git a/src/Authoring/Studio/DragAndDrop/DropContainer.h b/src/Authoring/Studio/DragAndDrop/DropContainer.h
new file mode 100644
index 00000000..808a5bf4
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropContainer.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_DROPCONTAINER
+#define INCLUDED_DROPCONTAINER
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropProxy.h"
+#include "DropSource.h"
+#include <vector>
+
+class CStudioApp;
+
+class CDropContainer
+{
+public:
+ typedef std::vector<long> TFlavor;
+ typedef TFlavor::iterator TFlavorItr;
+
+protected:
+ TFlavor m_Flavors; ///< This is a list of flavors handled by this container.
+
+public:
+ CDropContainer() {}
+ virtual ~CDropContainer() {}
+
+ CDropContainer::TFlavorItr GetFlavorBegin();
+ CDropContainer::TFlavorItr GetFlavorEnd();
+
+ void AddMainFlavor(long inMainFlavor);
+
+ // These need to get implemented by the Cross platform Container.
+ virtual bool OnDragWithin(CDropSource &inSource) = 0;
+ virtual bool OnDragReceive(CDropSource &inSource) = 0;
+ virtual void OnDragLeave() = 0;
+ virtual void OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags) = 0;
+};
+
+class CWinDropContainer : public CDropContainer
+{
+public:
+ CWinDropContainer();
+ virtual ~CWinDropContainer();
+
+ void RegiserForDnd(QWidget *inWindow);
+ long ReflectMouse(long inX, long inY);
+
+protected:
+ // These are utility functions.
+ CDropProxy m_DropProxy; ///< The COLEDropSource pass through.
+};
+
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/DropSource.cpp b/src/Authoring/Studio/DragAndDrop/DropSource.cpp
new file mode 100644
index 00000000..06a3fc8a
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropSource.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "DropTarget.h"
+#include "StudioObjectTypes.h"
+#include "HotKeys.h"
+#include "Doc.h"
+#include "IDragable.h"
+#include "FileDropSource.h"
+#include "ExplorerFileDropSource.h"
+#include "TimelineDropSource.h"
+#include "BasicObjectDropSource.h"
+#include "ListBoxDropSource.h"
+
+CDropSource::CDropSource(long inFlavor, unsigned long inSize)
+ : m_Flavor(inFlavor)
+ , m_Size(inSize)
+ , m_ObjectType(0)
+ , m_HasValidTarget(false)
+ , m_CurrentFlags(0)
+{
+}
+
+CDropSource::~CDropSource()
+{
+}
+
+CDropSource *CDropSourceFactory::Create(long inFlavor, void *inData, unsigned long inSize)
+{
+ CDropSource *theDropSource(nullptr);
+ switch (inFlavor) {
+
+ case EUIC_FLAVOR_FILE: {
+ theDropSource = new CExplorerFileDropSource(inFlavor, inData, inSize);
+ } break;
+ case EUIC_FLAVOR_TEXT:
+ // Don't do anythiing for this
+ break;
+
+ case EUIC_FLAVOR_ASSET_UICFILE:
+ // make an Aset out of this.
+ theDropSource = new CFileDropSource(inFlavor, inData, inSize);
+ break;
+ }
+
+ return theDropSource;
+}
+
+CDropSource *CDropSourceFactory::Create(long inFlavor, IDragable *inDragable)
+{
+ CDropSource *theDropSource(nullptr);
+ switch (inFlavor) {
+ case EUIC_FLAVOR_LISTBOX:
+ theDropSource = new CListBoxDropSource(inFlavor, inDragable);
+ break;
+
+ case EUIC_FLAVOR_BASIC_OBJECTS:
+ theDropSource = new CBasicObjectDropSource(inFlavor, inDragable);
+ break;
+
+ case EUIC_FLAVOR_ASSET_TL:
+ theDropSource = new CTimeLineDropSource(inFlavor, inDragable);
+ break;
+
+ default:
+ theDropSource = Create(inFlavor, reinterpret_cast<void *>(inDragable), sizeof(inDragable));
+ }
+
+ return theDropSource;
+}
+
+CDropSource *CDropSourceFactory::Extract(long inFlavor, void *inData, unsigned long /*inSize*/)
+{
+ CDropSource *theDropSource(nullptr);
+ switch (inFlavor) {
+ // For all of the Studio Flavors we just need to extract the dropsource out of it.
+ case EUIC_FLAVOR_LISTBOX:
+ // make an Aset out of this.
+ theDropSource = static_cast<CListBoxDropSource *>(inData);
+ break;
+
+ case EUIC_FLAVOR_BASIC_OBJECTS:
+ // make an Aset out of this.
+ theDropSource = static_cast<CBasicObjectDropSource *>(inData);
+ break;
+
+ case EUIC_FLAVOR_ASSET_TL:
+ // cast it to the right type just so we don't loose the virtual table.
+ theDropSource = static_cast<CTimeLineDropSource *>(inData);
+ break;
+
+ case EUIC_FLAVOR_ASSET_UICFILE:
+ theDropSource = static_cast<CFileDropSource *>(inData);
+
+ break;
+ default:
+ theDropSource = nullptr;
+ }
+
+ return theDropSource;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/DropSource.h b/src/Authoring/Studio/DragAndDrop/DropSource.h
new file mode 100644
index 00000000..542b14ac
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropSource.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#ifndef INCLUDED_DROPSOURCE
+#define INCLUDED_DROPSOURCE
+
+#include "UICDMHandles.h"
+#include "Pt.h"
+#include "Cmd.h"
+
+#include <QMimeData>
+
+typedef enum _EDROPDESTINATION {
+ EDROPDESTINATION_ON, ///< drop occurs on the target asset
+ EDROPDESTINATION_ABOVE, ///< drop occurs above the target asset
+ EDROPDESTINATION_BELOW ///< drop occurs below the target asset
+} EDROPDESTINATION;
+
+class CDropTarget;
+class IDragable;
+class CStudioApp;
+
+class CDropSource : public QMimeData
+{
+protected:
+ long m_Flavor;
+ unsigned long m_Size;
+
+ UICDM::TInstanceHandleList m_Instances;
+ long m_ObjectType;
+ bool m_HasValidTarget;
+ CPt m_CurrentPoint;
+ Qt::KeyboardModifiers m_CurrentFlags;
+
+public:
+ CDropSource(long inFlavor, unsigned long inSize);
+ virtual ~CDropSource();
+
+ virtual bool CanMove() = 0;
+ virtual bool CanCopy() = 0;
+ long GetObjectType() const { return m_ObjectType; }
+ long GetFlavor() const { return m_Flavor; }
+ virtual bool ValidateTarget(CDropTarget *) = 0;
+
+ virtual bool GetHasValidTarget() const { return m_HasValidTarget; }
+ virtual void SetHasValidTarget(bool inValid) { m_HasValidTarget = inValid; }
+ virtual void InterpretKeyFlags(long) {}
+
+ virtual void SetCurrentPoint(CPt &inPoint) { m_CurrentPoint = inPoint; }
+ virtual CPt GetCurrentPoint() const { return m_CurrentPoint; }
+
+ virtual void SetCurrentFlags(Qt::KeyboardModifiers inFlags) { m_CurrentFlags = inFlags; }
+ virtual Qt::KeyboardModifiers GetCurrentFlags() const { return m_CurrentFlags; }
+
+ virtual CCmd *GenerateAssetCommand(UICDM::CUICDMInstanceHandle, EDROPDESTINATION,
+ UICDM::CUICDMSlideHandle)
+ {
+ return nullptr;
+ }
+ virtual CCmd *GenerateSlideCommand(long) { return nullptr; }
+};
+
+class CDropSourceFactory
+{
+public:
+ static CDropSource *Create(long inFlavor, IDragable *inDragable);
+ static CDropSource *Create(long inFlavor, void *inData, unsigned long inSize);
+ static CDropSource *Extract(long inFlavor, void *inData, unsigned long inSize);
+};
+
+#endif
diff --git a/src/Authoring/Studio/DragAndDrop/DropTarget.cpp b/src/Authoring/Studio/DragAndDrop/DropTarget.cpp
new file mode 100644
index 00000000..2ca783e5
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropTarget.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "stdafx.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+#include "DropTarget.h"
+
+CDropTarget::CDropTarget()
+ : m_Instance(0)
+ , m_ObjectType(OBJTYPE_UNKNOWN)
+{
+}
+
+bool CDropTarget::CanAddToMaster()
+{
+ long theTargetObjectType = GetObjectType();
+ if (theTargetObjectType == OBJTYPE_SCENE || theTargetObjectType == OBJTYPE_COMPONENT)
+ return true;
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::ISlideSystem *theSlideSystem = theDoc->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theTargetSlide = theSlideSystem->GetAssociatedSlide(GetInstance());
+ return theTargetSlide && theSlideSystem->IsMasterSlide(theTargetSlide);
+}
diff --git a/src/Authoring/Studio/DragAndDrop/DropTarget.h b/src/Authoring/Studio/DragAndDrop/DropTarget.h
new file mode 100644
index 00000000..7bbec133
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/DropTarget.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_DROPTARGET
+#define INCLUDED_DROPTARGET
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioObjectTypes.h"
+#include "UICDMHandles.h"
+
+class CDropSource;
+class CStudioApp;
+
+class CDropTarget
+{
+protected:
+ UICDM::CUICDMInstanceHandle m_Instance;
+ long m_ObjectType;
+
+public:
+ CDropTarget();
+ virtual ~CDropTarget() {}
+
+ virtual bool Accept(CDropSource &inSource) = 0;
+ virtual bool Drop(CDropSource &inSource) = 0;
+ virtual long GetObjectType() = 0;
+
+ virtual void SetInstance(UICDM::CUICDMInstanceHandle inInstance) { m_Instance = inInstance; }
+ virtual UICDM::CUICDMInstanceHandle GetInstance() { return m_Instance; }
+
+ virtual bool IsRelative(UICDM::CUICDMInstanceHandle) { return false; }
+ virtual bool IsSelf(UICDM::CUICDMInstanceHandle) { return false; }
+ virtual bool IsMaster() { return false; }
+ virtual bool CanAddToMaster();
+};
+
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.cpp b/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.cpp
new file mode 100644
index 00000000..f32cbe9b
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "qtAuthoring-config.h"
+#include "ExplorerFileDropSource.h"
+#include "Dialogs.h"
+#include "DropTarget.h"
+#include "StudioObjectTypes.h"
+#include "IDragable.h"
+#include "UICFileTools.h"
+#include "ImportUtils.h"
+
+bool CExplorerFileDropSource::s_FileHasValidTarget = false;
+
+//===============================================================================
+/**
+ *
+ */
+bool CExplorerFileDropSource::ValidateTarget(CDropTarget *inTarget)
+{
+ // Check the type is valid and if target can accept
+ bool theValidTarget =
+ ((m_ObjectType != OBJTYPE_UNKNOWN) && (inTarget->GetObjectType() == EUIC_FLAVOR_FILE));
+ SetHasValidTarget(theValidTarget);
+
+ return theValidTarget;
+}
+
+//===============================================================================
+/**
+ *
+ */
+CExplorerFileDropSource::CExplorerFileDropSource(long inFlavor, void *inData, unsigned long inSize)
+ : CDropSource(inFlavor, inSize)
+ , m_File("")
+{
+ // Pull out all of the SDropItemData and build a file.
+ m_File = *(CUICFile *)inData;
+ Q3DStudio::CFilePath thePath(m_File.GetAbsolutePath());
+ m_ObjectType = Q3DStudio::ImportUtils::GetObjectFileTypeForFile(thePath).m_IconType;
+ // Fix because DAE files are the *only* thing you can drop onto the project
+ if (thePath.GetExtension().Compare(CDialogs::GetWideDAEFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false)) {
+ m_ObjectType = OBJTYPE_GROUP;
+ }
+#ifdef QT_3DSTUDIO_FBX
+ else if (thePath.GetExtension().Compare(CDialogs::GetWideFbxFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false)) {
+ m_ObjectType = OBJTYPE_GROUP;
+ }
+#endif
+}
+
+//===============================================================================
+/**
+ *
+ */
+void CExplorerFileDropSource::SetHasValidTarget(bool inValid)
+{
+ m_HasValidTarget = inValid;
+ CExplorerFileDropSource::s_FileHasValidTarget = inValid;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CExplorerFileDropSource::GetHasValidTarget()
+{
+ return CExplorerFileDropSource::s_FileHasValidTarget;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CExplorerFileDropSource::CanMove()
+{
+ return false;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CExplorerFileDropSource::CanCopy()
+{
+ return true;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.h b/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.h
new file mode 100644
index 00000000..03269aaf
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ExplorerFileDropSource.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __EXPLORERFILEDROPSOURCE_H__
+#define __EXPLORERFILEDROPSOURCE_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropSource.h"
+#include "UICFile.h"
+
+class CStudioApp;
+class CDropTarget;
+
+//==============================================================================
+/**
+ * @class CExplorerFileDropSource
+ * @brief Drop Source for EUIC_FLAVOR_UICFILE
+ *
+ * This class is meant to handle drag and drop for EUIC_FLAVOR_UICFILE,
+ * for example when user drags a file from Explorer Window (outside Studio).
+ */
+class CExplorerFileDropSource : public CDropSource
+{
+protected:
+ CUICFile m_File;
+ static bool s_FileHasValidTarget;
+
+public:
+ CExplorerFileDropSource(long inFlavor, void *inData, unsigned long inSize);
+
+ bool CanMove() override;
+ bool CanCopy() override;
+ bool ValidateTarget(CDropTarget *inTarget) override;
+ bool GetHasValidTarget();
+ void SetHasValidTarget(bool inValid) override;
+ CUICFile GetFile() const { return m_File; }
+};
+
+#endif // #ifndef __EXPLORERFILEDROPSOURCE_H__
diff --git a/src/Authoring/Studio/DragAndDrop/FileDropSource.cpp b/src/Authoring/Studio/DragAndDrop/FileDropSource.cpp
new file mode 100644
index 00000000..a4164e02
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/FileDropSource.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "Dialogs.h"
+#include "FileDropSource.h"
+#include "DropTarget.h"
+#include "StudioObjectTypes.h"
+#include "HotKeys.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentEditor.h"
+#include "UICFileTools.h"
+#include "ImportUtils.h"
+
+bool CFileDropSource::s_FileHasValidTarget = false;
+
+//===============================================================================
+/**
+ *
+ */
+bool CFileDropSource::ValidateTarget(CDropTarget *inTarget)
+{
+ using namespace Q3DStudio;
+
+ EStudioObjectType targetType = (EStudioObjectType)inTarget->GetObjectType();
+ bool theValidTarget = false;
+
+ // the only thing we want to do from here is check the type.
+ theValidTarget = CStudioObjectTypes::AcceptableParent(
+ (EStudioObjectType)m_ObjectType, (EStudioObjectType)inTarget->GetObjectType());
+
+ if (!theValidTarget) {
+ SetHasValidTarget(theValidTarget);
+ return theValidTarget;
+ } else {
+ if (CHotKeys::IsKeyDown(Qt::AltModifier) && targetType != OBJTYPE_SCENE
+ && targetType != OBJTYPE_COMPONENT) {
+ UICDM::CUICDMInstanceHandle theTarget = inTarget->GetInstance();
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ IDocumentReader &theReader(theDoc->GetDocumentReader());
+ UICDM::CUICDMSlideHandle toSlide = theReader.GetAssociatedSlide(theTarget);
+ ;
+
+ if (!theReader.IsMasterSlide(toSlide))
+ theValidTarget = false;
+ }
+
+ SetHasValidTarget(theValidTarget);
+ return theValidTarget;
+ }
+}
+
+//===============================================================================
+/**
+ *
+ */
+CFileDropSource::CFileDropSource(long inFlavor, void *inData, unsigned long inSize)
+ : CDropSource(inFlavor, inSize)
+ , m_File("")
+{
+ // Pull out all of the SDropItemData and build a file.
+ m_File = *(CUICFile *)inData;
+ m_ObjectType =
+ Q3DStudio::ImportUtils::GetObjectFileTypeForFile(m_File.GetAbsolutePath()).m_ObjectType;
+}
+
+//===============================================================================
+/**
+ *
+ */
+void CFileDropSource::SetHasValidTarget(bool inValid)
+{
+ m_HasValidTarget = inValid;
+ CFileDropSource::s_FileHasValidTarget = inValid;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CFileDropSource::GetHasValidTarget()
+{
+ return CFileDropSource::s_FileHasValidTarget;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CFileDropSource::CanMove()
+{
+ return false;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CFileDropSource::CanCopy()
+{
+ return true;
+}
+
+CCmd *CFileDropSource::GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide)
+{
+ UICDM::CUICDMInstanceHandle theTarget = inTarget;
+
+ CDoc &theDoc(*g_StudioApp.GetCore()->GetDoc());
+ Q3DStudio::CFilePath theFilePath(m_File.GetAbsolutePath());
+ CPt thePoint;
+ // if ( CHotKeys::IsKeyDown( Qt::AltModifier ) )
+ // thePoint = GetCurrentPoint();
+
+ long theStartTime = -1;
+ if (CHotKeys::IsKeyDown(Qt::ControlModifier))
+ theStartTime = theDoc.GetCurrentViewTime();
+
+ if (theFilePath.IsFile()) {
+ Q3DStudio::DocumentEditorFileType::Enum theDocType(
+ Q3DStudio::ImportUtils::GetObjectFileTypeForFile(theFilePath).m_FileType);
+ QString theCommandName;
+ // TODO: internationalize
+ switch (theDocType) {
+ case Q3DStudio::DocumentEditorFileType::DAE:
+ theCommandName = QObject::tr("File Drop DAE File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Import:
+ theCommandName = QObject::tr("File Drop Import File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Image:
+ theCommandName = QObject::tr("File Drop Image File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Behavior:
+ theCommandName = QObject::tr("File Drop Behavior File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Mesh:
+ theCommandName = QObject::tr("File Drop Mesh File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Font:
+ theCommandName = QObject::tr("File Drop Font File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Effect:
+ theCommandName = QObject::tr("File Drop Effect File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Material:
+ theCommandName = QObject::tr("File Drop Material File");
+ break;
+ case Q3DStudio::DocumentEditorFileType::Path:
+ theCommandName = QObject::tr("File Drop Path File");
+ break;
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(theDoc, theCommandName)
+ ->ImportFile(theDocType, theFilePath, theTarget, inSlide,
+ CDialogs::GetImportFileExtension(),
+ Q3DStudio::ImportUtils::GetInsertTypeForDropType(inDestType), thePoint,
+ theStartTime);
+ }
+ return nullptr;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/FileDropSource.h b/src/Authoring/Studio/DragAndDrop/FileDropSource.h
new file mode 100644
index 00000000..57127b0d
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/FileDropSource.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __FILEDROPSOURCE_H__
+#define __FILEDROPSOURCE_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+// This should go into its own FileDropSourceFile.
+#include "DropSource.h"
+#include "UICFile.h"
+
+class CStudioApp;
+class CDropTarget;
+
+//==============================================================================
+/**
+ * @class CFileDropSource
+ * @brief Drop Source for EUIC_FLAVOR_ASSET_UICFILE
+ *
+ * This class is meant to handle drag and drop for EUIC_FLAVOR_ASSET_UICFILE,
+ * for example when user drags a file from within Studio.
+ */
+class CFileDropSource : public CDropSource
+{
+protected:
+ CUICFile m_File;
+ static bool s_FileHasValidTarget;
+
+public:
+ CFileDropSource(long inFlavor, void *inData, unsigned long inSize);
+
+ bool CanMove() override;
+ bool CanCopy() override;
+ bool ValidateTarget(CDropTarget *inTarget) override;
+ bool GetHasValidTarget();
+ void SetHasValidTarget(bool inValid) override;
+
+ CCmd *GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide) override;
+};
+
+#endif // #ifndef __FILEDROPSOURCE_H__
diff --git a/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.cpp b/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.cpp
new file mode 100644
index 00000000..da602679
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ListBoxDropSource.h"
+#include "DropTarget.h"
+#include "ListBoxItem.h"
+#include "IDragable.h"
+
+//===============================================================================
+/**
+ * Constructor
+ */
+CListBoxDropSource::CListBoxDropSource(long inFlavor, IDragable *inDragable)
+ : CDropSource(inFlavor, 0)
+{
+ m_Item = reinterpret_cast<CListBoxItem *>(inDragable);
+}
+
+//===============================================================================
+/**
+ * Validate that the drop target is EUIC_FLAVOR_LISTBOX type.
+ * @param inTarget Drop target for validation
+ * @return true if inTarget is EUIC_FLAVOR_LISTBOX; false if otherwise
+ */
+bool CListBoxDropSource::ValidateTarget(CDropTarget *inTarget)
+{
+ bool theValidTarget = (inTarget->GetObjectType() == EUIC_FLAVOR_LISTBOX);
+ SetHasValidTarget(theValidTarget);
+
+ return theValidTarget;
+}
+
+//===============================================================================
+/**
+ * @return true
+ */
+bool CListBoxDropSource::CanMove()
+{
+ return true;
+}
+
+//===============================================================================
+/**
+ * @return true
+ */
+bool CListBoxDropSource::CanCopy()
+{
+ return true;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.h b/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.h
new file mode 100644
index 00000000..a28678d4
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ListBoxDropSource.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __ListBoxDropSource_H__
+#define __ListBoxDropSource_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropSource.h"
+
+class CStudioApp;
+class CDropTarget;
+class CListBoxItem;
+
+class CListBoxDropSource : public CDropSource
+{
+protected:
+ CListBoxItem *m_Item;
+
+public:
+ CListBoxDropSource(long inFlavor, IDragable *inDragable);
+
+ bool CanMove() override;
+ bool CanCopy() override;
+ bool ValidateTarget(CDropTarget *inTarget) override;
+
+ CListBoxItem *GetItem() { return m_Item; }
+};
+
+#endif // #ifndef __ListBoxDropSource_H__
diff --git a/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.cpp b/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.cpp
new file mode 100644
index 00000000..752cc430
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ListBoxDropTarget.h"
+#include "ListBoxDropSource.h"
+#include "IDragable.h"
+
+//===============================================================================
+/**
+ * Constructor
+ */
+CListBoxDropTarget::CListBoxDropTarget()
+ : m_Item(nullptr)
+{
+}
+
+//===============================================================================
+/**
+ * This get called on every DragWithin.
+ * Note: the source will validate the target instead of the otherway around.
+ * This is because the DropSource knows how to get information from itself without
+ * creating an asset. This is mainly for DropSources that have a lazy creation idiom.
+ * like files.
+ * @param the DropSource in question.
+ * @return true if the DropSource likes the DropTarget.
+ */
+bool CListBoxDropTarget::Accept(CDropSource &inSource)
+{
+ return inSource.ValidateTarget(this);
+}
+
+//===============================================================================
+/**
+ * This is where is actually happens.
+ * Note: At this point everything should be verified, and setup in the dropsource.
+ * The only thing left to do is to get the Assets and move/copy or connect them.
+ * @param inSource the Object in question.
+ * @return true if the drop was successful .
+ */
+bool CListBoxDropTarget::Drop(CDropSource &inSource)
+{
+ if (m_Item) {
+ inSource;
+ /*
+ CSlideDropSource* theSlideDropSource = static_cast< CSlideDropSource* >(
+ &inSource );
+ CSlideControl* theSlideControl = theSlideDropSource->GetSlide( );
+ CTimeContext* theTimeContext = theSlideControl->GetTimeContext( );
+
+ // Reorder this slide to the position held by this drop target.
+ CCmdRearrangeTimeContext* theCommand = new CCmdRearrangeTimeContext( theTimeContext,
+ m_SlideInsert->GetInsertIndex( ) );
+ if ( theCommand )
+ {
+ theTimeContext->GetAsset( )->ExecuteCommand( theCommand, false );
+ }
+ */
+ }
+
+ // we are always successful
+ return true;
+}
+
+//===============================================================================
+/**
+ * This will get the objec ttype from the Asset.
+ * Note: The asset can change all of the time, so i always ask the asset for its type.
+ * @return the Studio object type.
+ */
+long CListBoxDropTarget::GetObjectType()
+{
+ return EUIC_FLAVOR_LISTBOX;
+}
+
+//===============================================================================
+/**
+ * Set the SlideInsertionControl that is the drop target
+ */
+void CListBoxDropTarget::SetItem(CListBoxItem *inItem)
+{
+ m_Item = inItem;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.h b/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.h
new file mode 100644
index 00000000..44199092
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ListBoxDropTarget.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __ListBoxDropTarget_H__
+#define __ListBoxDropTarget_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropTarget.h"
+
+class CDropSource;
+class CListBoxItem;
+
+//==============================================================================
+/**
+ * @class
+ * @brief justin.ebert needs to enter a brief description here.
+ *
+ * justin.ebert needs to enter a long description here.
+ */
+class CListBoxDropTarget : public CDropTarget
+{
+protected:
+ CListBoxItem *m_Item; ///< The list item that we can drop on
+
+public:
+ CListBoxDropTarget();
+ bool Accept(CDropSource &inSource) override;
+ bool Drop(CDropSource &inSource) override;
+ long GetObjectType() override;
+
+ void SetItem(CListBoxItem *inItem);
+};
+
+#endif // #ifndef __ListBoxDropTarget_H__
diff --git a/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.cpp b/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.cpp
new file mode 100644
index 00000000..5344d459
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "qtAuthoring-config.h"
+#include "ProjectDropTarget.h"
+#include "DropSource.h"
+#include "ExplorerFileDropSource.h"
+#include "IDragable.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Dialogs.h"
+#include "Doc.h"
+#include "UICFile.h"
+#include "UICFileTools.h"
+#include "UICImportPerformImport.h"
+#include "UICImportTranslation.h"
+#include "IDocumentEditor.h"
+#include "PathImportTranslator.h"
+
+//===============================================================================
+/**
+ * Constructor
+ */
+CProjectDropTarget::CProjectDropTarget(const Q3DStudio::CFilePath &inTargetDir)
+ : m_TargetDir(inTargetDir)
+{
+}
+
+//===============================================================================
+/**
+ * This get called on every DragWithin.
+ * Note: the source will validate the target instead of the otherway around.
+ * This is because the DropSource knows how to get information from itself without
+ * creating an asset. This is mainly for DropSources that have a lazy creation idiom.
+ * like files.
+ * @param the DropSource in question.
+ * @return true if the DropSource likes the DropTarget.
+ */
+bool CProjectDropTarget::Accept(CDropSource &inSource)
+{
+ return inSource.ValidateTarget(this);
+}
+
+//===============================================================================
+/**
+ * This is where is actually happens.
+ * Note: At this point everything should be verified, and setup in the dropsource.
+ * The only thing left to do is to get the Assets and move/copy or connect them.
+ * @param inSource the Object in question.
+ * @return true if the drop was successful .
+ */
+bool CProjectDropTarget::Drop(CDropSource &inSource)
+{
+ using namespace Q3DStudio;
+ using namespace UICIMP;
+ // Drag and Drop - From Explorer window to Project Palette
+ // For all valid Project File Types:
+ // - This performs a file copy from the source Explorer location to the selected Project Palette
+ // Folder
+ // - The destination copy must NOT be read-only even if the source is read-only
+ // For DAE, it will import the file.
+ if (inSource.GetFlavor() == EUIC_FLAVOR_FILE) {
+ // Create target directory if it doesn't exist
+ if (!m_TargetDir.Exists())
+ m_TargetDir.CreateDir(true);
+ // also make sure that target directory is a directory, not a file
+ else if (m_TargetDir.IsFile())
+ m_TargetDir = m_TargetDir.GetDirectory();
+
+ // Sanity check
+ ASSERT(m_TargetDir.IsDirectory());
+
+ // Get the Source file to be copied
+ CExplorerFileDropSource *theFileDropSource =
+ static_cast<CExplorerFileDropSource *>(&inSource);
+ CFilePath theSourceFile(theFileDropSource->GetFile().GetAbsolutePath());
+
+ if (theSourceFile.IsFile() && m_TargetDir.IsDirectory()) {
+ // Get the file extension
+ Q3DStudio::CString theExtension(theSourceFile.GetExtension());
+
+ Q3DStudio::CString theFileStem = theSourceFile.GetFileStem();
+ Q3DStudio::CString outputFileName(theFileStem + L"."
+ + CDialogs::GetImportFileExtension());
+
+ if (theExtension.Compare(CDialogs::GetWideDAEFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false)) {
+ SColladaTranslator theTranslator(theSourceFile.toQString());
+
+ CFilePath theOutputDir =
+ SFileTools::FindUniqueDestDirectory(m_TargetDir, theFileStem);
+ CFilePath theFullOutputFile(
+ CFilePath::CombineBaseAndRelative(theOutputDir, outputFileName));
+ SImportResult theImportResult =
+ CPerformImport::TranslateToImportFile(theTranslator, theFullOutputFile);
+ bool forceError = theFullOutputFile.IsFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ theSourceFile.toQString(), theImportResult.m_Error,
+ g_StudioApp.GetCore()->GetDoc()->GetImportFailedHandler(),
+ theTranslator.m_TranslationLog, forceError);
+#ifdef QT_3DSTUDIO_FBX
+ } else if (theExtension.Compare(CDialogs::GetWideFbxFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false)) {
+ SFbxTranslator theTranslator(theSourceFile.toQString());
+
+ CFilePath theOutputDir =
+ SFileTools::FindUniqueDestDirectory(m_TargetDir, theFileStem);
+ CFilePath theFullOutputFile(
+ CFilePath::CombineBaseAndRelative(theOutputDir, outputFileName));
+ SImportResult theImportResult =
+ CPerformImport::TranslateToImportFile(theTranslator, theFullOutputFile);
+ bool forceError = theFullOutputFile.IsFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ theSourceFile.toQString(), theImportResult.m_Error,
+ g_StudioApp.GetCore()->GetDoc()->GetImportFailedHandler(),
+ theTranslator.m_TranslationLog, forceError);
+#endif
+ } else if (theExtension.Compare(L"svg", Q3DStudio::CString::ENDOFSTRING, false)) {
+ IDocumentReader &theReader(g_StudioApp.GetCore()->GetDoc()->GetDocumentReader());
+ SPathImportTranslator theTranslator(theSourceFile.toQString(), *theReader.GetLuaContext(),
+ theReader.GetFoundation());
+ CFilePath theOutputDir =
+ SFileTools::FindUniqueDestDirectory(m_TargetDir, theFileStem);
+ CFilePath theFullOutputFile(
+ CFilePath::CombineBaseAndRelative(theOutputDir, outputFileName));
+ SImportResult theImportResult =
+ CPerformImport::TranslateToImportFile(theTranslator, theFullOutputFile);
+ bool forceError = theFullOutputFile.IsFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ theSourceFile.toQString(), theImportResult.m_Error,
+ g_StudioApp.GetCore()->GetDoc()->GetImportFailedHandler(),
+ theTranslator.m_TranslationLog, forceError);
+ } else {
+ // Copy the file to target directory
+ // FindAndCopyDestFile will make sure the file name is unique and make sure it is
+ // not read only.
+ SFileErrorCodeFileNameAndNumBytes theCopyResult = SFileTools::FindAndCopyDestFile(
+ m_TargetDir, CFilePath::GetAbsolutePath(theSourceFile));
+
+ // Sanity check
+ ASSERT(theCopyResult.m_Error == Q3DStudio::FileErrorCodes::NoError);
+ ASSERT(theCopyResult.m_DestFilename.Exists());
+
+ // For effect and custom material files, automatically copy related resources
+ if (CDialogs::IsEffectFileExtension(theExtension)
+ || CDialogs::IsMaterialFileExtension(theExtension)) {
+ std::shared_ptr<IImportFailedHandler> theHandler(
+ g_StudioApp.GetCore()->GetDoc()->GetImportFailedHandler());
+ CFilePath theShaderFile(theSourceFile);
+
+ if (theShaderFile.GetExtension() != "effect"
+ && theShaderFile.GetExtension() != "material") {
+ ASSERT(false); // what file is this?
+ } else {
+ }
+
+ std::vector<Q3DStudio::CString> theEffectFileSourcePaths;
+ g_StudioApp.GetCore()
+ ->GetDoc()
+ ->GetDocumentReader()
+ .ParseSourcePathsOutOfEffectFile(
+ Q3DStudio::CFilePath::GetAbsolutePath(theSourceFile),
+ theEffectFileSourcePaths);
+
+ CFilePath theFileDir(
+ Q3DStudio::CFilePath::GetAbsolutePath(theSourceFile).GetDirectory());
+ CFilePath theDocumentDir(
+ g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory());
+ for (size_t idx = 0; idx < theEffectFileSourcePaths.size(); ++idx) {
+ CFilePath theSourcePath = CFilePath::CombineBaseAndRelative(
+ theFileDir, theEffectFileSourcePaths[idx]);
+ CFilePath theResultPath = CFilePath::CombineBaseAndRelative(
+ theDocumentDir, theEffectFileSourcePaths[idx]);
+ CFilePath theResultDir(theResultPath.GetDirectory());
+ theResultDir.CreateDir(true);
+ // If the file already exists, these file flags will ensure it won't get
+ // overwritten.
+ SFileTools::Copy(theResultPath,
+ qt3ds::foundation::FileOpenFlags(
+ qt3ds::foundation::FileOpenFlagValues::Create
+ | qt3ds::foundation::FileOpenFlagValues::Write),
+ theSourcePath);
+ }
+ }
+ }
+ }
+ }
+
+ // we are always successful
+ return true;
+}
+
+//===============================================================================
+/**
+ * This will get the objec ttype from the Asset.
+ * Note: The asset can change all of the time, so i always ask the asset for its type.
+ * @return the Studio object type.
+ */
+long CProjectDropTarget::GetObjectType()
+{
+ return EUIC_FLAVOR_FILE;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.h b/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.h
new file mode 100644
index 00000000..234c30ed
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/ProjectDropTarget.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __PROJECTDROPTARGET_H__
+#define __PROJECTDROPTARGET_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropTarget.h"
+#include "UICFileTools.h"
+
+class CDropSource;
+
+//==============================================================================
+/**
+ * @class CProjectDropTarget
+ * @brief Drop Target for Project Palette
+ */
+class CProjectDropTarget : public CDropTarget
+{
+public:
+ CProjectDropTarget(const Q3DStudio::CFilePath &inTargetDir);
+
+ bool Accept(CDropSource &inSource) override;
+ bool Drop(CDropSource &inSource) override;
+ long GetObjectType() override;
+
+protected:
+ Q3DStudio::CFilePath m_TargetDir; ///< The target directory to copy file to.
+};
+
+#endif // #ifndef __PROJECTDROPTARGET_H__
diff --git a/src/Authoring/Studio/DragAndDrop/SceneDropTarget.cpp b/src/Authoring/Studio/DragAndDrop/SceneDropTarget.cpp
new file mode 100644
index 00000000..47850e80
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/SceneDropTarget.cpp
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SceneDropTarget.h"
+#include "DropTarget.h"
+#include "DropSource.h"
+
+#include "StudioApp.h"
+#include "Doc.h"
+
+#include "HotKeys.h"
+#include "IDropTargetHelper.h"
+#include "Core.h"
+#include "GraphUtils.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMDataCore.h"
+#include "UICDMSlides.h"
+
+// Sceneview stuff
+//===============================================================================
+/**
+ * Constructor.
+ */
+CSceneViewDropTarget::CSceneViewDropTarget()
+ : m_DropTime(-1)
+{
+ m_ObjectType = OBJTYPE_LAYER;
+ m_DropSourceObjectType = OBJTYPE_UNKNOWN;
+}
+
+//===============================================================================
+/**
+ * This will get the objec ttype from the Asset.
+ * Note: The asset can change all of the time, so i always ask the asset for its type.
+ * @return the Studio object type.
+ */
+long CSceneViewDropTarget::GetObjectType()
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+
+ if (theInstance.Valid()) {
+ CClientDataModelBridge *theBridge =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ m_ObjectType = theBridge->GetObjectType(theInstance);
+ return m_ObjectType;
+ }
+ return OBJTYPE_UNKNOWN;
+}
+
+//===============================================================================
+/**
+ * This get called on every DragWithin.
+ * Note: the source will validate the target instead of the otherway around.
+ * This is because the DropSource knows how to get information from itself without
+ * creating an asset. This is mainly for DropSources that have a lazy creation idiom.
+ * like files.
+ * Dropping into "locked" layers are not allowed.
+ * @param inSource the DropSource in question.
+ * @return true if the DropSource likes the DropTarget.
+ */
+bool CSceneViewDropTarget::Accept(CDropSource &inSource)
+{
+ CClientDataModelBridge *theBridge =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+
+ // We have to set this so we can adjust the Target to accept this source.
+ SetDropSourceObjectType(inSource.GetObjectType());
+
+ bool theAcceptable = false;
+
+ // We don't want to generate an asset right now so let the DropSource ask us if it can drop.
+ theAcceptable = inSource.ValidateTarget(this);
+
+ // The DropSource already generated the asset for this in the above.
+ if (theAcceptable && m_Instance.Valid()) {
+ theAcceptable = !theBridge->IsLockedAtAll(m_Instance);
+ }
+
+ return theAcceptable;
+}
+
+//===============================================================================
+/**
+ * This is so the questioned object type can be cached so we can get the correct asset.
+ * @param inObjType the object type of the Questioned object.
+ */
+void CSceneViewDropTarget::SetDropSourceObjectType(long inObjType)
+{
+ m_DropSourceObjectType = inObjType;
+}
+
+//===============================================================================
+/**
+ * This is where is actually happens.
+ * Note: At this point either everything should be verified, and setup in the dropsource.
+ * Then the only thing left to do is to get the Assets and move/copy or connect them.
+ * Or the dropsource needs the target to perform the actual drop.
+ * Note that if the Control key is depressed, the start time follows the current view time(
+ *i.e. playhead position )
+ * And if the Alt key (KEY_MENU) is depressed, the object is dropped at the mouse location.
+ *
+ * @param inSource the Object in question.
+ * @return true if the drop was successful .
+ */
+bool CSceneViewDropTarget::Drop(CDropSource &inSource)
+{
+ // The Parent is a tree control item, so iwe know it can be converted to an Asset.
+
+ // We have to set this so we can adjust the Target to accept this source.
+ SetDropSourceObjectType(inSource.GetObjectType());
+
+ UICDM::CUICDMInstanceHandle theTargetInstance = GetInstance();
+ if (theTargetInstance.Valid()) {
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::ISlideSystem *theSlideSystem = theDoc->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theSlide = theDoc->GetActiveSlide();
+ if (!theSlideSystem->IsMasterSlide(theSlide)
+ && (inSource.GetCurrentFlags() & CHotKeys::MODIFIER_ALT)) {
+ if (CanAddToMaster()) {
+ UICDM::CUICDMSlideHandle theMasterSlideHandle =
+ theSlideSystem->GetMasterSlide(theSlide);
+ if (theMasterSlideHandle.Valid())
+ theSlide = theMasterSlideHandle;
+ }
+ }
+ CCmd *command =
+ inSource.GenerateAssetCommand(theTargetInstance, EDROPDESTINATION_ON, theSlide);
+ if (command != nullptr)
+ theDoc->GetCore()->ExecuteCommand(command);
+ }
+
+ return true;
+}
+
+//===============================================================================
+/**
+ * @return the Asset that we would like the DropSource to drop on to.
+ */
+UICDM::CUICDMInstanceHandle CSceneViewDropTarget::GetInstance()
+{
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::CUICDMInstanceHandle theRootObject = theDoc->GetActiveRootInstance();
+ EStudioObjectType theRootObjType =
+ theDoc->GetStudioSystem()->GetClientDataModelBridge()->GetObjectType(theRootObject);
+
+ // Check if the inObjectType can just go ahead and drop onto the Root object.
+ if (CStudioObjectTypes::AcceptableParent((EStudioObjectType)m_DropSourceObjectType,
+ theRootObjType)) {
+ m_Instance = theRootObject;
+ } else if (theRootObject == theDoc->GetSceneInstance()
+ && CStudioObjectTypes::AcceptableParent((EStudioObjectType)m_DropSourceObjectType,
+ OBJTYPE_LAYER)) {
+ m_Instance = theDoc->GetActiveLayer();
+ }
+
+ return m_Instance;
+}
+
+//===============================================================================
+/**
+ * Check to see if the Asset is a relative of our asset.
+ * @return true if the inAsset is a parent grandparent...etc. of this asset.
+ */
+bool CSceneViewDropTarget::IsRelative(UICDM::CUICDMInstanceHandle inInstance)
+{
+ bool theReturn = false;
+
+ UICDM::CUICDMInstanceHandle theThisInstance = GetInstance();
+ // This will check to see if the inAsset is already a parent, grandparent....etc.
+ if (theThisInstance.Valid())
+ theReturn = IsAscendant(theThisInstance, inInstance,
+ g_StudioApp.GetCore()->GetDoc()->GetAssetGraph());
+ return theReturn;
+}
+
+//===============================================================================
+/**
+ * Check to see if the inAsset is our asset.
+ * @param inAsset The Asset to check.
+ * @return true if we are the same.
+ */
+bool CSceneViewDropTarget::IsSelf(UICDM::CUICDMInstanceHandle inInstance)
+{
+ UICDM::CUICDMInstanceHandle theThisInstance = GetInstance();
+ return (theThisInstance == inInstance);
+}
+
+//===============================================================================
+/**
+ * Set the Drop time for all sources.
+ * @param inDropTime The time to drop the source.
+ */
+void CSceneViewDropTarget::SetDropTime(long inDropTime)
+{
+ m_DropTime = inDropTime;
+}
+
+//===============================================================================
+/**
+ * @return The time that all sources will be droped.
+ */
+long CSceneViewDropTarget::GetDropTime()
+{
+ return m_DropTime;
+}
+
+// Last Sceneview related stuff.
diff --git a/src/Authoring/Studio/DragAndDrop/SceneDropTarget.h b/src/Authoring/Studio/DragAndDrop/SceneDropTarget.h
new file mode 100644
index 00000000..cdcb7afc
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/SceneDropTarget.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __SCENEDROPTARGET_H__
+#define __SCENEDROPTARGET_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropTarget.h"
+
+//==============================================================================
+/**
+ * @class
+ * @brief LEONARD.PONCE needs to enter a brief description here.
+ *
+ * LEONARD.PONCE needs to enter a long description here.
+ */
+class CSceneViewDropTarget : public CDropTarget
+{
+protected:
+ long m_DropTime; ///< The Time to drop any thing.
+ long m_DropSourceObjectType;
+
+public:
+ CSceneViewDropTarget();
+ bool Accept(CDropSource &inSource) override;
+ bool Drop(CDropSource &inSource) override;
+ UICDM::CUICDMInstanceHandle GetInstance() override;
+
+ bool IsRelative(UICDM::CUICDMInstanceHandle inInstance) override;
+ bool IsSelf(UICDM::CUICDMInstanceHandle inInstance) override;
+ long GetObjectType() override;
+ void SetDropSourceObjectType(long inObjType);
+ void SetDropTime(long inDropTime);
+ long GetDropTime();
+};
+#endif // #ifndef __SCENEDROPTARGET_H__
diff --git a/src/Authoring/Studio/DragAndDrop/TimelineDropSource.cpp b/src/Authoring/Studio/DragAndDrop/TimelineDropSource.cpp
new file mode 100644
index 00000000..cf664825
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/TimelineDropSource.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TimelineDropSource.h"
+#include "FileDropSource.h"
+#include "Dispatch.h"
+#include "DropTarget.h"
+#include "StudioObjectTypes.h"
+#include "HotKeys.h"
+#include "Core.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMSlides.h"
+#include "Bindings/UICDMTimelineItemBinding.h"
+#include "IDocumentEditor.h"
+#include "ImportUtils.h"
+
+#pragma warning(disable : 4100)
+
+using namespace Q3DStudio;
+
+//===============================================================================
+/**
+ *
+ */
+CTimeLineDropSource::CTimeLineDropSource(long inFlavor, IDragable *inDraggable)
+ : CDropSource(inFlavor, sizeof(inDraggable))
+{
+ m_Copy = true;
+
+ m_Instances = g_StudioApp.GetCore()->GetDoc()->GetSelectedValue().GetSelectedInstances();
+ if (m_Instances.size())
+ m_ObjectType = g_StudioApp.GetCore()
+ ->GetDoc()
+ ->GetStudioSystem()
+ ->GetClientDataModelBridge()
+ ->GetObjectType(m_Instances[0]);
+ else
+ m_ObjectType = OBJTYPE_UNKNOWN;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CTimeLineDropSource::CanMove()
+{
+ return !m_Copy;
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CTimeLineDropSource::CanCopy()
+{
+ bool theReturn = false;
+ // This is here because some Assets can not be copied ( scene, material )
+ theReturn = m_Copy && g_StudioApp.GetCore()->GetDoc()->CanCopyObject(m_Instances);
+
+ return theReturn;
+}
+
+//===============================================================================
+/**
+ *
+ */
+void CTimeLineDropSource::InterpretKeyFlags(long inModifyerKeys)
+{
+ m_Copy = ((inModifyerKeys & CHotKeys::MODIFIER_CONTROL) != 0);
+}
+
+//===============================================================================
+/**
+ *
+ */
+bool CTimeLineDropSource::ValidateTarget(CDropTarget *inTarget)
+{
+ // the only thing we want to do from here is check the type.
+ bool theValidTarget = CStudioObjectTypes::AcceptableParent(
+ (EStudioObjectType)GetObjectType(), (EStudioObjectType)inTarget->GetObjectType());
+
+ for (size_t idx = 0, end = m_Instances.size(); idx < end && theValidTarget; ++idx) {
+ UICDM::CUICDMInstanceHandle theHandle(m_Instances[idx]);
+
+ if (theValidTarget && theHandle.Valid()) {
+ theValidTarget &= (!inTarget->IsSelf(theHandle) && !inTarget->IsRelative(theHandle));
+ UICDM::ISlideSystem *theSlideSystem =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theSlide = theSlideSystem->GetAssociatedSlide(theHandle);
+ bool theIsMaster = theSlideSystem->IsMasterSlide(theSlide);
+
+ theValidTarget &= !(theIsMaster && !inTarget->IsMaster());
+ }
+ }
+
+ SetHasValidTarget(theValidTarget);
+
+ return theValidTarget;
+}
+
+using namespace UICDM;
+using namespace Q3DStudio;
+
+inline void Rearrange(CDoc &inDoc, const UICDM::TInstanceHandleList &inInstances,
+ CUICDMInstanceHandle inTarget, DocumentEditorInsertType::Enum inInsertType)
+{
+ SCOPED_DOCUMENT_EDITOR(inDoc, QObject::tr("Rearrange Object"))
+ ->RearrangeObjects(inInstances, inTarget, inInsertType);
+}
+
+CCmd *CTimeLineDropSource::GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide)
+{
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ CClientDataModelBridge *theBridge = theDoc->GetStudioSystem()->GetClientDataModelBridge();
+
+ if (CanCopy()) {
+ SCOPED_DOCUMENT_EDITOR(*theDoc, QObject::tr("Duplicate Object"))
+ ->DuplicateInstances(m_Instances, inTarget,
+ ImportUtils::GetInsertTypeForDropType(inDestType));
+ } else {
+ // We can't do the rearrange inline because it deletes a timeline item.
+ // So we will effectively postmessage and do it out of line.
+ theDoc->GetCore()->GetDispatch()->FireOnAsynchronousCommand(
+ std::bind(Rearrange, std::ref(*theDoc), m_Instances, inTarget,
+ ImportUtils::GetInsertTypeForDropType(inDestType)));
+ }
+
+ return nullptr;
+}
diff --git a/src/Authoring/Studio/DragAndDrop/TimelineDropSource.h b/src/Authoring/Studio/DragAndDrop/TimelineDropSource.h
new file mode 100644
index 00000000..3a385518
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/TimelineDropSource.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __TIMELINEDROPSOURCE_H__
+#define __TIMELINEDROPSOURCE_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioApp.h"
+#include "DropSource.h"
+
+//==============================================================================
+// Forward
+//==============================================================================
+class CDropTarget;
+class IDragable;
+
+class CTimeLineDropSource : public CDropSource
+{
+protected:
+ bool m_Copy;
+
+public:
+ CTimeLineDropSource(long inFlavor, IDragable *inDraggable);
+
+ bool CanMove() override;
+ bool CanCopy() override;
+ bool ValidateTarget(CDropTarget *inTarget) override;
+ void InterpretKeyFlags(long inModifyerKeys) override;
+
+ CCmd *GenerateAssetCommand(UICDM::CUICDMInstanceHandle inTarget,
+ EDROPDESTINATION inDestType,
+ UICDM::CUICDMSlideHandle inSlide) override;
+};
+
+#endif // #ifndef __TIMELINEDROPSOURCE_H__
diff --git a/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.cpp b/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.cpp
new file mode 100644
index 00000000..25a1352b
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.cpp
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "TimelineDropTarget.h"
+#include "StudioApp.h"
+#include "DropSource.h"
+#include "HotKeys.h"
+#include "Core.h"
+#include "Doc.h"
+#include "IDropTargetHelper.h"
+#include "GraphUtils.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMSlides.h"
+
+// Timeline stuff
+
+//===============================================================================
+/**
+ * This get called on every DragWithin.
+ * Note: the source will validate the target instead of the otherway around.
+ * This is because the DropSource knows how to get information from itself without
+ * creating an asset. This is mainly for DropSources that have a lazy creation idiom.
+ * like files.
+ * @param the DropSource in question.
+ * @return true if the DropSource likes the DropTarget.
+ */
+bool CTimeLineDropTarget::Accept(CDropSource &inSource)
+{
+ bool theDropFlag = inSource.ValidateTarget(this);
+ return theDropFlag;
+}
+
+//===============================================================================
+/**
+ * This is where is actually happens.
+ * Note: At this point either everything should be verified, and setup in the dropsource.
+ * Then the only thing left to do is to get the Assets and move/copy or connect them.
+ * Or the dropsource needs the target to perform the actual drop.
+ * Note that if the Control key is depressed, the start time follows the current view time(
+ *i.e. playhead position )
+ *
+ * @param inSource the Object in question.
+ * @return true if the drop was successful .
+ */
+bool CTimeLineDropTarget::Drop(CDropSource &inSource)
+{
+ UICDM::CUICDMInstanceHandle theTargetInstance = GetInstance();
+
+ if (theTargetInstance.Valid()) {
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::ISlideSystem *theSlideSystem = theDoc->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theSlide = theDoc->GetActiveSlide();
+ if (!theSlideSystem->IsMasterSlide(theSlide)
+ && (inSource.GetCurrentFlags() & CHotKeys::MODIFIER_ALT)) {
+ if (CanAddToMaster()) {
+ UICDM::CUICDMSlideHandle theMasterSlideHandle =
+ theSlideSystem->GetMasterSlide(theSlide);
+ if (theMasterSlideHandle.Valid())
+ theSlide = theMasterSlideHandle;
+ }
+ }
+ CCmd *theCmd = inSource.GenerateAssetCommand(theTargetInstance, m_Destination, theSlide);
+ if (theCmd)
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ }
+
+ return true;
+}
+
+//===============================================================================
+/**
+ * This will get the objec ttype from the Asset.
+ * Note: The asset can change all of the time, so i always ask the asset for its type.
+ * @return the Studio object type.
+ */
+long CTimeLineDropTarget::GetObjectType()
+{
+ UICDM::CUICDMInstanceHandle theTargetInstance = GetTargetInstance();
+ if (theTargetInstance.Valid()) {
+ CClientDataModelBridge *theBridge =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ return theBridge->GetObjectType(theTargetInstance);
+ }
+
+ return m_ObjectType;
+}
+
+//===============================================================================
+/**
+ * Check to see if the Asset is a relative of our asset.
+ * @return true if the inAsset is a parent grandparent...etc. of this asset.
+ */
+bool CTimeLineDropTarget::IsRelative(UICDM::CUICDMInstanceHandle inInstance)
+{
+ bool theReturn = false; ///< Default return value.
+ UICDM::CUICDMInstanceHandle theThisInstance = GetInstance();
+
+ // This will check to see if the inAsset is already some sort of parent grandparent....etc.
+ if (theThisInstance.Valid())
+ theReturn = IsAscendant(theThisInstance, inInstance,
+ g_StudioApp.GetCore()->GetDoc()->GetAssetGraph());
+
+ return theReturn;
+}
+
+//===============================================================================
+/**
+ * Check to see if the inAsset is our asset.
+ * @param inAsset The Asset to check.
+ * @return true if we are the same.
+ */
+bool CTimeLineDropTarget::IsSelf(UICDM::CUICDMInstanceHandle inInstance)
+{
+ UICDM::CUICDMInstanceHandle theThisInstance = GetInstance();
+ // true if self.....
+ return (theThisInstance == inInstance);
+}
+
+//===============================================================================
+/**
+ * This method is used to detirmine validity for dropping on Master items.
+ * We do not allow Master items to be dropped on non-master targets because
+ * it does not make any hierarchical sense. Also checks for NULL and Scene object.
+ * (The scene object reports as a master object so that we can re-arrange layers (On the master
+ *slide).)
+ * Changed checking for scene to checking for root object, which may be a component. This
+ * would allow for the root component (as in edit component) to re-arrange its children, even if
+ *either
+ * party is not a master.
+ * @param inAsset The Asset to check.
+ * @return true if we are the same.
+ */
+bool CTimeLineDropTarget::IsMaster()
+{
+ if (!m_Instance.Valid())
+ return true;
+ else if (m_Instance == g_StudioApp.GetCore()->GetDoc()->GetActiveRootInstance())
+ return true;
+ else {
+ UICDM::ISlideSystem *theSlideSystem =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theSlide = theSlideSystem->GetAssociatedSlide(m_Instance);
+ return theSlideSystem->IsMasterSlide(theSlide);
+ }
+}
+
+void CTimeLineDropTarget::SetDestination(EDROPDESTINATION inDestination)
+{
+ m_Destination = inDestination;
+}
+
+EDROPDESTINATION CTimeLineDropTarget::GetDestination() const
+{
+ return m_Destination;
+}
+
+//===============================================================================
+/**
+ * Figure out the destination (parent) asset that the drop is to occur.
+ * In the case that the drop occurs ON this asset, then the to-be-dropped asset becomes a child
+ *of m_Asset
+ * Otherwise, this m_Asset ends up being a sibling
+ */
+UICDM::CUICDMInstanceHandle CTimeLineDropTarget::GetTargetInstance()
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ if (!theInstance.Valid())
+ return 0;
+
+ CClientDataModelBridge *theBridge =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ bool theIsActiveComponent = false;
+ bool theIsComponent = (theBridge->GetObjectType(theInstance) == OBJTYPE_COMPONENT);
+ if (theIsComponent)
+ theIsActiveComponent = theBridge->IsActiveComponent(theInstance);
+
+ // If the drop destination is ON, it will be valid if this is not a component or it's in the
+ // component timeline
+ if (m_Destination == EDROPDESTINATION_ON) {
+ if (!theIsComponent || theIsActiveComponent)
+ return theInstance;
+ else
+ return 0;
+ }
+
+ // if target is a component, and we want to insert it above/below, and we are viewing this
+ // component, then it's an invalid operation
+ // thus set the target to 0.
+ if (theIsActiveComponent)
+ return 0;
+
+ return theBridge->GetParentInstance(theInstance);
+}
+
+// LASTTIMELINE RELATED CODE \ No newline at end of file
diff --git a/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.h b/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.h
new file mode 100644
index 00000000..29133afd
--- /dev/null
+++ b/src/Authoring/Studio/DragAndDrop/TimelineDropTarget.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __TIMELINEDROPTARGET_H__
+#define __TIMELINEDROPTARGET_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropTarget.h"
+#include "DropSource.h"
+
+class CControl;
+class CDropSource;
+
+//==============================================================================
+/**
+ * @class
+ * @brief LEONARD.PONCE needs to enter a brief description here.
+ *
+ * LEONARD.PONCE needs to enter a long description here.
+ */
+class CTimeLineDropTarget : public CDropTarget
+{
+
+public:
+ CTimeLineDropTarget()
+ : m_Destination(EDROPDESTINATION_ON)
+ , m_InsertionMarkedRow(nullptr)
+ , m_InsertionMarkedIndent(0)
+ {
+ }
+ bool Accept(CDropSource &inSource) override;
+ bool Drop(CDropSource &inSource) override;
+ long GetObjectType() override;
+
+ bool IsRelative(UICDM::CUICDMInstanceHandle inInstance) override;
+ bool IsSelf(UICDM::CUICDMInstanceHandle inInstance) override;
+ bool IsMaster() override;
+
+ void SetDestination(EDROPDESTINATION inDestination);
+ EDROPDESTINATION GetDestination() const;
+
+ // Return info where the insertion markers is supposed to be drawn at.
+ CControl *GetInsertionMarkerRow() const { return m_InsertionMarkedRow; }
+ void SetInsertionMarkerRow(CControl *inControl) { m_InsertionMarkedRow = inControl; }
+ long GetInsertionMarkerIndent() const { return m_InsertionMarkedIndent; }
+ void SetInsertionMarkerIndent(long inIndent) { m_InsertionMarkedIndent = inIndent; }
+
+protected:
+ UICDM::CUICDMInstanceHandle GetTargetInstance();
+
+protected:
+ EDROPDESTINATION m_Destination;
+ CControl *m_InsertionMarkedRow;
+ long m_InsertionMarkedIndent;
+};
+
+#endif // #ifndef __TIMELINEDROPTARGET_H__
diff --git a/src/Authoring/Studio/English.lproj/Strings/Static.stri b/src/Authoring/Studio/English.lproj/Strings/Static.stri
new file mode 100644
index 00000000..d65e7fa7
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/Static.stri
Binary files differ
diff --git a/src/Authoring/Studio/English.lproj/Strings/Static.stro b/src/Authoring/Studio/English.lproj/Strings/Static.stro
new file mode 100644
index 00000000..0b3577f6
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/Static.stro
Binary files differ
diff --git a/src/Authoring/Studio/English.lproj/Strings/Strings.h b/src/Authoring/Studio/English.lproj/Strings/Strings.h
new file mode 100644
index 00000000..ef70db6e
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/Strings.h
@@ -0,0 +1,301 @@
+
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//===============================================================================================================================
+// CODEGEN -- FILE CREATED BY STUDIO CODE GENERATION SYSTEM
+//
+// Do not modify the contents of this file. Your changes will be destroyed by
+// code generation.
+//
+// Please see the CodeGen folder for more information or to make changes.
+// StringsReadme.htm
+//===============================================================================================================================
+//{{AFX_INSERT_LOCATION}}
+
+#ifndef STUDIOSTRINGSH
+#define STUDIOSTRINGSH
+
+#define STRING_RESOURCE_COUNT 254
+#define IDS_PREFS_PRESENTATIONWIDTH 1
+#define IDS_PREFS_PRESENTATIONHEIGHT 2
+#define IDS_PREFS_PRESENTATIONASPECTRATIO 3
+#define IDS_PREFS_AUTHORNAME 4
+#define IDS_PREFS_COMPANYNAME 5
+#define IDS_PREFS_NUDGEAMOUNT 6
+#define IDS_PREFS_SNAPRANGE 7
+#define IDS_PREFS_INTERPOLATIONDEFAULT 8
+#define IDS_PREFS_TIMELINEGRIDSNAPPING 9
+#define IDS_PREFS_TIMELINEGRIDRESOLUTION 10
+#define IDS_PREFS_RESTOREDEFAULTS 11
+#define IDS_PREFS_REFERENCECONTROLS 12
+#define IDS_WEBSITELINK 13
+#define IDS_SUPPORTEMAIL_TEXT 14
+#define IDS_SUPPORTEMAIL 15
+#define IDS_PALETTE_TIMELINE 16
+#define IDS_PALETTE_INSPECTOR 17
+#define IDS_PALETTE_SLIDE 18
+#define IDS_PALETTE_ACTION 19
+#define IDS_PALETTE_BASIC_OBJECTS 20
+#define IDS_PALETTE_PROJECT 21
+#define IDS_OBJTYPE_MASTER 22
+#define IDS_SLIDE_EDIT_MASTER 23
+#define IDS_SLIDE_LEAVE_MASTER 24
+#define IDS_SLIDE_DEFAULT_TITLE 25
+#define IDS_HELP_FILE_NAME 26
+#define IDS_PREF_RESTOREDEFAULT_TITLE 27
+#define IDS_PREF_RESTOREDEFAULT_TEXT 28
+#define IDS_PREF_INTERPOLATION_1 29
+#define IDS_PREF_INTERPOLATION_2 30
+#define IDS_PREF_SNAPRANGE_1 31
+#define IDS_PREF_SNAPRANGE_2 32
+#define IDS_PREF_SNAPRANGE_3 33
+#define IDS_ERROR_REFRESHRESOURCETEXT 34
+#define IDS_ERROR_REFRESHRESOURCETITLE 35
+#define IDS_ERROR_IMPORTRESOURCETEXT 36
+#define IDS_ERROR_IMPORTRESOURCETEXT_CHECKFILE 37
+#define IDS_ERROR_IMPORTRESOURCETEXT_REASON 38
+#define IDS_ERROR_IMPORTRESOURCETEXT_COMPLETEWITHWARNING 39
+#define IDS_ERROR_IMPORTRESOURCETEXT_FAILED 40
+#define IDS_ERROR_IMPORTRESOURCETITLE 41
+#define IDS_ERROR_IMPORTRESOURCETITLE_ERROR 42
+#define IDS_ERROR_IMPORTRESOURCETITLE_WARNING 43
+#define IDS_ERROR_IMPORTLUARESOURCETEXT 44
+#define IDS_ERROR_LOADPRESENTATION 45
+#define IDS_ERROR_LOADFILENOTEXIST 46
+#define IDS_ERROR_LOADPRESENTATION_TITLE 47
+#define IDS_ERROR_EXPORTPRESENTATION 48
+#define IDS_ERROR_MISSING_RESOURCES 49
+#define IDS_ERROR_IMPORTUNSUPPORTEDRESOURCETYPETEXT 50
+#define IDS_ERROR_MSGTITLE 51
+#define IDS_ERROR_CLIENTSAVE 52
+#define IDS_ERROR_PROJECT_VARIABLES_TITLE 53
+#define IDS_ERROR_PROJECT_VARIABLES_MSG 54
+#define IDS_ERROR_OBJECT_RENAME_TITLE 55
+#define IDS_ERROR_OBJECT_RENAME_EMPTY_STRING 56
+#define IDS_ERROR_OBJECT_RENAME_DUPLICATED_STRING 57
+#define IDS_ERROR_PATHOLOGICAL_PASTE_TITLE 58
+#define IDS_ERROR_PATHOLOGICAL_PASTE_MESSAGE 59
+#define IDS_ERROR_MSGPASSING 60
+#define IDS_WARNING_IMPORTLUARESOURCETEXT 61
+#define IDS_WARNING_MSGTITLE 62
+#define IDS_TITLE_WARNING 63
+#define IDS_FILE_DESC_UIP 64
+#define IDS_FILE_EXT_UIP 65
+#define IDS_LIBRARYIMPORT_MODEL 66
+#define IDS_LIBRARYIMPORT_IMAGE 67
+#define IDS_LIBRARYIMPORT_BEHAVIOR 68
+#define IDS_LIBRARYIMPORT_MESH 69
+#define IDS_LIBRARYIMPORT_IMPORT 70
+#define IDS_LIBRARYIMPORT_EFFECT 71
+#define IDS_PROJNAME 72
+#define IDS_REPORTDLG_INFO 73
+#define IDS_REPORTDLG_DESCHEADER 74
+#define IDS_REPORTDLG_EMAILHEADER 75
+#define IDS_REPORTDLG_SYSINFOHEADER 76
+#define IDS_COMMAND_COPYACTION 77
+#define IDS_COMMAND_PASTEACTION 78
+#define IDS_COMMAND_CUTACTION 79
+#define IDS_COMMAND_DELETEACTION 80
+#define IDS_HELP_BEHAVIORREFERENCE 81
+#define IDS_SAVE_READONLY_WARNING 82
+#define IDS_COMMENT_SUGGESTION_TOADDR 83
+#define IDS_COMMENT_BUG_TOADDR 84
+#define IDS_COMMENT_TYPE_SUGGESTION 85
+#define IDS_COMMENT_TYPE_BUG 86
+#define IDS_CM_RENAME_OBJECT 87
+#define IDS_CM_DUPLICATE_OBJECT 88
+#define IDS_CM_DELETE_OBJECT 89
+#define IDS_CM_MAKE_COMPONENT 90
+#define IDS_CM_COPY_OBJECT_PATH 91
+#define IDS_CM_EXTERNALIZE_OBJECT_BUFFER 92
+#define IDS_CM_INTERNALIZE_OBJECT_BUFFER 93
+#define IDS_CM_INSPECT_COMPONENT 94
+#define IDS_TEXTEDIT_CONTEXT_MENU_CUT 95
+#define IDS_TEXTEDIT_CONTEXT_MENU_COPY 96
+#define IDS_TEXTEDIT_CONTEXT_MENU_PASTE 97
+#define IDS_UIC_STUDIO_VERSION 98
+#define IDS_CRASH_REPORT_TOADDR 99
+#define IDS_CRASH_REPORT_FROMADDR 100
+#define IDS_CRASH_REPORT_SERVERADDR 101
+#define IDS_UICOMPOSER_PALETTE_SETTINGS_REGISTRY_KEY 102
+#define IDS_COPYPASTE_FORMAT_NAME 103
+#define IDS_STUDIOFONT_SIZE 104
+#define IDS_STUDIOPREFSTITLE 105
+#define IDS_NOOBJECTSELECTED 106
+#define IDS_CONTEXTKEY_SETINTERPOLATION 107
+#define IDS_CONTEXTKEY_DELETESELECTED 108
+#define IDS_CONTEXTKEY_CUTSELECTED 109
+#define IDS_CONTEXTKEY_COPYSELECTED 110
+#define IDS_CONTEXTKEY_PASTE 111
+#define IDS_CONTEXTKEY_INSERTKEY 112
+#define IDS_CONTEXTKEY_SETKEYFRAMETIME 113
+#define IDS_CONTEXTKEY_DELETECHANNEL 114
+#define IDS_CONTEXTKEY_BARCOLOR 115
+#define IDS_CONTEXTKEY_BARTEXT 116
+#define IDS_CONTEXTKEY_SHOWTIMEBARHANDLES 117
+#define IDS_CONTEXTKEY_HIDETIMEBARHANDLES 118
+#define IDS_CONTEXTKEY_SETTIMEBARTIME 119
+#define IDS_CONTEXTKEY_MAKE_ANIMATION_DYNAMIC 120
+#define IDS_CONTEXTKEY_MAKE_ANIMATION_STATIC 121
+#define IDS_CONTEXTKEY_MAKE_ANIMATIONS_DYNAMIC 122
+#define IDS_CONTEXTKEY_MAKE_ANIMATIONS_STATIC 123
+#define IDS_CONTEXT_SLIDE_NEW 124
+#define IDS_CONTEXT_SLIDE_DELETE 125
+#define IDS_CONTEXT_SLIDE_DUPLICATE 126
+#define IDS_CONTEXT_LINK_STATEWISE 127
+#define IDS_CONTEXT_UNLINK_STATEWISE 128
+#define IDS_HK_CMD 129
+#define IDS_HK_ALPHALOCK 130
+#define IDS_CONTROLS_FLOAT_MINUS 131
+#define IDS_CONTROLS_FLOAT_PERIOD 132
+#define IDS_PROJECT_BEHAVIORLIB_PATH 133
+#define IDS_PROJECT_EFFECTLIB_PATH 134
+#define IDS_PROJECT_FONTLIB_PATH 135
+#define IDS_PROJECT_IMAGELIB_PATH 136
+#define IDS_PROJECT_MATERIALLIB_PATH 137
+#define IDS_PROJECT_MODELLIB_PATH 138
+#define IDS_PROJECT_BUTTON_BEHAVIOR 139
+#define IDS_PROJECT_BUTTON_EFFECT 140
+#define IDS_PROJECT_BUTTON_FONT 141
+#define IDS_PROJECT_BUTTON_IMAGE 142
+#define IDS_PROJECT_BUTTON_MATERIAL 143
+#define IDS_PROJECT_BUTTON_MODEL 144
+#define IDS_PROJECT_CM_SHOW_IN_EXPLORER 145
+#define IDS_PROJECT_CM_COPY_PATH 146
+#define IDS_PROJECT_CM_COPY_FULL_PATH 147
+#define IDS_PROJECT_CM_LOCATE_FOLDER 148
+#define IDS_PROJECT_CM_LOCATE_FILE 149
+#define IDS_PROJECT_FILE_NOT_IN_DOCUMENT_SUBDIR 150
+#define IDS_UNTITLED_DOCUMENT_TITLE 151
+#define IDS_CREATE_NEW_DOCUMENT_TITLE 152
+#define IDS_MENU_WIN_UNDO_FORMAT 153
+#define IDS_MENU_WIN_REDO_FORMAT 154
+#define IDS_MENU_WIN_COPY_FORMAT 155
+#define IDS_MENU_WIN_PASTE_FORMAT 156
+#define IDS_MENU_WIN_CUT_FORMAT 157
+#define IDS_MENU_COPYPASTE_TYPE_ACTION 158
+#define IDS_MENU_COPYPASTE_TYPE_KEYFRAMES 159
+#define IDS_MENU_COPYPASTE_TYPE_OBJECT 160
+#define IDS_PROMPT_FOR_SAVE 161
+#define IDS_PROMPT_FOR_REVERT 162
+#define IDS_WAIT_LOADING 163
+#define IDS_HELP_VISIT_QT 164
+#define IDS_FLTR_TOOLTIP_BEHAVIOR1 165
+#define IDS_FLTR_TOOLTIP_BEHAVIOR2 166
+#define IDS_FLTR_TOOLTIP_PROPERTIES1 167
+#define IDS_FLTR_TOOLTIP_PROPERTIES2 168
+#define IDS_FLTR_TOOLTIP_MATERIALS1 169
+#define IDS_FLTR_TOOLTIP_MATERIALS2 170
+#define IDS_FLTR_TOOLTIP_SHY1 171
+#define IDS_FLTR_TOOLTIP_SHY2 172
+#define IDS_FLTR_TOOLTIP_VISIBLE1 173
+#define IDS_FLTR_TOOLTIP_VISIBLE2 174
+#define IDS_FLTR_TOOLTIP_LOCK1 175
+#define IDS_FLTR_TOOLTIP_LOCK2 176
+#define IDS_TOOLTIP_ANIMATE_TOGGLE 177
+#define IDS_TIME_EDIT_DLG_GO_TO_TIME 178
+#define IDS_TIME_EDIT_DLG_SET_KEYFRAME_TIME 179
+#define IDS_TIME_EDIT_DLG_SET_TIMEBAR_TIME 180
+#define IDS_TIME_EDIT_DLG_START_TIME 181
+#define IDS_TIME_EDIT_DLG_END_TIME 182
+#define IDS_TIME_EDIT_DLG_MIN_SEC_MSEC 183
+#define IDS_REFRESH_IMPORT 184
+#define IDS_COLORCHOOSER_AUTO 185
+#define IDS_COLORCHOOSER_MORE 186
+#define IDS_SPLASH_COPYRIGHT1 187
+#define IDS_SPLASH_COPYRIGHT2 188
+#define IDS_ABOUT_COPYRIGHT 189
+#define IDS_ABOUT_PAINTLIB_CREDIT 190
+#define IDS_ABOUT_FBX_CREDIT 191
+#define IDS_DEFAULT_PALETTE_LAYOUT 192
+#define IDS_BASIC_OBJ_PALETTE_LAYER 193
+#define IDS_BASIC_OBJ_PALETTE_CAMERA 194
+#define IDS_BASIC_OBJ_PALETTE_LIGHT 195
+#define IDS_BASIC_OBJ_PALETTE_GROUP 196
+#define IDS_BASIC_OBJ_PALETTE_CUBE 197
+#define IDS_BASIC_OBJ_PALETTE_CYLINDER 198
+#define IDS_BASIC_OBJ_PALETTE_CONE 199
+#define IDS_BASIC_OBJ_PALETTE_RECTANGLE 200
+#define IDS_BASIC_OBJ_PALETTE_SPHERE 201
+#define IDS_BASIC_OBJ_PALETTE_TEXT 202
+#define IDS_BASIC_OBJ_PALETTE_COMPONENT 203
+#define IDS_BASIC_OBJ_PALETTE_ALIAS 204
+#define IDS_CREATE_NEW_PALETTE 205
+#define IDS_MOVE_PALETTE 206
+#define IDS_GROUP_EXPAND_TOGGLE_TOOLTIP 207
+#define IDS_GROUP_MOVE_UP_TOOLTIP 208
+#define IDS_GROUP_MOVE_DOWN_TOOLTIP 209
+#define IDS_ACTION_DELETE_TOOLTIP 210
+#define IDS_HANDLER_CONTROL_NAME 211
+#define IDS_EVENT_CONTROL_NAME 212
+#define IDS_EVENT_TARGET_OBJ 213
+#define IDS_EVENT_TRIGGER_OBJ 214
+#define IDS_ACTION_LISTEN_FOR_STRING 215
+#define IDS_ACTION_LISTEN_FOR_TELL_STRING 216
+#define IDS_ACTION_LISTEN_TO_FOR_STRING 217
+#define IDS_ACTION_LISTEN_TO_FOR_TELL_STRING 218
+#define IDS_ACTION_NEW 219
+#define IDS_DELETE_ACTION 220
+#define IDS_PROPERTIES_BASIC 221
+#define IDS_PROPERTIES_SHARED 222
+#define IDS_OBJ_REF_PATH 223
+#define IDS_OBJ_REF_PATH_TYPE 224
+#define IDS_OBJ_REF_PATH_ABSOLUTE 225
+#define IDS_OBJ_REF_PATH_RELATIVE 226
+#define IDS_PROP_UNKNOWN_SOURCE 227
+#define IDS_PROP_UNKNOWN_TARGET 228
+#define IDS_PROP_UNKNOWN_PROP 229
+#define IDS_PROP_UNKNOWN_HANDLER 230
+#define IDS_ACTION_ENABLE_STATE 231
+#define IDS_ASSET_NAME_UNKNOWN 232
+#define IDS_DELETE_OBJ_CONFIRM_TITLE 233
+#define IDS_DELETE_OBJ_CONFIRM_MSG 234
+#define IDS_SCENE_CAMERA_VIEW 235
+#define IDS_PROJECT_VARIABLES_FORMAT 236
+#define IDS_CONFIG_EXPORT_PREVIEW 237
+#define IDS_CONFIG_EXPORT_DEPLOY 238
+#define IDS_CONFIG_PREVIEW_TITLE 239
+#define IDS_CONFIG_PREVIEW_ERROR_TITLE 240
+#define IDS_CONFIG_PREVIEW_ERROR_MSG 241
+#define IDS_CONFIG_PREVIEW_ERROR_TEXT 242
+#define IDS_CONFIG_PREVIEW_REDIRECT_ERROR 243
+#define IDS_CONFIG_PREVIEW_INPUT 244
+#define IDS_CONFIG_PREVIEW_OUTPUT 245
+#define IDS_RENAME_OBJ_CONFIRM_TITLE 246
+#define IDS_RENAME_OBJ_CONFIRM_MSG 247
+#define IDS_BUILDCONFIGS_ERROR_TITLE 248
+#define IDS_MISSINGSLIDE 249
+#define IDS_SAVE_UNKNOWN_ERROR 250
+#define IDS_GL_VERSION_ERROR 251
+#define IDS_GL_VERSION_WARNING 252
+#define IDS_PRESENTATION_MODIFIED_EXTERNALLY 253
+#ifdef KDAB_TEMPORARILY_REMOVED
+#define placeholder 254
+#endif
+
+#endif //STUDIOSTRINGSH \ No newline at end of file
diff --git a/src/Authoring/Studio/English.lproj/Strings/StringsReadme.htm b/src/Authoring/Studio/English.lproj/Strings/StringsReadme.htm
new file mode 100644
index 00000000..78dec4f1
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/StringsReadme.htm
@@ -0,0 +1,124 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Language" content="en-us">
+<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
+<meta name="ProgId" content="FrontPage.Editor.Document">
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+<title>Studio String Files</title>
+</head>
+
+<body>
+
+<h2>Studio String Files</h2>
+<p>The directory Studio\English.lproj\Strings contain files that help manage the
+following ideas:</p>
+<ul>
+ <li>the text strings displayed in Studio's user interface and how they are
+ mapped onto integer-based identifiers in the C++ source code</li>
+ <li>the process of internationalization (or localization) of the GUI and the
+ strings that are displayed in non-English versions of Studio</li>
+</ul>
+<p>This document provides an overview of the various files and how they are
+used.</p>
+<address align="right">
+ Paul Tweedlie, January 27 2005</address>
+<h3>Compile.py</h3>
+<p>The Python script 'compile.py' takes the 2 input files (stri) and generates
+the 2 output files (stro) and a C++ header file 'Strings.h'. The 2 stri input
+files are ASCII-based and are typically edited by code developers while adding
+new GUI and functionality. The 2 stro files generated by the script are 16-bit
+Unicode-based files that are designed to be language-independent, see
+<a href="http://www.unicode.org/">http://www.unicode.org/</a>. The 2 stro files
+form part of the Studio release and are loaded by Studio.exe on start-up. They
+are installed on a user's machine in the directory:</p>
+<p>&nbsp;&nbsp;&nbsp; C:\Program Files\Qt 3D Studio\res\Strings</p>
+<p>The stro files may be edited at the end of a development cycle to localize
+Studio's GUI from English to another language that may use a different script,
+e.g. Japanese.</p>
+<p>To run the compile.py script, you need a Python interpreter installed on your
+machine, get one from <a href="http://www.python.org/">http://www.python.org/</a>.
+The typical steps for a code developer are:</p>
+<ol>
+ <li>Check out the input files <i>and</i> the output files before running the
+ script. It's easiest to just check out the entire Strings directory using
+ Perforce.</li>
+ <li>Make your text changes to the stri file(s)</li>
+ <li>Run the compile.py script</li>
+ <li>Build Studio and verify your changes</li>
+ <li>Go to Perforce and revert unchanged files in the Strings directory</li>
+ <li>Check in the Strings directory back into Perforce</li>
+</ol>
+<h3>Strings.h</h3>
+<p>The C++ header file 'Strings.h' is auto-generated and should never be hand
+edited. The file contains a bunch of #define statements that set up constant
+identifiers where each identifier is assigned a unique integer. For example,</p>
+<p>&nbsp;&nbsp;&nbsp; #define IDS_CONTEXT_SLIDE_NEW 570</p>
+<h3>Stri: ASCII Input Files</h3>
+<div align="center">
+ <center>
+ <table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="90%" id="AutoNumber1">
+ <tr>
+ <td width="15%">Static.stri</td>
+ <td width="85%">An ASCII text file that specifies the strings that are
+ displayed in Studio's user interface such as buttons and tooltips. The
+ file is organized as a mapping of pairs <b>from</b> integer-based
+ identifiers that are referenced in the C++ source code<b> to</b> text
+ strings. For example, the mapping for the 'New Slide' button appears as:<p>&nbsp;&nbsp;&nbsp;
+ &lt;string name=&quot;IDS_CONTEXT_SLIDE_NEW&quot; value=&quot;New Slide&quot; /&gt;</p>
+ <p>where the integer identifier 'IDS_CONTEXT_SLIDE_NEW' is used in the
+ source file SlideContextMenu.cpp and 'New Slide' is the text that appears
+ in the list of options when the user right-clicks in the Slide palette.
+ </p>
+ <p>Developers should edit this file when writing new GUI code, e.g. adding
+ new buttons.</td>
+ </tr>
+ </table>
+ </center>
+</div>
+<h3>Stro: Unicode Output Files</h3>
+<div align="center">
+ <center>
+ <table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="90%" id="AutoNumber1">
+ <tr>
+ <td width="15%">Static.stro</td>
+ <td width="85%">A Unicode file that specifies the strings that are
+ displayed in Studio's user interface such as buttons and tooltips. The
+ file is organized into a list of triples: an identifier, a numeric value,
+ and a Unicode string. For example, the triple representing the 'New Slide'
+ button appears as:<p>&nbsp;&nbsp;&nbsp; &lt;string name=&quot;IDS_CONTEXT_SLIDE_NEW&quot;
+ ID=&quot;570&quot; value=&quot;New Slide&quot;/&gt;. </p>
+ <p>Language translators should edit the 'value' strings in this file. For
+ example, the above triple may be translated as follows:</p>
+ <p>&nbsp;&nbsp; &lt;string name=&quot;IDS_CONTEXT_SLIDE_NEW&quot; ID=&quot;570&quot;
+ value=&quot;Nouveau Slide&quot;/&gt;. </td>
+ </tr>
+ </table>
+ </center>
+</div>
+<h3>Localization</h3>
+<p>Studio's user interface uses the Arial font by default and a point side of
+13pt. If you want to change the GUI's font and/or the point size, you should
+search in the Static.stro file for the following identifiers:</p>
+<p>&nbsp;&nbsp; &lt;string name=&quot;IDS_STUDIOFONT_FACE&quot; ID=&quot;548&quot; value=&quot;Arial&quot;/&gt;</p>
+<p>&nbsp;&nbsp; &lt;string name=&quot;IDS_STUDIOFONT_SIZE&quot; ID=&quot;547&quot; value=&quot;13&quot;</p>
+<p>If you want to use a font that supports non-English characters, for instance
+Japanese symbols, you should first change the 'Arial' font to something like
+'Arial Unicode MS'. For example,</p>
+<p>&nbsp;&nbsp; &lt;string name=&quot;IDS_STUDIOFONT_FACE&quot; ID=&quot;548&quot; value=&quot;Arial Unicode
+MS&quot;/&gt;</p>
+<p>Next you substitute all the English strings in the stro files for their
+Japanese-translated counterparts. You will need to use an editor such as Notepad
+or Wordpad that supports the Unicode format. For example, perhaps the 'New
+Slide' option is translated so:</p>
+<p>&nbsp;&nbsp; &lt;string name=&quot;IDS_CONTEXT_SLIDE_NEW&quot; ID=&quot;570&quot; value=&quot;&#12461;&#12515;&#12483;&#12471;&#12531;&quot;/&gt;.
+</p>
+<p>Note from Gim Guan Chua: To see the Japanese symbols in Studio, you may have
+to set your machine to Japanese settings (in Control Panel-&gt;Regional and
+Language Settings-&gt;Advance). Studio is considered a &quot;non-Unicode&quot; program in
+contrast to Notepad and Wordpad which are Unicode-aware in XP.</p>
+<p>&nbsp;</p>
+
+</body>
+
+</html>
diff --git a/src/Authoring/Studio/English.lproj/Strings/compile.py b/src/Authoring/Studio/English.lproj/Strings/compile.py
new file mode 100644
index 00000000..255d1f45
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/compile.py
@@ -0,0 +1,245 @@
+'''
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+'''
+
+'''Compile the string input (stri) into string output (stro) + Strings.h'''
+import os, os.path, re
+import xml.sax.handler, xml.sax, codecs
+
+
+class CGenericXMLFactory:
+ '''Generic factory for xml generation'''
+ def __init__( self ):
+ self.m_Elements = {}
+
+ def AddTagElement( self, inName, inFunction ):
+ self.m_Elements[inName] = inFunction
+
+ def CreateObject( self, inName, inAttrs, inParent ):
+ theElement = self.m_Elements[inName](inName, inAttrs, inParent )
+ return theElement
+
+
+class CBaseXMLHandler( xml.sax.handler.ContentHandler ):
+ """Base xml parsing class. Can be used to form a dom system"""
+ def __init__( self, inFactory ):
+ self.m_Objects = [] #stack of objects used for parsing (targets require the latest project)
+ self.m_Factory = inFactory
+ self.m_CurrentObject = None
+ self.m_CharacterHandler = None
+ self.m_TopObject = None
+
+ def startElement( self, name, attrs ):
+ theParent = None
+ if( not len(self.m_Objects) == 0 ):
+ theParent = self.m_Objects[-1]
+ self.m_CurrentObject = self.m_Factory.CreateObject( name, attrs, theParent )
+ try:
+ self.m_CurrentObject.startElement( self )
+ except AttributeError:
+ pass #Don't care if the object does not support the method call
+ self.OnObjectCreated( self.m_CurrentObject )
+
+ def endElement( self, name ):
+ theElement = self.m_Objects.pop()
+ try:
+ theElement.endElement( self )
+ except AttributeError:
+ pass #Don't care if the object does not support the method call
+
+ if( len( self.m_Objects ) != 0 ):
+ self.m_CurrentObject = self.m_Objects[-1]
+
+ def characters( self, content ):
+ if( self.m_CharacterHandler ):
+ self.m_CharacterHandler( self.m_CurrentObject, content )
+
+ def SetCharacterHandler( self, inHandler ):
+ self.m_CharacterHandler = inHandler
+
+ def OnObjectCreated( self, theObject ):
+ '''Callback every time a factory returns an object'''
+ self.m_Objects.append( theObject )
+ if( len( self.m_Objects ) == 1 ):
+ self.m_TopObject = self.m_CurrentObject
+
+ def SetFactory( self, inFactory ):
+ self.m_Factory = inFactory
+
+ def Parse( self, inFilename ):
+ '''Parse the file, grabbing the extension'''
+ theDirectory = os.path.dirname( inFilename )
+ theFile = os.path.basename( inFilename )
+ self.m_BasePath = theDirectory
+ theReturn = None
+ theExcept = None
+ self.m_TopObject = None
+ self.m_CurrentObject = None
+ self.m_Objects = []
+ theParse = xml.sax.parse( inFilename, self )
+ return self.m_TopObject
+
+
+class CStringObject:
+ '''Basic string object'''
+ def __init__( self ):
+ self.m_Name = ''
+ self.m_Value = ''
+
+ def SetName( self, inName ):
+ self.m_Name = inName
+
+ def GetName( self ):
+ return self.m_Name
+
+ def SetValue( self, inValue ):
+ self.m_Value = inValue
+
+ def GetValue( self ):
+ return self.m_Value
+
+def XMLStringObjectInit( inName, inAttrs, inParent ):
+ '''Create a string object from xml'''
+ theObject = CStringObject( )
+ try:
+ theObject.SetName( inAttrs['name'] )
+ theObject.SetValue( inAttrs['value'] )
+ except KeyError:
+ print 'Failed to parse string object'
+ inParent.AddObject( theObject )
+ return theObject
+
+
+class CStringObjectList:
+ '''Create a list of string objects'''
+ def __init__( self ):
+ self.m_StringObjects = []
+
+ def AddObject( self, inStringObject ):
+ self.m_StringObjects.append( inStringObject )
+
+ def GetObjects( self ):
+ return self.m_StringObjects
+
+def XMLStringObjectListInit( inName, inAttrs, inParent ):
+ '''Create a list from xml'''
+ theStringList = CStringObjectList()
+ return theStringList
+
+def ExportObjects( inFactory ):
+ inFactory.AddTagElement( 'stringresourceinput', XMLStringObjectListInit )
+ inFactory.AddTagElement( 'string', XMLStringObjectInit )
+
+
+
+
+###Main Code Below###########################################
+theLists = []
+#parse into a named group the matches anything up to the period and an ending of stri
+theSourceRegex = re.compile( r'(?P<stem>.*)\.stri$' ) # $ means end of line/input
+theTotalFiles = os.listdir( '.' ) #List files in the current working directory
+
+
+#Create xml parsing context
+theFactory = CGenericXMLFactory()
+theHandler = CBaseXMLHandler( theFactory )
+ExportObjects( theFactory ) #Export the dom objects we would like to use
+
+theStringCount = 0
+
+for theFile in theTotalFiles:
+ theMatch = theSourceRegex.match( theFile )
+ if( theMatch ):
+ theList = None
+ try:
+ print 'parsing %s' % ( theFile )
+ theList = theHandler.Parse( theFile )
+ theLists.append( ( theMatch.group( 'stem' ), theList ) ) #append a touple of the source name
+ theStringCount += len( theList.GetObjects() )
+ except Exception, e:
+ print 'Failed to parse %s: %s' % ( theFile, e )
+ #salvage anything possible
+ theList = theHandler.m_TopObject
+ if( theList ):
+ theLists.append( ( theMatch.group( 'stem' ), theList ) ) #append a touple of the source name
+ theStringCount += len( theList.GetObjects() )
+
+theIndex = 1
+theHeaderName = 'Strings.h'
+theHeaderFile = open( 'Strings.h', 'w' )
+
+theHeaderLine = '''
+//===============================================================================================================================
+// CODEGEN -- FILE CREATED BY STUDIO CODE GENERATION SYSTEM
+//
+// Do not modify the contents of this file. Your changes will be destroyed by
+// code generation.
+//
+// Please see the CodeGen folder for more information or to make changes.
+// C:\NVIDIA\p4sw\sw\contenttools\UIComposer\ViewComposer\Main\Studio\English.lproj\Strings\StringsReadme.htm\StringsReadme.htm
+//===============================================================================================================================
+//{{AFX_INSERT_LOCATION}}
+
+#ifndef STUDIOSTRINGSH
+#define STUDIOSTRINGSH
+
+#define STRING_RESOURCE_COUNT %s
+''' % ( theStringCount )
+
+theResultLine = '''
+<!-- ============================================================================================================================== -->
+<!-- CODEGEN - FILE CREATED BY STUDIO CODE GENERATION SYSTEM -->
+<!-- -->
+<!-- Do not modify the contents of this file. Your changes will be destroyed -->
+<!-- by code generation. -->
+<!-- -->
+<!-- Please see the CodeGen folder for more information or to make changes. -->
+<!-- C:\NVIDIA\p4sw\sw\contenttools\UIComposer\ViewComposer\Main\Studio\English.lproj\Strings\StringsReadme.htm\StringsReadme.htm -->
+<!-- ============================================================================================================================== -->
+
+<stringresourceinput>
+'''
+
+theHeaderFile.write( theHeaderLine )
+for theName, theList in theLists:
+ theFileName = theName + '.stro'
+ print 'write file %s' % ( theFileName )
+ theResultFile = codecs.open( theFileName, 'w', 'utf-16' )
+ theResultFile.write( theResultLine )
+ for theObject in theList.GetObjects():
+ theName = theObject.GetName()
+ theValue = theObject.GetValue()
+ theResultFile.write( '\t<string name="%s" ID="%s" value="%s"/>\n' % ( theName, theIndex, theValue ) )
+ theHeaderFile.write( '#define %s %s\n' % ( theName, theIndex ) )
+ theIndex+= 1
+ theResultFile.write( '</stringresourceinput>' )
+ theResultFile.close()
+
+theHeaderFile.write( '\n#endif //STUDIOSTRINGSH' )
+theHeaderFile.close()
diff --git a/src/Authoring/Studio/English.lproj/Strings/compile32.py b/src/Authoring/Studio/English.lproj/Strings/compile32.py
new file mode 100644
index 00000000..a1be6cab
--- /dev/null
+++ b/src/Authoring/Studio/English.lproj/Strings/compile32.py
@@ -0,0 +1,272 @@
+'''
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+'''
+'''Compile the string input (stri) into string output (stro) + Strings.h'''
+import os, os.path, re
+import xml.sax.handler, xml.sax, codecs
+
+
+class CGenericXMLFactory:
+ '''Generic factory for xml generation'''
+ def __init__( self ):
+ self.m_Elements = {}
+
+ def AddTagElement( self, inName, inFunction ):
+ self.m_Elements[inName] = inFunction
+
+ def CreateObject( self, inName, inAttrs, inParent ):
+ theElement = self.m_Elements[inName](inName, inAttrs, inParent )
+ return theElement
+
+
+class CBaseXMLHandler( xml.sax.handler.ContentHandler ):
+ """Base xml parsing class. Can be used to form a dom system"""
+ def __init__( self, inFactory ):
+ self.m_Objects = [] #stack of objects used for parsing (targets require the latest project)
+ self.m_Factory = inFactory
+ self.m_CurrentObject = None
+ self.m_CharacterHandler = None
+ self.m_TopObject = None
+
+ def startElement( self, name, attrs ):
+ theParent = None
+ if( not len(self.m_Objects) == 0 ):
+ theParent = self.m_Objects[-1]
+ self.m_CurrentObject = self.m_Factory.CreateObject( name, attrs, theParent )
+ try:
+ self.m_CurrentObject.startElement( self )
+ except AttributeError:
+ pass #Don't care if the object does not support the method call
+ self.OnObjectCreated( self.m_CurrentObject )
+
+ def endElement( self, name ):
+ theElement = self.m_Objects.pop()
+ try:
+ theElement.endElement( self )
+ except AttributeError:
+ pass #Don't care if the object does not support the method call
+
+ if( len( self.m_Objects ) != 0 ):
+ self.m_CurrentObject = self.m_Objects[-1]
+
+ def characters( self, content ):
+ if( self.m_CharacterHandler ):
+ self.m_CharacterHandler( self.m_CurrentObject, content )
+
+ def SetCharacterHandler( self, inHandler ):
+ self.m_CharacterHandler = inHandler
+
+ def OnObjectCreated( self, theObject ):
+ '''Callback every time a factory returns an object'''
+ self.m_Objects.append( theObject )
+ if( len( self.m_Objects ) == 1 ):
+ self.m_TopObject = self.m_CurrentObject
+
+ def SetFactory( self, inFactory ):
+ self.m_Factory = inFactory
+
+ def Parse( self, inFilename ):
+ '''Parse the file, grabbing the extension'''
+ theDirectory = os.path.dirname( inFilename )
+ theFile = os.path.basename( inFilename )
+ self.m_BasePath = theDirectory
+ theReturn = None
+ theExcept = None
+ self.m_TopObject = None
+ self.m_CurrentObject = None
+ self.m_Objects = []
+ theParse = xml.sax.parse( inFilename, self )
+ return self.m_TopObject
+
+
+class CStringObject:
+ '''Basic string object'''
+ def __init__( self ):
+ self.m_Name = ''
+ self.m_Value = ''
+
+ def SetName( self, inName ):
+ self.m_Name = inName
+
+ def GetName( self ):
+ return self.m_Name
+
+ def SetValue( self, inValue ):
+ self.m_Value = inValue
+
+ def GetValue( self ):
+ return self.m_Value
+
+def XMLStringObjectInit( inName, inAttrs, inParent ):
+ '''Create a string object from xml'''
+ theObject = CStringObject( )
+ try:
+ theObject.SetName( inAttrs['name'] )
+ theObject.SetValue( inAttrs['value'] )
+ except KeyError:
+ print("Failed to parse string object")
+
+ inParent.AddObject( theObject )
+ return theObject
+
+
+class CStringObjectList:
+ '''Create a list of string objects'''
+ def __init__( self ):
+ self.m_StringObjects = []
+
+ def AddObject( self, inStringObject ):
+ self.m_StringObjects.append( inStringObject )
+
+ def GetObjects( self ):
+ return self.m_StringObjects
+
+def XMLStringObjectListInit( inName, inAttrs, inParent ):
+ '''Create a list from xml'''
+ theStringList = CStringObjectList()
+ return theStringList
+
+def ExportObjects( inFactory ):
+ inFactory.AddTagElement( 'stringresourceinput', XMLStringObjectListInit )
+ inFactory.AddTagElement( 'string', XMLStringObjectInit )
+
+
+
+
+###Main Code Below###########################################
+theLists = []
+#parse into a named group the matches anything up to the period and an ending of stri
+theSourceRegex = re.compile( r'(?P<stem>.*)\.stri$' ) # $ means end of line/input
+theTotalFiles = os.listdir( '.' ) #List files in the current working directory
+
+
+#Create xml parsing context
+theFactory = CGenericXMLFactory()
+theHandler = CBaseXMLHandler( theFactory )
+ExportObjects( theFactory ) #Export the dom objects we would like to use
+
+theStringCount = 0
+
+for theFile in theTotalFiles:
+ theMatch = theSourceRegex.match( theFile )
+ if( theMatch ):
+ theList = None
+ try:
+ print( 'parsing %s' % ( theFile ) )
+ theList = theHandler.Parse( theFile )
+ theLists.append( ( theMatch.group( 'stem' ), theList ) ) #append a touple of the source name
+ theStringCount += len( theList.GetObjects() )
+ except Exception as e:
+ print( 'Failed to parse %s: %s' % ( theFile, e ) )
+ #salvage anything possible
+ theList = theHandler.m_TopObject
+ if( theList ):
+ theLists.append( ( theMatch.group( 'stem' ), theList ) ) #append a touple of the source name
+ theStringCount += len( theList.GetObjects() )
+
+theIndex = 1
+theHeaderName = 'Strings.h'
+theHeaderFile = open( 'Strings.h', 'w' )
+
+theHeaderLine = r'''
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//===============================================================================================================================
+// CODEGEN -- FILE CREATED BY STUDIO CODE GENERATION SYSTEM
+//
+// Do not modify the contents of this file. Your changes will be destroyed by
+// code generation.
+//
+// Please see the CodeGen folder for more information or to make changes.
+// StringsReadme.htm
+//===============================================================================================================================
+//{{AFX_INSERT_LOCATION}}
+
+#ifndef STUDIOSTRINGSH
+#define STUDIOSTRINGSH
+
+#define STRING_RESOURCE_COUNT %s
+''' % ( theStringCount )
+
+theResultLine = r'''
+<!-- ============================================================================================================================== -->
+<!-- CODEGEN - FILE CREATED BY STUDIO CODE GENERATION SYSTEM -->
+<!-- -->
+<!-- Do not modify the contents of this file. Your changes will be destroyed -->
+<!-- by code generation. -->
+<!-- -->
+<!-- Please see the CodeGen folder for more information or to make changes. -->
+<!-- StringsReadme.htm -->
+<!-- ============================================================================================================================== -->
+
+<stringresourceinput>
+'''
+
+theHeaderFile.write( theHeaderLine )
+for theName, theList in theLists:
+ theFileName = theName + '.stro'
+ print('write file %s' % ( theFileName ))
+ theResultFile = codecs.open( theFileName, 'w', 'utf-16' )
+ theResultFile.write( theResultLine )
+ for theObject in theList.GetObjects():
+ theName = theObject.GetName()
+ theValue = theObject.GetValue()
+ theResultFile.write( '\t<string name="%s" ID="%s" value="%s"/>\n' % ( theName, theIndex, theValue ) )
+ theHeaderFile.write( '#define %s %s\n' % ( theName, theIndex ) )
+ theIndex+= 1
+ theResultFile.write( '</stringresourceinput>' )
+ theResultFile.close()
+
+theHeaderFile.write( '\n#endif //STUDIOSTRINGSH' )
+theHeaderFile.close()
diff --git a/src/Authoring/Studio/MainFrm.cpp b/src/Authoring/Studio/MainFrm.cpp
new file mode 100644
index 00000000..9db47a65
--- /dev/null
+++ b/src/Authoring/Studio/MainFrm.cpp
@@ -0,0 +1,2049 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "MainFrm.h"
+#include "ui_MainFrm.h"
+
+#include "StudioConst.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Bindings/TimelineTranslationManager.h"
+#include "Bindings/UICDMTimelineItemBinding.h"
+#include "Bindings/ITimelineTimebar.h"
+#include "SceneView.h"
+#include "Strings.h"
+#include "StringLoader.h"
+#include "StudioApp.h"
+#include "TimelineControl.h"
+#include "UICOptions.h"
+#include "UICColor.h"
+
+#include "Doc.h"
+#include "IKeyframesManager.h"
+#include "Dispatch.h"
+#include "Dialogs.h"
+#include "StudioPreferencesPropSheet.h"
+#include "StudioProjectSettings.h"
+#include "StudioPreferences.h"
+#include "Views.h"
+#include "HotKeys.h"
+#include "RecentItems.h"
+#include "PaletteManager.h"
+#include "Core.h"
+#include "ITickTock.h"
+#include "IStudioRenderer.h"
+#include "SubPresentationsDlg.h"
+#include "StudioTutorialWidget.h"
+#include "remoteproject.h"
+
+#include "InspectorControlView.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qdesktopservices.h>
+#include <QtWidgets/qdockwidget.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdir.h>
+
+// Constants
+const long PLAYBACK_TIMER_TIMEOUT = 10; // 10 milliseconds
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ON_MESSAGE(WM_STUDIO_MESSAGE_ROUTER, OnMsgRouterMsg)
+#endif
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CMainFrame::CMainFrame()
+ : m_ui(new Ui::MainFrame)
+{
+ m_ui->setupUi(this);
+
+ OnCreate();
+
+ g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this);
+ g_StudioApp.GetCore()->GetDispatch()->AddFileOpenListener(this);
+ g_StudioApp.GetCore()->GetDispatch()->AddClientPlayChangeListener(this);
+ g_StudioApp.SetupTimer(WM_STUDIO_TIMER, this);
+
+ m_remoteProject = new RemoteProject(this);
+
+ // File Menu
+ connect(m_ui->action_Open, &QAction::triggered, this, &CMainFrame::OnFileOpen);
+ connect(m_ui->action_Save, &QAction::triggered, this, &CMainFrame::OnFileSave);
+ connect(m_ui->action_New, &QAction::triggered, this, &CMainFrame::OnFileNew);
+ connect(m_ui->actionSave_As, &QAction::triggered, this, &CMainFrame::OnFileSaveAs);
+ connect(m_ui->actionSave_a_Copy, &QAction::triggered, this, &CMainFrame::OnFileSaveCopy);
+ connect(m_ui->action_Revert, &QAction::triggered, this, &CMainFrame::OnFileRevert);
+ connect(m_ui->action_Connect_to_Device, &QAction::triggered, this,
+ &CMainFrame::OnFileConnectToDevice);
+ connect(m_remoteProject, &RemoteProject::connectionChanged,
+ m_ui->action_Connect_to_Device, &QAction::setChecked);
+ connect(m_ui->action_Exit, &QAction::triggered, this, &CMainFrame::close);
+
+ m_RecentItems = new CRecentItems(m_ui->menuRecent_Projects, 0);
+ connect(m_RecentItems, &CRecentItems::openRecent, this, &CMainFrame::OnFileOpenRecent);
+ //ON_COMMAND_RANGE(WM_STUDIO_OPEN_RECENT_MIN, WM_STUDIO_OPEN_RECENT_MAX, OnFileOpenRecent)
+
+ // Edit Menu
+ connect(m_ui->action_Undo, &QAction::triggered, this, &CMainFrame::OnEditUndo);
+ connect(m_ui->action_Redo, &QAction::triggered, this, &CMainFrame::OnEditRedo);
+ connect(m_ui->action_Copy, &QAction::triggered, this, &CMainFrame::OnEditCopy);
+ connect(m_ui->action_Cut, &QAction::triggered, this, &CMainFrame::OnEditCut);
+ connect(m_ui->actionAutoset_Keyframes, &QAction::triggered, this, &CMainFrame::OnToolAutosetkeys);
+ connect(m_ui->action_Paste, &QAction::triggered, this, &CMainFrame::OnEditPaste);
+ connect(m_ui->actionStudio_Preferences, &QAction::triggered, this, &CMainFrame::OnEditApplicationPreferences);
+ connect(m_ui->actionPresentation_Settings, &QAction::triggered, this, &CMainFrame::OnEditPresentationPreferences);
+ connect(m_ui->actionSubpresentations, &QAction::triggered, this,
+ &CMainFrame::OnEditSubPresentations);
+ connect(m_ui->action_Duplicate_Object, &QAction::triggered, this, &CMainFrame::OnEditDuplicate);
+
+ // Timeline Menu
+ connect(m_ui->actionDelete_Selected_Keyframe_s, &QAction::triggered, this, &CMainFrame::OnTimelineDeleteSelectedKeyframes);
+ connect(m_ui->actionSet_Interpolation, &QAction::triggered, this, &CMainFrame::OnTimelineSetInterpolation);
+ connect(m_ui->actionChange_Time_Bar_Color, &QAction::triggered, this, &CMainFrame::OnTimelineSetTimeBarColor);
+ connect(m_ui->actionSet_Changed_Keyframes, &QAction::triggered, this, &CMainFrame::OnTimelineSetChangedKeyframe);
+
+ // View Menu
+ connect(m_ui->actionBounding_Boxes, &QAction::triggered, this, &CMainFrame::OnViewBoundingBoxes);
+ connect(m_ui->actionPivot_Point, &QAction::triggered, this, &CMainFrame::OnViewPivotPoint);
+ connect(m_ui->actionWireframe, &QAction::triggered, this, &CMainFrame::OnViewWireframe);
+ connect(m_ui->actionTooltips, &QAction::triggered, this, &CMainFrame::OnViewTooltips);
+
+ // Tools Menu
+ connect(m_ui->actionTimeline, &QAction::triggered, this, &CMainFrame::OnViewTimeline);
+ connect(m_ui->actionAction, &QAction::triggered, this, &CMainFrame::OnViewAction);
+ connect(m_ui->actionBasic_Objects, &QAction::triggered, this, &CMainFrame::OnViewBasicObjects);
+ connect(m_ui->actionProject, &QAction::triggered, this, &CMainFrame::OnViewProject);
+ connect(m_ui->actionSlide, &QAction::triggered, this, &CMainFrame::OnViewSlide);
+ connect(m_ui->actionInspector, &QAction::triggered, this, &CMainFrame::OnViewInspector);
+
+ // Help Menu
+ connect(m_ui->action_Reference_Manual, &QAction::triggered, this, &CMainFrame::OnHelpIndex);
+ connect(m_ui->action_Visit_Qt_Web_Site, &QAction::triggered, this, &CMainFrame::OnHelpVisitQt);
+ connect(m_ui->action_About_Qt_3D_Studio, &QAction::triggered, []() { g_StudioApp.OnAppAbout(); });
+ connect(m_ui->action_Open_Tutorial, &QAction::triggered, this, &CMainFrame::OnHelpOpenTutorial);
+
+
+ connect(m_ui->actionItem_Select_Tool, &QAction::triggered, m_SceneView, &CSceneView::OnToolItemSelection);
+ connect(m_ui->actionGroup_Select_Tool, &QAction::triggered, m_SceneView, &CSceneView::OnToolGroupSelection);
+
+ // Playback toolbar
+ connect(m_ui->actionPlay, &QAction::triggered, this, &CMainFrame::OnPlaybackPlay);
+ connect(m_ui->actionRewind, &QAction::triggered, this, &CMainFrame::OnPlaybackRewind);
+ connect(m_ui->actionStop, &QAction::triggered, this, &CMainFrame::OnPlaybackStop);
+ connect(m_ui->actionPreview, &QAction::triggered, this, &CMainFrame::OnPlaybackPreview);
+
+ // Tool mode toolbar
+ connect(m_ui->actionPosition_Tool, &QAction::triggered, this, &CMainFrame::OnToolMove);
+ connect(m_ui->actionRotation_Tool, &QAction::triggered, this, &CMainFrame::OnToolRotate);
+ connect(m_ui->actionScale_Tool, &QAction::triggered, this, &CMainFrame::OnToolScale);
+ connect(m_ui->actionLocal_Global_Manipulators, &QAction::triggered, this, &CMainFrame::OnToolGlobalManipulators);
+
+ // Edit Camera toolbar
+ connect(m_ui->actionFit_Selected, &QAction::triggered, this, &CMainFrame::OnEditCameraZoomExtent);
+ connect(m_ui->actionPan_Tool, &QAction::triggered, this, &CMainFrame::OnEditCameraPan);
+ connect(m_ui->actionOrbit_Tool, &QAction::triggered, this, &CMainFrame::OnEditCameraRotate);
+ connect(m_ui->actionZoom_Tool, &QAction::triggered, this, &CMainFrame::OnEditCameraZoom);
+ connect(m_ui->actionShading_Mode, &QAction::triggered, this, &CMainFrame::OnEditViewFillMode);
+ connect(m_ui->actionRulers_Guides, &QAction::triggered, this, &CMainFrame::OnViewGuidesRulers);
+ connect(m_ui->actionClear_Guides, &QAction::triggered, this, &CMainFrame::OnClearGuides);
+ connect(m_ui->actionLock_Guides, &QAction::triggered, this, &CMainFrame::OnLockGuides);
+
+ // TODO: better solution?
+ QTimer* updateUITimer = new QTimer;
+ updateUITimer->start(500);
+ connect(updateUITimer, &QTimer::timeout, [&]() {
+ if (QApplication::activeWindow() != this)
+ return;
+
+ OnUpdateFileSave();
+ OnUpdateEditUndo();
+ OnUpdateEditRedo();
+ OnUpdateEditCopy();
+ OnUpdateEditCut();
+ OnUpdateToolAutosetkeys();
+ OnUpdateEditPaste();
+ OnUpdateEditDuplicate();
+ OnUpdateTimelineDeleteSelectedKeyframes();
+ OnUpdateTimelineSetInterpolation();
+ OnUpdateTimelineSetTimeBarColor();
+ OnUpdateViewBoundingBoxes();
+ OnUpdateViewPivotPoint();
+ OnUpdateViewWireframe();
+ OnUpdateViewTooltips();
+ OnUpdateViewTimeline();
+ OnUpdateViewInspector();
+ OnUpdateViewAction();
+ OnUpdateViewBasicObjects();
+ OnUpdateViewProject();
+ OnUpdateViewSlide();
+ OnUpdateHelpIndex();
+ OnUpdatePlaybackPlay();
+ OnUpdatePlaybackRewind();
+ OnUpdatePlaybackStop();
+ OnUpdatePlaybackPreview();
+ OnUpdateToolMove();
+ OnUpdateToolRotate();
+ OnUpdateToolScale();
+ OnUpdateToolGlobalManipulators();
+ OnUpdateToolGroupSelection();
+ OnUpdateToolItemSelection();
+ OnUpdateCameraZoomExtentAndAuthorZoom();
+ OnUpdateEditCameraPan();
+ OnUpdateEditCameraRotate();
+ OnUpdateEditCameraZoom();
+ OnUpdateEditViewFillMode();
+ OnUpdateViewGuidesRulers();
+ OnUpdateClearGuides();
+ OnUpdateLockGuides();
+ });
+
+ m_playbackTimer.setInterval(PLAYBACK_TIMER_TIMEOUT);
+ connect(&m_playbackTimer, &QTimer::timeout, this, &CMainFrame::onPlaybackTimeout);
+ qApp->installEventFilter(this);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CMainFrame::~CMainFrame()
+{
+}
+
+//==============================================================================
+/**
+ * Timer callback
+ */
+void CMainFrame::onPlaybackTimeout()
+{
+ // Timer callback that drives playback
+ Q_ASSERT(&g_StudioApp);
+ g_StudioApp.GetCore()->GetDoc()->ClientStep();
+}
+
+//==============================================================================
+
+void CMainFrame::showEvent(QShowEvent *event)
+{
+ QMainWindow::showEvent(event);
+
+ QSettings settings;
+ restoreGeometry(settings.value("mainWindowGeometry").toByteArray());
+ restoreState(settings.value("mainWindowState").toByteArray());
+}
+
+/**
+ * Called when the main frame is actually created. Sets up tool bars and default
+ * views.
+ */
+int CMainFrame::OnCreate()
+{
+ m_SceneView = new CSceneView(&g_StudioApp, this);
+ connect(m_SceneView, &CSceneView::toolChanged, this, &CMainFrame::OnUpdateToolChange);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ // tell the edit camera bar about this scene view
+ m_ui->m_EditCamerasBar->SetSceneView(m_SceneView);
+#endif
+
+ // Newly launched, the file dialog for open and import should default to more recent
+ // opened/imported
+ CDialogs *theDialogs = g_StudioApp.GetDialogs();
+ // this must NOT be in 'command line' mode
+ if (theDialogs) {
+ Q3DStudio::CString theMostRecentOpen;
+ if (m_RecentItems && m_RecentItems->GetItemCount() > 0)
+ theMostRecentOpen = m_RecentItems->GetItem(0).GetPath();
+ if (theMostRecentOpen.IsEmpty()) // default to exe
+ theMostRecentOpen = CUICFile::GetApplicationDirectory().GetPath();
+
+ theDialogs->ResetSettings(theMostRecentOpen);
+ }
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+ // Change the background color for the menu bar
+ if (IsMenu(theMenu)) {
+ MENUINFO theMenuInfo = { 0 };
+ theMenuInfo.cbSize = sizeof(MENUINFO);
+ theMenuInfo.fMask = MIM_BACKGROUND;
+
+ CBrush *theNewBrush = new CBrush();
+ CColor theBaseColor(CStudioPreferences::GetMenuBarBaseColor());
+ theNewBrush->CreateSolidBrush(
+ RGB(theBaseColor.GetRed(), theBaseColor.GetGreen(), theBaseColor.GetBlue()));
+ theMenuInfo.hbrBack = *theNewBrush; // Brush you want to draw
+
+ SetMenuInfo(theMenu, &theMenuInfo);
+ }
+ #endif
+
+ // Create the view manager
+ m_PaletteManager = new CPaletteManager(this);
+
+ // Remove basic toolbar (open, save, undo/redo, etc.)
+ // Kept in ui form in case it is going to be added back later on.
+ delete m_ui->toolBar;
+
+ setCentralWidget(m_SceneView);
+ return 0;
+}
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+// If we want things to look good then on the *first* paint we want to actually
+// paint over the background. I don't know why, but if you were to paint
+// the entire background on every message you would get some flashing.
+void CMainFrame::OnPaint()
+{
+ static bool needsPaint = true;
+ if (needsPaint) {
+ needsPaint = false;
+ RECT theRect;
+ ::GetClientRect(GetSafeHwnd(), &theRect);
+ CBrush theBrush;
+ ::CColor theColor = CStudioPreferences::GetBaseColor();
+ DWORD theColorRef = RGB(theColor.GetRed(), theColor.GetGreen(), theColor.GetBlue());
+ theBrush.CreateSolidBrush(theColor);
+ CPaintDC dc(this);
+ dc.FillRect(&theRect, &theBrush);
+ }
+ CFrameWnd::OnPaint();
+}
+//==============================================================================
+void CMainFrame::OnInitMenu(CMenu *inMenu)
+{
+ ReleaseFocus();
+ // Emulate a Control ModifierUp on the Sceneview to "unstick" the Alt-Key/Scale Tool
+ m_SceneView->HandleModifierUp(CHotKeys::KEY_MENU, 0, 0);
+ CFrameWnd::OnInitMenu(inMenu);
+}
+
+::CString CMainFrame::m_WindowClass;
+
+::CString CMainFrame::GetWindowClass()
+{
+ if (m_WindowClass.GetLength() == 0) {
+ ::CColor theColor = CStudioPreferences::GetBaseColor();
+ DWORD theRefColor = RGB(theColor.GetRed(), theColor.GetGreen(), theColor.GetBlue());
+ HBRUSH theBrush = ::CreateSolidBrush(theRefColor);
+ m_WindowClass = AfxRegisterWndClass(0, 0, theBrush, 0);
+ }
+ return m_WindowClass;
+}
+
+BOOL CMainFrame::PreCreateWindow(CREATESTRUCT &cs)
+{
+ if (!CFrameWnd::PreCreateWindow(cs))
+ return FALSE;
+
+ // TODO: Modify the Window class or styles here by modifying
+ cs.lpszClass = GetWindowClass();
+ return TRUE;
+}
+
+BOOL CMainFrame::DestroyWindow()
+{
+ // Save before we destroy anything
+ SaveLayout();
+
+ SAFE_DELETE(m_PaletteManager);
+
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+ g_StudioApp.GetCore()->GetDispatch()->RemoveFileOpenListener(this);
+ g_StudioApp.GetCore()->GetDispatch()->RemoveClientPlayChangeListener(this);
+
+ SAFE_DELETE(m_RecentItems);
+
+ // In order to help debug why image lists sometimes crash on shutdown
+ // This will help identify whcih cone is causing the problem
+ m_ClientToolsImageList.DeleteImageList();
+ m_ClientToolsImageListHot.DeleteImageList();
+ m_ClientToolsImageListDisabled.DeleteImageList();
+ m_PlaybackImageList.DeleteImageList();
+ m_PlaybackImageListHot.DeleteImageList();
+ m_PlaybackImageListDisabled.DeleteImageList();
+ m_EditCameraToolsImageList.DeleteImageList();
+ m_EditCameraToolsImageListHot.DeleteImageList();
+ m_EditCameraToolsImageListDisabled.DeleteImageList();
+
+ return CFrameWnd::DestroyWindow();
+}
+#endif
+
+//==============================================================================
+/**
+ * Called when a new presenation is created. We have to wait to associate the
+ * scene object with the scene view until this point, because the scene object
+ * does not exist until this function gets called.
+ */
+void CMainFrame::OnNewPresentation()
+{
+ // TODO - CN needs to fix this
+ // Associate the scene object with the scene view
+ m_ui->m_EditCamerasBar->SetupCameras();
+}
+
+//==============================================================================
+/**
+ * Called when the current presentation is being closed.
+ * This will close all the editor windows that are open.
+ */
+void CMainFrame::OnClosingPresentation()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the Timeline | Set Interpolation menu item
+ * This is a temporary method that will display the Set Interpolation dialog.
+ */
+void CMainFrame::OnTimelineSetInterpolation()
+{
+ g_StudioApp.GetCore()->GetDoc()->SetKeyframeInterpolation();
+}
+
+//==============================================================================
+/**
+ * Store all the information regarding the layout of the main frame and its palettes.
+ * This is used to save the current position, so when the app is restarted the
+ * window will come back to it's previous location.
+ * On one hand, there is the main frame which continues to use the original
+ * CPaletteState scheme. Now there is also the control bars/palettes that dock.
+ * These all now use the MFC path for serialization to the registry.
+ * This operation used to occur every time there was a change to any window, now
+ * the final state is only saved when the app is closed.
+ */
+void CMainFrame::SaveLayout()
+{
+ if (m_PaletteManager)
+ m_PaletteManager->Save();
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Only save the window position if we're not minimized
+ if (!IsIconic()) {
+ CPaletteState theState("MainWindow");
+
+ theState.SetMaximized(IsZoomed() == TRUE);
+ if (!IsZoomed()) {
+ /*
+ SDJ: 12/17/03 - From MSDN
+ The coordinates used in a WINDOWPLACEMENT structure should be used only by the
+ GetWindowPlacement and SetWindowPlacement functions. Passing coordinates
+ to functions which expect screen coordinates (such as SetWindowPos) will result
+ in the window appearing in the wrong location. For example, if the taskbar is at
+ the top of the screen, saving window coordinates using GetWindowPlacement and
+ restoring them using SetWindowPos causes the window to appear to "creep" up the screen.
+
+ We were using GetWindowPlacement with MoveWindow (in SetLayout) which was causing
+ "creep" when the taskbar was at the top of the screen. Replaced GetWindowPlacement
+ with the following GetWindowRect call.
+ */
+
+ CRect theRect;
+ GetWindowRect(&theRect);
+
+ theState.SetPosition(CPt(theRect.left, theRect.top));
+ theState.SetSize(CPt(theRect.right - theRect.left, theRect.bottom - theRect.top));
+ }
+ theState.SetVisible(IsWindowVisible() ? true : false);
+
+ theState.SaveState();
+
+ // Force a recalc of the layout to ensure that everything is correct with the docking
+ RecalcLayout(TRUE);
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Restore all the information regarding the layout of the main frame and its palettes.
+ * This will make the window be located at the last stored location.
+ * On one hand, there is the main frame which continues to use the original
+ * CPaletteState scheme. Now there is also the control bars/palettes that dock.
+ * These all now use the MFC path for serialization to the registry. This happens
+ * on startup.
+ */
+void CMainFrame::RestoreLayout()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CPaletteState theState("MainWindow");
+ theState.LoadState();
+
+ SetLayout(theState);
+
+ if (!m_PaletteManager->Load())
+ m_PaletteManager->RestoreDefaults(true);
+
+ // Force a recalc for the layout for the docking palettes to work
+ RecalcLayout(TRUE);
+#endif
+}
+
+//==============================================================================
+/**
+ * Set the state of this window to the specified state.
+ */
+void CMainFrame::SetLayout(const CPaletteState &inState)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CPt thePosition = inState.GetPosition();
+ CPt theSize = inState.GetSize();
+ bool isVisible = inState.IsVisible();
+
+ CRect theWindowRect(thePosition.x, thePosition.y, thePosition.x + theSize.x,
+ thePosition.y + theSize.y);
+
+ // Relocate the window to the new location
+ MoveWindow(theWindowRect);
+
+ if (inState.IsMaximized())
+ ::AfxGetApp()->m_nCmdShow = SW_SHOWMAXIMIZED;
+ ShowWindow(::AfxGetApp()->m_nCmdShow);
+ ShowWindow((isVisible ? SW_SHOW : SW_HIDE));
+#endif
+ // UpdateWindow();
+}
+
+//==============================================================================
+/**
+ * OnEditRedo: calls handleRedoOperation
+ */
+//==============================================================================
+void CMainFrame::OnEditRedo()
+{
+ g_StudioApp.GetCore()->GetCmdStack()->Redo();
+}
+
+//==============================================================================
+/**
+ * OnEditUndo: calls HandleUndoOperation
+ */
+//==============================================================================
+void CMainFrame::OnEditUndo()
+{
+ g_StudioApp.GetCore()->GetCmdStack()->Undo();
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditUndo: Handler for ID_EDIT_UNDO message
+ *
+ * @param pCmndUI The UI element that generated the message
+ */
+void CMainFrame::OnUpdateEditUndo()
+{
+ const QString undoDescription = QObject::tr("Undo %1\tCtrl+Z").arg(g_StudioApp.GetCore()->GetCmdStack()->GetUndoDescription());
+ m_ui->action_Undo->setEnabled(g_StudioApp.CanUndo());
+ m_ui->action_Undo->setText(undoDescription);
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditRedo: handles the message ID_EDIT_REDO
+ *
+ * @param pCmndUI The UI element that generated the message
+ */
+void CMainFrame::OnUpdateEditRedo()
+{
+ const QString redoDescription = QObject::tr("Redo %1\tCtrl+Y").arg(g_StudioApp.GetCore()->GetCmdStack()->GetRedoDescription());
+ m_ui->action_Redo->setEnabled(g_StudioApp.CanRedo());
+ m_ui->action_Redo->setText(redoDescription);
+}
+
+//==============================================================================
+/**
+ * OnEditCopy: Handles the Copy message
+ *
+ * Tells the doc to copy the selected keyframes.
+ */
+void CMainFrame::OnEditCopy()
+{
+ g_StudioApp.OnCopy();
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditCopy: Handle the update UI command for the copy button and menu item
+ *
+ * If there are keyframes selected, the button is enabled, otherwise, it is
+ * disabled.
+ *
+ * @param pCmndUI The UI element that generated the message
+ */
+void CMainFrame::OnUpdateEditCopy()
+{
+ if (g_StudioApp.CanCopy()) {
+ Q3DStudio::CString theDescription;
+ theDescription.Format(::LoadResourceString(IDS_MENU_WIN_COPY_FORMAT),
+ static_cast<const wchar_t *>(g_StudioApp.GetCopyType()));
+
+ m_ui->action_Copy->setText(theDescription.toQString());
+ m_ui->action_Copy->setEnabled(true);
+ } else {
+ m_ui->action_Copy->setEnabled(false);
+ }
+}
+
+//==============================================================================
+/**
+ * OnEditCut: Handles the Cut message
+ *
+ * Tells the doc to cut the selected keyframes.
+ */
+void CMainFrame::OnEditCut()
+{
+ g_StudioApp.OnCut();
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditCut: Handle the update UI command for the cut button and menu item
+ *
+ * If there are keyframes selected, the button is enabled, otherwise, it is
+ * disabled.
+ *
+ * @param pCmndUI The UI element that generated the message
+ */
+void CMainFrame::OnUpdateEditCut()
+{
+ if (g_StudioApp.CanCut()) {
+ Q3DStudio::CString theDescription;
+ theDescription.Format(::LoadResourceString(IDS_MENU_WIN_CUT_FORMAT),
+ static_cast<const wchar_t *>(g_StudioApp.GetCopyType()));
+
+ m_ui->action_Cut->setText(theDescription.toQString());
+ m_ui->action_Cut->setEnabled(true);
+ } else {
+ m_ui->action_Cut->setEnabled(false);
+ }
+}
+
+//==============================================================================
+/**
+ * OnEditPaste: Handles the Paste command
+ *
+ * Tells the doc to paste the copied keyframes at the current playhead time, on
+ * the currently selected object.
+ */
+void CMainFrame::OnEditPaste()
+{
+ g_StudioApp.OnPaste();
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditPaste: Handle the update UI command for the paste button and menu item
+ *
+ * If there we can perform a keyframe paste, the button is enabled, otherwise, it is
+ * disabled.
+ *
+ * @param pCmndUI The UI element that generated the message
+ */
+void CMainFrame::OnUpdateEditPaste()
+{
+ if (g_StudioApp.CanPaste()) {
+ Q3DStudio::CString theUndoDescription;
+ theUndoDescription.Format(::LoadResourceString(IDS_MENU_WIN_PASTE_FORMAT),
+ static_cast<const wchar_t *>(g_StudioApp.GetPasteType()));
+
+ m_ui->action_Paste->setText(theUndoDescription.toQString());
+
+ m_ui->action_Paste->setEnabled(true);
+ } else {
+ m_ui->action_Paste->setEnabled(false);
+ }
+}
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+//=============================================================================
+/**
+ * Router for forwarding messages onto the MsgRouter.
+ * This is used for posting messages between threads.
+ */
+LRESULT CMainFrame::OnMsgRouterMsg(WPARAM inWParam, LPARAM inLParam)
+{
+ CMsgRouter::GetInstance()->HandleMessage(inWParam, inLParam);
+
+ return TRUE;
+}
+#endif
+
+//=============================================================================
+/**
+ * Called when a tool mode changes from a modifier key
+ */
+void CMainFrame::OnUpdateToolChange()
+{
+ long theSelectMode = g_StudioApp.GetSelectMode();
+ m_ui->actionGroup_Select_Tool->setChecked(theSelectMode == STUDIO_SELECTMODE_GROUP);
+ m_ui->actionItem_Select_Tool->setChecked(theSelectMode == STUDIO_SELECTMODE_ENTITY);
+
+ // See what tool mode we are in and change checks accordingly
+ long theToolMode = g_StudioApp.GetToolMode();
+ m_ui->actionPosition_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_MOVE);
+ m_ui->actionRotation_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_ROTATE);
+ m_ui->actionScale_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_SCALE);
+ m_ui->actionLocal_Global_Manipulators->setChecked(g_StudioApp.GetMinpulationMode()
+ == StudioManipulationModes::Global);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ m_ui->actionPan_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_CAMERA_PAN);
+ m_ui->actionOrbit_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_CAMERA_ROTATE);
+ m_ui->actionZoom_Tool->setChecked(theToolMode == STUDIO_TOOLMODE_CAMERA_ZOOM);
+
+/*CEditCameraContainer* theEditCameras = g_StudioApp.GetCore()->GetDoc()->GetEditCameraContainer( );
+CSEditCamera* theActiveEditCamera = theEditCameras->GetActiveEditCamera( );
+bool theIsEditView = !m_SceneView->IsDeploymentView( );
+m_EditCamerasBar.GetToolBarCtrl( ).EnableButton( ID_TOOL_EDITCAMERA_ROTATE, ( theActiveEditCamera &&
+theActiveEditCamera->Is3D( ) ) );
+m_EditCamerasBar.GetToolBarCtrl( ).EnableButton( ID_TOOL_EDITCAMERA_PAN, theIsEditView );
+m_EditCamerasBar.GetToolBarCtrl( ).EnableButton( ID_TOOL_EDITCAMERA_ZOOM, theIsEditView );
+m_EditCamerasBar.GetToolBarCtrl( ).EnableButton( ID_EDITCAMERA_ZOOMEXTENT, theIsEditView );
+
+m_EditCamerasBar.GetToolBarCtrl( ).EnableButton( ID_TOOL_EDITVIEW_FILLMODE, theIsEditView );
+m_EditCamerasBar.GetToolBarCtrl( ).CheckButton( ID_TOOL_EDITVIEW_FILLMODE, theIsEditView &&
+theEditCameras->GetEditViewFillMode( ) );
+*/
+#endif
+}
+
+//==============================================================================
+/**
+ * OnTimelineSettimebarcolor: Handles the ID_TIMELINE_SETTIMEBARCOLOR message.
+ *
+ * Called when the user clicks on Timeline->Change Time Bar Color. Changes
+ * the currently selected timebar's color.
+ */
+void CMainFrame::OnTimelineSetTimeBarColor()
+{
+ ITimelineTimebar *theTimelineTimebar = GetSelectedTimelineTimebar();
+ if (theTimelineTimebar != NULL) {
+ CColor theColor = theTimelineTimebar->GetTimebarColor();
+
+ if (g_StudioApp.GetDialogs()->PromptObjectTimebarColor(theColor))
+ theTimelineTimebar->SetTimebarColor(theColor);
+ }
+}
+
+//==============================================================================
+/**
+ * OnUpdateTimelineSetTimeBarColor: Handles the update UI message for the
+ * "Change Time Bar Color" menu item.
+ *
+ * If the currently selected object is an item in the timeline and it has a
+ * time bar, this menu item is enabled. Otherwise, the menu item is disabled.
+ *
+ * @param pCmndUI Pointer to the ui object that generated this update message.
+ */
+void CMainFrame::OnUpdateTimelineSetTimeBarColor()
+{
+ m_ui->actionChange_Time_Bar_Color->setEnabled(g_StudioApp.CanChangeTimebarColor());
+}
+
+//==============================================================================
+/**
+ * OnTimelineSetChangedKeyframe: Handles the ID_TIMELINE_SETCHANGEDKEYFRAME message.
+ *
+ * Calls the StudioDoc handler to insert keyframes for animatable properties that
+ * have changed.
+ */
+void CMainFrame::OnTimelineSetChangedKeyframe()
+{
+ g_StudioApp.HandleSetChangedKeys();
+}
+
+//==============================================================================
+/**
+ * OnTimelineDeleteSelectedKeyframes: Handles the ID_TIMELINE_DELETESELECTEDKEYFRAMES
+ * message.
+ *
+ * Deletes all currently selected keyframes.
+ */
+void CMainFrame::OnTimelineDeleteSelectedKeyframes()
+{
+ g_StudioApp.DeleteSelectedKeys();
+}
+
+//==============================================================================
+/**
+ * OnUpdateTimelineDeleteSelectedKeyframes: Handles the update UI message for
+ * the "Delete Selected Keyframe(s)" message.
+ *
+ * If there are currently keyframes selected, the menu item is enabled. Otherwise,
+ * the menu item is disabled.
+ *
+ * @param pCmdUI The UI element that generated this message
+ */
+void CMainFrame::OnUpdateTimelineDeleteSelectedKeyframes()
+{
+ m_ui->actionDelete_Selected_Keyframe_s->setEnabled(g_StudioApp.CanCopy());
+}
+
+//==============================================================================
+/**
+ * OnUpdateTimelineSetInterpolation: Handles the update UI message for
+ * the "Set Interpolation" message.
+ *
+ * If there are currently keyframes selected, this menu item is enabled, otherwise
+ * it is disabled.
+ *
+ * @param pCmdUI The UI element that generated this message
+ */
+void CMainFrame::OnUpdateTimelineSetInterpolation()
+{
+ m_ui->actionSet_Interpolation->setEnabled(g_StudioApp.GetCore()->GetDoc()->GetKeyframesManager()->HasSelectedKeyframes());
+}
+
+//==============================================================================
+/**
+ * OnEditDuplicate: Handles the ID_EDIT_DUPLICATE message.
+ *
+ * Pass through to the doc.
+ */
+void CMainFrame::OnEditDuplicate()
+{
+ g_StudioApp.HandleDuplicateCommand();
+}
+
+//==============================================================================
+/**
+ * OnUpdateEditDuplicate: Handles the UPDATE COMMAND UI message for this menu item.
+ *
+ * If the currently selected object is not null, and it is not in the library,
+ * then the user can duplicate the object and the menu item is enabled. Otherwise,
+ * it is disabled.
+ */
+void CMainFrame::OnUpdateEditDuplicate()
+{
+ m_ui->action_Duplicate_Object->setEnabled(g_StudioApp.CanDuplicateObject());
+}
+
+//=============================================================================
+/**
+ * Command handler for the File Open menu and toolbar options.
+ * This will save the file, if the file has not been saved before this will
+ * do a save as operation.
+ */
+void CMainFrame::OnFileOpen()
+{
+ g_StudioApp.OnFileOpen();
+}
+
+//=============================================================================
+/**
+ * Command handler for the File Save menu and toolbar options.
+ * This will save the file, if the file has not been saved before this will
+ * do a save as operation.
+ */
+void CMainFrame::OnFileSave()
+{
+ g_StudioApp.OnSave();
+}
+
+void CMainFrame::OnUpdateFileSave()
+{
+ m_ui->action_Save->setEnabled(g_StudioApp.GetCore()->GetDoc()->IsModified());
+}
+//=============================================================================
+/**
+ * Command handler for the File Save As menu option.
+ * This will prompt the user for a location to save the file out to then
+ * will perform the save.
+ */
+void CMainFrame::OnFileSaveAs()
+{
+ g_StudioApp.OnSaveAs();
+}
+
+//=============================================================================
+/**
+ * Command handler for the File Save a Copy menu option.
+ * This will prompt the user for a location to save the file out to then
+ * save a copy, leaving the original file open in the editor.
+ */
+void CMainFrame::OnFileSaveCopy()
+{
+ g_StudioApp.OnSaveCopy();
+}
+
+//=============================================================================
+/**
+ * Command handler for the New Document menu option.
+ * This will create a new default document.
+ */
+void CMainFrame::OnFileNew()
+{
+ g_StudioApp.OnFileNew();
+}
+
+
+//==============================================================================
+/**
+ * Overrides the close method to prompt if the document is modified.
+ */
+void CMainFrame::closeEvent(QCloseEvent *event)
+{
+ QSettings settings;
+ settings.setValue("mainWindowGeometry", saveGeometry());
+ settings.setValue("mainWindowState", saveState());
+ QMainWindow::closeEvent(event);
+
+ if (g_StudioApp.GetCore()->GetDoc()->IsModified()) {
+ CDialogs::ESavePromptResult theResult = g_StudioApp.GetDialogs()->PromptForSave();
+ if (theResult == CDialogs::SAVE_FIRST) {
+ // If the save was canceled or failed then do not exit.
+ if (!g_StudioApp.OnSave())
+ return;
+ }
+ // On cancel ditch out of here and abort exit. Abort! Abort!
+ else if (theResult == CDialogs::CANCEL_OPERATION) {
+ event->ignore();
+ return;
+ }
+ }
+
+ // Tell the app to shutdown, do it here so it does not rely on static destructor.
+ g_StudioApp.PerformShutdown();
+}
+
+//==============================================================================
+/**
+ * Displays the preferences dialog and can change program settings.
+ */
+void CMainFrame::OnEditApplicationPreferences()
+{
+ EditPreferences(PAGE_STUDIOAPPPREFERENCES);
+}
+
+//==============================================================================
+/**
+ * Displays the preferences dialog and can change program settings.
+ */
+void CMainFrame::OnEditPresentationPreferences()
+{
+ EditPreferences(PAGE_STUDIOPROJECTSETTINGS);
+}
+
+//==============================================================================
+/**
+ * Displays the preferences dialog and can change subpresentation settings.
+ */
+void CMainFrame::OnEditSubPresentations()
+{
+ QString dir = g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory().toQString();
+
+ CSubPresentationsDlg dlg(QDir::toNativeSeparators(dir),
+ g_StudioApp.m_subpresentations);
+ dlg.exec();
+ if (dlg.result() == QDialog::Accepted) {
+ g_StudioApp.m_subpresentations = dlg.subpresentations();
+ g_StudioApp.SaveUIAFile();
+ }
+}
+
+//==============================================================================
+/**
+ * EditPreferences: Displays the presentation settings property sheet with
+ * the specified active page.
+ *
+ * Used for editing the application and project settings.
+ *
+ * @param inPageIndex The page index to select when displayed.
+ */
+void CMainFrame::EditPreferences(short inPageIndex)
+{
+ // Set the active page based on the inPageIndex
+ CStudioPreferencesPropSheet thePropSheet(::LoadResourceString(IDS_STUDIOPREFSTITLE).toQString(),
+ this, inPageIndex);
+
+ // CStudioProjectSettings *theProjectSettings = g_StudioApp.GetCore()->GetStudioProjectSettings();
+
+ // Q3DStudio::CString theAuthorName theAuthorName = theProjectSettings->GetAuthor();
+ // Q3DStudio::CString theCompanyName theCompanyName = theProjectSettings->GetCompany();
+
+ // Display the CStudioPreferencesPropSheet
+ int thePrefsReturn = thePropSheet.exec();
+
+ // Ugly hack to fix some bug
+ m_SceneView->OnEditCameraChanged();
+
+ if (thePrefsReturn == PREFS_RESET_DEFAULTS) {
+ // Restore default values
+ g_StudioApp.SetAutosetKeyframes(true);
+ CStudioPreferences::SetBoundingBoxesOn(true);
+ CStudioPreferences::SetTimebarDisplayTime(true);
+ g_StudioApp.GetCore()->GetDoc()->SetDefaultKeyframeInterpolation(true);
+ CStudioPreferences::SetNudgeAmount(10.0f);
+ CStudioPreferences::SetSnapRange(10);
+ CStudioPreferences::SetTimelineSnappingGridActive(true);
+ CStudioPreferences::SetTimelineSnappingGridResolution(SNAPGRID_SECONDS);
+// Edit Cameras
+#ifdef INCLUDE_EDIT_CAMERA
+ CColor theDefaultBgColor(CStudioPreferences::EDITVIEW_DEFAULTBGCOLOR);
+ CStudioPreferences::SetEditViewBackgroundColor(theDefaultBgColor);
+ // g_StudioApp.GetCore()->GetDoc()->SetEditViewBackgroundColor( theDefaultBgColor );
+ CStudioPreferences::SetPreferredStartupView(
+ CStudioPreferences::PREFERREDSTARTUP_DEFAULTINDEX);
+#endif
+
+ if (m_PaletteManager)
+ m_PaletteManager->RestoreDefaults();
+
+ RecheckSizingMode();
+
+ // Remove keys related to the ObjRef and PropRef Pickers
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("ObjectReferenceWidth");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("ObjectReferenceHeight");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("ObjectReferenceXPos");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("ObjectReferenceYPos");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("PropertyReferenceWidth");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("PropertyReferenceHeight");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("PropertyReferenceXPos");
+ CPreferences::GetUserPreferences(::LoadResourceString(IDS_PREFS_REFERENCECONTROLS))
+ .RemoveKey("PropertyReferenceYPos");
+
+ // Also clear out the viewer's settings file
+ Q3DStudio::CFilePath theFilePath(Q3DStudio::CFilePath::CombineBaseAndRelative(
+ Q3DStudio::CFilePath::GetUserApplicationDirectory(),
+ "UIComposer\\UICViewerSettings.txt"));
+ theFilePath.DeleteThisFile();
+ }
+}
+
+//==============================================================================
+/**
+ * OnToolAutosetkeys: Called when the Autoset Keyframe button is pressed.
+ * Calls the doc to turn off or on the Autoset Keyframe preference.
+ */
+void CMainFrame::OnToolAutosetkeys()
+{
+ // Toggle autoset keyframes to the opposite of what it's currently set as
+ g_StudioApp.SetAutosetKeyframes(!CStudioPreferences::IsAutosetKeyframesOn());
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolAutosetkeys: Updates the UI associated with this button.
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolAutosetkeys()
+{
+ // If autoset keyframes is on
+ m_ui->actionAutoset_Keyframes->setChecked(CStudioPreferences::IsAutosetKeyframesOn());
+}
+
+//==========================================================================
+/**
+ * Called when the presentation is being played in Studio. Updates the play
+ * button on the main frame.
+ */
+void CMainFrame::OnPlayStart()
+{
+ // Update the play button since this doesn't always happen automatically
+ m_ui->actionPlay->setChecked(true);
+
+ if (m_PlaybackFlag == false) {
+ m_PlaybackFlag = true;
+
+ // g_StudioApp.GetCore()->GetDoc()->SetPlayMode( PLAYMODE_PLAY );
+
+ m_playbackTimer.start();
+ }
+}
+
+//==========================================================================
+/**
+ * Called when the presentation stops being played in Studio. Updates the play
+ * button on the main frame.
+ */
+void CMainFrame::OnPlayStop()
+{
+ // Update the play button since this doesn't always happen automatically
+ m_ui->actionPlay->setChecked(false);
+
+ if (m_PlaybackFlag == true) {
+ m_PlaybackFlag = false;
+ m_playbackTimer.stop();
+ // g_StudioApp.GetCore()->GetDoc()->SetPlayMode( PLAYMODE_STOP );
+ }
+}
+
+//==========================================================================
+/**
+ * Called when the presentation time changes. Not handled by this class,
+ * but included because the base class requires it to be implemented.
+ */
+void CMainFrame::OnTimeChanged(long inTime)
+{
+ Q_UNUSED(inTime);
+ // Do nothing
+}
+
+//==============================================================================
+/**
+ * Handles pressing the play button.
+ */
+void CMainFrame::OnPlaybackPlay()
+{
+ g_StudioApp.PlaybackPlay();
+}
+
+//==============================================================================
+/**
+ * Handles pressing of the stop button.
+ */
+void CMainFrame::OnPlaybackStop()
+{
+ g_StudioApp.PlaybackStopNoRestore();
+}
+
+//==============================================================================
+/**
+ * Handles pressing the preview button.
+ */
+void CMainFrame::OnPlaybackPreview()
+{
+ if (m_remoteProject->isConnected())
+ CPreviewHelper::OnDeploy(*m_remoteProject);
+ else
+ CPreviewHelper::OnPreview();
+}
+
+//==============================================================================
+/**
+ * Handles the update ui message for the preview button.
+ * Adding more UI updating here would just be redundant.
+ * @param inCmdUI Pointer to the UI element that needs updating
+ */
+void CMainFrame::OnUpdatePlaybackPreview()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the update ui message for the play button. Does nothing because the
+ * button state is being updated manually in OnPlayStart() and OnPlayStop.
+ * Adding more UI updating here would just be redundant.
+ * @param inCmdUI Pointer to the UI element that needs updating
+ */
+void CMainFrame::OnUpdatePlaybackPlay()
+{
+}
+
+//==============================================================================
+/**
+ * Handles pressing the rewind button.
+ */
+void CMainFrame::OnPlaybackRewind()
+{
+ g_StudioApp.PlaybackRewind();
+}
+
+//==============================================================================
+/**
+ * Handles the update ui message for the rewind button. Does nothing because
+ * no additional ui handling is necessary for this button.
+ * @param inCmdUI Pointer to the UI element that needs updating
+ */
+void CMainFrame::OnUpdatePlaybackRewind()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the update ui message for the stop button. Doesn't do anything
+ * because no special ui handling is necessary for the stop button.
+ * @param inCmdUI Pointer to the UI element that needs updating
+ */
+void CMainFrame::OnUpdatePlaybackStop()
+{
+}
+
+//==============================================================================
+/**
+ * Registers all the keys it will need for shortcuts, also telsl children to register theirs
+ * @param inHotKeys the hotkeys to with which to register
+ */
+void CMainFrame::RegisterGlobalKeyboardShortcuts(CHotKeys *inHotKeys)
+{
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnToolMove),
+ 0, Qt::Key_W);
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnToolRotate),
+ 0, Qt::Key_R);
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnToolScale),
+ 0, Qt::Key_E);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnViewBoundingBoxes),
+ Qt::ControlModifier, Qt::Key_B);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnViewPivotPoint),
+ Qt::ControlModifier | Qt::AltModifier, Qt::Key_P);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnEditPresentationPreferences),
+ Qt::ControlModifier, Qt::Key_P);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnOpenMostRecentlyUsedDocument),
+ Qt::ControlModifier | Qt::AltModifier | Qt::ShiftModifier, Qt::Key_P);
+
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowAction),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_A);
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowBasic),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_B);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowInspector),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_I);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowProject),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_P);
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowSlide),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_D);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnShowTimeline),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_T);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnViewGuidesRulers),
+ Qt::ControlModifier, Qt::Key_Semicolon);
+ inHotKeys->RegisterKeyEvent(new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnLockGuides),
+ Qt::ControlModifier | Qt::AltModifier, Qt::Key_Semicolon);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ inHotKeys->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::HandleEditViewFillModeKey), 0,
+ Qt::Key_F3);
+ inHotKeys->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::HandleEditCameraZoomExtent), 0, Qt::Key_F);
+#endif
+
+ inHotKeys->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CMainFrame>(this, &CMainFrame::OnPlaybackPreview), 0,
+ Qt::Key_F5);
+
+ m_SceneView->RegisterGlobalKeyboardShortcuts(inHotKeys);
+
+ CTimelineControl *theTimelineControl = GetTimelineControl();
+ if (theTimelineControl)
+ theTimelineControl->RegisterGlobalKeyboardShortcuts(inHotKeys);
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolMove: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateToolMove()
+{
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionPosition_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_MOVE && m_ui->actionPosition_Tool->isEnabled());
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolRotate: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolRotate()
+{
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionRotation_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_ROTATE && m_ui->actionRotation_Tool->isEnabled());
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolScale: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolScale()
+{
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionScale_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_SCALE && m_ui->actionScale_Tool->isEnabled());
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolScale: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolGlobalManipulators()
+{
+ StudioManipulationModes::Enum theMode = g_StudioApp.GetMinpulationMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionLocal_Global_Manipulators->setChecked(theMode == StudioManipulationModes::Global && m_ui->actionLocal_Global_Manipulators->isEnabled());
+}
+
+//==============================================================================
+/**
+ * OnToolMove: Called when the Move button is pressed.
+ * Sets the current tool mode and changes the cursor.
+ */
+void CMainFrame::OnToolMove()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_MOVE);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_MOVE);
+}
+
+//==============================================================================
+/**
+ * OnToolRotate: Called when the Rotate button is pressed.
+ * Sets the current tool mode and changes the cursor.
+ */
+void CMainFrame::OnToolRotate()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_ROTATE);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_ROTATE);
+}
+
+//==============================================================================
+/**
+ * OnToolScale: Called when the Scale button is pressed.
+ * Sets the current tool mode and changes the cursor.
+ */
+void CMainFrame::OnToolScale()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_SCALE);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_SCALE);
+}
+
+void CMainFrame::OnToolGlobalManipulators()
+{
+ if (m_ui->actionLocal_Global_Manipulators->isChecked())
+ g_StudioApp.SetMinpulationMode(StudioManipulationModes::Global);
+ else
+ g_StudioApp.SetMinpulationMode(StudioManipulationModes::Local);
+
+ g_StudioApp.GetRenderer().RequestRender();
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolGroupSelection: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolGroupSelection()
+{
+ long theCurrentSelectSettings = g_StudioApp.GetSelectMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionGroup_Select_Tool->setChecked(theCurrentSelectSettings == STUDIO_SELECTMODE_GROUP && m_ui->actionGroup_Select_Tool->isEnabled());
+}
+
+//==============================================================================
+/**
+ * OnUpdateToolItemSelection: Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * tool mode, and whether or not the button is enabled.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+void CMainFrame::OnUpdateToolItemSelection()
+{
+ long theCurrentSelectSettings = g_StudioApp.GetSelectMode();
+
+ // If the current tool mode matches this button
+ // If the button is currently enabled
+ m_ui->actionItem_Select_Tool->setChecked(theCurrentSelectSettings == STUDIO_SELECTMODE_ENTITY && m_ui->actionItem_Select_Tool->isEnabled());
+}
+
+//==============================================================================
+/**
+ * Called when the edit camera Zoom Extent button is pressed.
+ * Inform the current active edit camera to toggle itself.
+ */
+void CMainFrame::OnEditCameraZoomExtent()
+{
+ if (g_StudioApp.GetRenderer().GetEditCamera() >= 0)
+ g_StudioApp.GetRenderer().EditCameraZoomToFit();
+ else
+ g_StudioApp.SetAuthorZoom(!g_StudioApp.IsAuthorZoom());
+}
+
+//==============================================================================
+/**
+ * Called when the "Zoom Extent" keyboard shortcut is pressed.
+ * Inform the current active edit camera to toggle itself.
+ */
+//==============================================================================
+void CMainFrame::HandleEditCameraZoomExtent()
+{
+ OnEditCameraZoomExtent();
+}
+
+//==============================================================================
+/**
+ * Called when the Pan Edit Camera button is pressed.
+ * Inform the current active edit camera on their tool mode.
+ */
+void CMainFrame::OnEditCameraPan()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+}
+
+//==============================================================================
+/**
+ * Called when the Rotate Edit Camera button is pressed.
+ * Inform the current active edit camera on their tool mode.
+ */
+void CMainFrame::OnEditCameraRotate()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+}
+
+//==============================================================================
+/**
+ * Called when the Zoom Edit Camera button is pressed.
+ * Inform the current active edit camera on their tool mode.
+ */
+void CMainFrame::OnEditCameraZoom()
+{
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ZOOM);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_CAMERA_ZOOM);
+}
+
+//==============================================================================
+/**
+ * Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * settings of the current edit camera tool.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateCameraZoomExtentAndAuthorZoom()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()) {
+ m_ui->actionFit_Selected->setChecked(false);
+ } else {
+ m_ui->actionFit_Selected->setChecked(g_StudioApp.IsAuthorZoom());
+ }
+}
+
+//==============================================================================
+/**
+ * Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * settings of the current edit camera tool.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateEditCameraPan()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()) {
+ m_ui->actionPan_Tool->setEnabled(true);
+
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+ m_ui->actionPan_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_CAMERA_PAN);
+ } else {
+ m_ui->actionPan_Tool->setEnabled(false);
+ m_ui->actionPan_Tool->setChecked(false);
+ }
+}
+
+//==============================================================================
+/**
+ * Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * settings of the current edit camera tool.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateEditCameraRotate()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()
+ && g_StudioApp.GetRenderer().DoesEditCameraSupportRotation(
+ g_StudioApp.GetRenderer().GetEditCamera())) {
+ m_ui->actionOrbit_Tool->setEnabled(true);
+
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+ m_ui->actionOrbit_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_CAMERA_ROTATE);
+ } else {
+ m_ui->actionOrbit_Tool->setEnabled(false);
+ m_ui->actionOrbit_Tool->setChecked(false);
+ }
+}
+
+//==============================================================================
+/**
+ * Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * settings of the current edit camera tool.
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateEditCameraZoom()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()) {
+ m_ui->actionZoom_Tool->setEnabled(true);
+
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+ m_ui->actionZoom_Tool->setChecked(theCurrentToolSettings == STUDIO_TOOLMODE_CAMERA_ZOOM);
+ } else {
+ m_ui->actionZoom_Tool->setEnabled(false);
+ m_ui->actionZoom_Tool->setChecked(false);
+ }
+}
+
+//==============================================================================
+/**
+ * Called when the "Render geometry as solid/wireframe in edit view" button is pressed.
+ * Toggle the mode and update the studio preferences, also cache it in EditCameraContainer
+ */
+//==============================================================================
+void CMainFrame::HandleEditViewFillModeKey()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()) {
+ OnEditViewFillMode();
+ bool theEditViewFillMode = g_StudioApp.GetRenderer().IsEditLightEnabled();
+ m_ui->actionShading_Mode->setChecked(theEditViewFillMode);
+ }
+}
+
+//==============================================================================
+/**
+ * Called when the "Render geometry as solid/wireframe in edit view" button is pressed.
+ * Toggle the mode and update the studio preferences, also cache it in EditCameraContainer
+ */
+//==============================================================================
+void CMainFrame::OnEditViewFillMode()
+{
+ bool theEditViewFillMode = !g_StudioApp.GetRenderer().IsEditLightEnabled();
+ g_StudioApp.GetRenderer().SetEnableEditLight(theEditViewFillMode);
+}
+
+//==============================================================================
+/**
+ * Updates the UI associated with this button.
+ *
+ * Checks or unchecks this button on the toolbar, depending on the current
+ * settings of the "Render geometry as solid/wireframe in edit view".
+ *
+ * @param pCmdUI Pointer to the button that generated the message.
+ */
+//==============================================================================
+void CMainFrame::OnUpdateEditViewFillMode()
+{
+ if (m_SceneView == GetActiveView() && !m_SceneView->IsDeploymentView()) {
+ m_ui->actionShading_Mode->setEnabled(true);
+ m_ui->actionShading_Mode->setChecked(g_StudioApp.GetRenderer().IsEditLightEnabled());
+ } else {
+ m_ui->actionShading_Mode->setEnabled(false);
+ m_ui->actionShading_Mode->setChecked(false);
+ }
+}
+
+void CMainFrame::OnViewGuidesRulers()
+{
+ g_StudioApp.GetRenderer().SetGuidesEnabled(!g_StudioApp.GetRenderer().AreGuidesEnabled());
+ g_StudioApp.GetCore()->GetDispatch()->FireAuthorZoomChanged();
+ m_SceneView->OnRulerGuideToggled();
+}
+
+void CMainFrame::OnUpdateViewGuidesRulers()
+{
+ m_ui->actionRulers_Guides->setEnabled(m_SceneView->IsDeploymentView());
+ m_ui->actionRulers_Guides->setChecked(g_StudioApp.GetRenderer().AreGuidesEnabled());
+}
+
+void CMainFrame::OnClearGuides()
+{
+ g_StudioApp.ClearGuides();
+}
+
+void CMainFrame::OnUpdateClearGuides()
+{
+ bool enable = g_StudioApp.GetRenderer().AreGuidesEnabled()
+ && g_StudioApp.GetRenderer().AreGuidesEditable() && m_SceneView->IsDeploymentView();
+
+ m_ui->actionClear_Guides->setEnabled(enable);
+}
+
+void CMainFrame::OnLockGuides()
+{
+ g_StudioApp.GetRenderer().SetGuidesEditable(!g_StudioApp.GetRenderer().AreGuidesEditable());
+}
+
+void CMainFrame::OnUpdateLockGuides()
+{
+ bool enable = g_StudioApp.GetRenderer().AreGuidesEnabled() && m_SceneView->IsDeploymentView();
+ m_ui->actionLock_Guides->setEnabled(enable);
+ // Set to the inverse of guides editable.
+ m_ui->actionLock_Guides->setChecked(!g_StudioApp.GetRenderer().AreGuidesEditable());
+}
+
+void CMainFrame::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == WM_STUDIO_TIMER)
+ g_StudioApp.GetTickTock().ProcessMessages();
+ QMainWindow::timerEvent(event);
+}
+
+void CMainFrame::OnViewAction()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_ACTION);
+ SaveLayout();
+}
+
+void CMainFrame::OnUpdateViewAction()
+{
+ m_ui->actionAction->setChecked(
+ m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_ACTION));
+}
+
+void CMainFrame::OnViewBasicObjects()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_BASICOBJECTS);
+ SaveLayout();
+}
+
+void CMainFrame::OnUpdateViewBasicObjects()
+{
+ m_ui->actionBasic_Objects->setChecked(m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_BASICOBJECTS));
+}
+
+void CMainFrame::OnViewInspector()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_INSPECTOR);
+ SaveLayout();
+}
+
+void CMainFrame::OnUpdateViewInspector()
+{
+ m_ui->actionInspector->setChecked(
+ m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_INSPECTOR));
+}
+
+void CMainFrame::OnViewProject()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_PROJECT);
+ SaveLayout();
+}
+
+void CMainFrame::OnUpdateViewProject()
+{
+ m_ui->actionProject->setChecked(
+ m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_PROJECT));
+}
+
+void CMainFrame::OnViewSlide()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_SLIDE);
+ SaveLayout();
+}
+
+void CMainFrame::OnUpdateViewSlide()
+{
+ m_ui->actionSlide->setChecked(
+ m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_SLIDE) ? TRUE : FALSE);
+}
+
+//==============================================================================
+/**
+ * Called when the View Inspector Palette menu item is chosen.
+ */
+void CMainFrame::OnViewTimeline()
+{
+ m_PaletteManager->ToggleControl(CPaletteManager::CONTROLTYPE_TIMELINE);
+ SaveLayout();
+}
+
+//==============================================================================
+/**
+ * Checks or unchecks the menu item depending on if the view is available or not.
+ * @param pCmdUI Pointer to the UI element that generated the message.
+ */
+void CMainFrame::OnUpdateViewTimeline()
+{
+ m_ui->actionTimeline->setChecked(
+ m_PaletteManager->IsControlVisible(CPaletteManager::CONTROLTYPE_TIMELINE));
+}
+
+//==============================================================================
+/**
+ * Called when the View Inspector Palette menu item is chosen.
+ */
+void CMainFrame::OnViewBoundingBoxes()
+{
+ CStudioPreferences::SetBoundingBoxesOn(!CStudioPreferences::IsBoundingBoxesOn());
+ g_StudioApp.GetRenderer().RequestRender();
+}
+
+//==============================================================================
+/**
+ * Checks or unchecks the menu item depending on if the view is available or not.
+ * @param pCmdUI Pointer to the UI element that generated the message.
+ */
+void CMainFrame::OnUpdateViewBoundingBoxes()
+{
+ m_ui->actionBounding_Boxes->setChecked(CStudioPreferences::IsBoundingBoxesOn());
+}
+
+//==============================================================================
+/**
+ * Called when the View Pivot Point menu item is chosen.
+ */
+void CMainFrame::OnViewPivotPoint()
+{
+ CStudioPreferences::SetDisplayPivotPoint(!CStudioPreferences::ShouldDisplayPivotPoint());
+ g_StudioApp.GetRenderer().RequestRender();
+}
+
+//==============================================================================
+/**
+ * Checks or unchecks the menu item depending on if the view is available or not.
+ * @param pCmdUI Pointer to the UI element that generated the message.
+ */
+void CMainFrame::OnUpdateViewPivotPoint()
+{
+ m_ui->actionPivot_Point->setChecked(CStudioPreferences::ShouldDisplayPivotPoint());
+}
+
+//==============================================================================
+/**
+ * Called when the View Wireframe menu item is chosen.
+ */
+void CMainFrame::OnViewWireframe()
+{
+ CStudioPreferences::SetWireframeModeOn(!CStudioPreferences::IsWireframeModeOn());
+ g_StudioApp.GetRenderer().RequestRender();
+}
+
+//==============================================================================
+/**
+ * Checks or unchecks the menu item depending on if the view is available or not.
+ * @param pCmdUI Pointer to the UI element that generated the message.
+ */
+void CMainFrame::OnUpdateViewWireframe()
+{
+ m_ui->actionWireframe->setChecked(CStudioPreferences::IsWireframeModeOn());
+}
+
+//==============================================================================
+/**
+ * Checks or unchecks the menu item depending whether tooltips should be shown
+ * or not.
+ * @param inCmdUI Pointer to the UI element that generated the message.
+ */
+void CMainFrame::OnUpdateViewTooltips()
+{
+ m_ui->actionTooltips->setChecked(CStudioPreferences::ShouldShowTooltips());
+}
+
+//==============================================================================
+/**
+ * Called when the "View->Tooltips" menu item is chosen. Toggles tooltips on
+ * and off for custom controls.
+ */
+void CMainFrame::OnViewTooltips()
+{
+ CStudioPreferences::SetShowTooltips(!CStudioPreferences::ShouldShowTooltips());
+}
+
+//==============================================================================
+/**
+ * Called when the update message occurs for the Help->Help Topics menu item.
+ * If the help file exists, the menu item is enabled, otherwise it's disabled.
+ * @param inCmdUI UI element that generated the message
+ */
+void CMainFrame::OnUpdateHelpIndex()
+{
+ CUICFile theFile(g_StudioApp.m_pszHelpFilePath);
+ m_ui->action_Reference_Manual->setEnabled(theFile.Exists());
+}
+
+//==============================================================================
+/**
+ * Handles the ID_HELP_INDEX command. Opens the online help for Studio.
+ */
+void CMainFrame::OnHelpIndex()
+{
+ CUICFile theFile(g_StudioApp.m_pszHelpFilePath);
+ if (theFile.Exists())
+ QDesktopServices::openUrl(QUrl::fromLocalFile(theFile.GetAbsolutePath().toQString()));
+}
+
+//==============================================================================
+/**
+ * Handles the ID_HELP_VISIT_QT command. Opens the Qt Web site.
+ */
+void CMainFrame::OnHelpVisitQt()
+{
+ Q3DStudio::CString theWebSite(::LoadResourceString(IDS_HELP_VISIT_QT));
+ QDesktopServices::openUrl(QUrl(theWebSite.toQString()));
+}
+
+//==============================================================================
+/**
+ * Opens the tutorial.
+ */
+void CMainFrame::OnHelpOpenTutorial()
+{
+ StudioTutorialWidget tutorial(false);
+ tutorial.exec();
+}
+
+//==============================================================================
+/**
+ * OnHelpBehaviorReference: Handles the ID_HELP_BEHAVIORREFERENCE message.
+ * Called when the Behavior Reference menu item is chosen inside the Help menu.
+ * Opens the HTML file to display the Behavior help.
+ */
+void CMainFrame::OnHelpBehaviorReference()
+{
+ CUICFile theFile(CUICFile::GetApplicationDirectory(),
+ ::LoadResourceString(IDS_HELP_BEHAVIORREFERENCE));
+ if (theFile.Exists()) {
+ g_StudioApp.GetCore()->GetDispatch()->FireOnNavigate(
+ Q3DStudio::CString::fromQString(theFile.GetURL().path()), true);
+ SaveLayout();
+ }
+}
+
+//==============================================================================
+/**
+ * Handle the file revert menu option.
+ */
+void CMainFrame::OnFileRevert()
+{
+ g_StudioApp.OnRevert();
+}
+
+void CMainFrame::OnFileConnectToDevice()
+{
+ if (m_remoteProject->isConnected())
+ m_remoteProject->disconnect();
+ else
+ m_remoteProject->connect();
+}
+
+//==============================================================================
+/**
+ * Handles the recent list.
+ */
+void CMainFrame::OnFileOpenRecent(int nID)
+{
+ g_StudioApp.OnFileOpenRecent(m_RecentItems->GetItem(nID));
+}
+
+//==============================================================================
+/**
+ * Tells the scene view to recheck its sizing mode and tells client to update
+ */
+void CMainFrame::RecheckSizingMode()
+{
+ m_SceneView->RecheckSizingMode();
+}
+
+//==============================================================================
+/**
+ * Callback when a Core is opened or fails to open.
+ */
+void CMainFrame::OnOpenDocument(const CUICFile &inFilename, bool inSucceeded)
+{
+ if (inSucceeded)
+ m_RecentItems->AddRecentItem(inFilename);
+ else
+ m_RecentItems->RemoveRecentItem(inFilename);
+}
+
+//==============================================================================
+/**
+ * Callback when a Core is saved or fails to save.
+ */
+void CMainFrame::OnSaveDocument(const CUICFile &inFilename, bool inSucceeded, bool inSaveCopy)
+{
+ if (!inSaveCopy)
+ OnOpenDocument(inFilename, inSucceeded);
+}
+
+//==============================================================================
+/**
+ * Callback for when a the doc gets a new path
+ */
+void CMainFrame::OnDocumentPathChanged(const CUICFile &inNewPath)
+{
+ QString theTitle = inNewPath.GetName().toQString();
+ if (theTitle.isEmpty())
+ theTitle = QObject::tr("Untitled");
+
+ theTitle = theTitle + " - " + QObject::tr("Qt 3D Studio");
+
+ // TODO: Move this whole pile to the studio app
+ setWindowTitle(theTitle);
+
+ if (inNewPath.Exists())
+ m_RecentItems->AddRecentItem(inNewPath);
+}
+
+//==============================================================================
+/**
+ * Secret hot-key compatibility with Aftereffects muscle memory
+ */
+void CMainFrame::OnOpenMostRecentlyUsedDocument()
+{
+ CUICFile theDocument = m_RecentItems->GetItem(0);
+ if (theDocument.Exists())
+ g_StudioApp.OnLoadDocument(theDocument);
+}
+
+void CMainFrame::OnShowSlide()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_SLIDE);
+}
+
+void CMainFrame::OnShowTimeline()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_TIMELINE);
+}
+
+void CMainFrame::OnShowBasic()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_BASICOBJECTS);
+}
+
+void CMainFrame::OnShowProject()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_PROJECT);
+}
+
+void CMainFrame::OnShowAction()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_ACTION);
+}
+
+void CMainFrame::OnShowInspector()
+{
+ m_PaletteManager->ShowControl(CPaletteManager::CONTROLTYPE_INSPECTOR);
+}
+
+CTimelineControl *CMainFrame::GetTimelineControl()
+{
+ return m_PaletteManager->GetTimelineControl();
+}
+
+ITimelineTimebar *CMainFrame::GetSelectedTimelineTimebar()
+{
+ CUICDMTimelineItemBinding *theTimelineItemBinding =
+ GetTimelineControl()->GetTranslationManager()->GetSelectedBinding();
+ if (theTimelineItemBinding == NULL)
+ return NULL;
+
+ return theTimelineItemBinding->GetTimelineItem()->GetTimebar();
+}
+
+CRecentItems *CMainFrame::GetRecentItems()
+{
+ return m_RecentItems;
+}
+
+QWidget *CMainFrame::GetActiveView()
+{
+ return centralWidget();
+}
+
+bool CMainFrame::eventFilter(QObject *obj, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::KeyPress: {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(event);
+ if (ke->key() == Qt::Key_Tab) {
+ if (m_PaletteManager->tabNavigateFocusedWidget(true))
+ return true;
+ } else if (ke->key() == Qt::Key_Backtab) {
+ if (m_PaletteManager->tabNavigateFocusedWidget(false))
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return QMainWindow::eventFilter(obj, event);
+}
diff --git a/src/Authoring/Studio/MainFrm.h b/src/Authoring/Studio/MainFrm.h
new file mode 100644
index 00000000..56373498
--- /dev/null
+++ b/src/Authoring/Studio/MainFrm.h
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_MAIN_FRAME
+#define INCLUDED_MAIN_FRAME 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PreviewHelper.h"
+#include "DispatchListeners.h"
+
+#include <QMainWindow>
+#include <QTimer>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CHotKeys;
+class CPaletteManager;
+class CPaletteState;
+class CRecentItems;
+class CSceneView;
+class CStudioApp;
+class CTimelineControl;
+class ITimelineTimebar;
+class RemoteProject;
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui
+{
+ class MainFrame;
+}
+QT_END_NAMESPACE
+
+class CMainFrame : public QMainWindow,
+ public CPresentationChangeListener,
+ public CFileOpenListener,
+ public CClientPlayChangeListener
+{
+ Q_OBJECT
+public:
+ CMainFrame();
+ virtual ~CMainFrame();
+
+ void OnNewPresentation() override;
+ void OnClosingPresentation() override;
+
+ // CFileOpenListener
+ void OnOpenDocument(const CUICFile &inFilename, bool inSucceeded) override;
+ void OnSaveDocument(const CUICFile &inFilename, bool inSucceeded, bool inSaveCopy) override;
+ void OnDocumentPathChanged(const CUICFile &inNewPath) override;
+
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+ void RecheckSizingMode();
+
+ void SaveLayout();
+ void RestoreLayout();
+ void SetLayout(const CPaletteState &inState);
+
+ // CClientPlayChangeListener
+ void OnPlayStart() override;
+ void OnPlayStop() override;
+ void OnTimeChanged(long inTime) override;
+
+ CRecentItems *GetRecentItems();
+
+ int OnCreate();
+
+ void onPlaybackTimeout();
+
+ void OnFileOpen();
+ void OnFileSave();
+ void OnUpdateFileSave();
+ void OnFileSaveAs();
+ void OnFileSaveCopy();
+ void OnFileNew();
+ void OnFileRevert();
+ void OnFileConnectToDevice();
+ void OnFileOpenRecent(int nID);
+
+ void OnEditRedo();
+ void OnEditUndo();
+ void OnUpdateEditUndo();
+ void OnUpdateEditRedo();
+ void OnEditCopy();
+ void OnUpdateEditCopy();
+ void OnEditCut();
+ void OnUpdateEditCut();
+ void OnEditPaste();
+ void OnUpdateEditPaste();
+ void OnEditDuplicate();
+ void OnUpdateEditDuplicate();
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ afx_msg LRESULT OnMsgRouterMsg(WPARAM inWParam, LPARAM inLParam);
+#endif
+ void timerEvent(QTimerEvent *event) override;
+ void showEvent(QShowEvent *event) override;
+
+ void OnUpdateTimelineSetTimeBarColor();
+ void OnTimelineSetTimeBarColor();
+ void OnTimelineSetChangedKeyframe();
+ void OnTimelineDeleteSelectedKeyframes();
+ void OnUpdateTimelineDeleteSelectedKeyframes();
+ void OnTimelineSetTimeBarText();
+ void OnUpdateTimelineSetTimeBarText();
+ void OnUpdateTimelineSetInterpolation();
+ void OnTimelineSetInterpolation();
+ void closeEvent(QCloseEvent *event) override;
+ void OnToolAutosetkeys();
+ void OnUpdateToolAutosetkeys();
+ void OnEditApplicationPreferences();
+ void OnEditPresentationPreferences();
+ void OnEditSubPresentations();
+ void OnPlaybackPlay();
+ void OnUpdatePlaybackPlay();
+ void OnPlaybackRewind();
+ void OnUpdatePlaybackRewind();
+ void OnPlaybackStop();
+ void OnUpdatePlaybackStop();
+ void OnPlaybackPreview();
+ void OnUpdatePlaybackPreview();
+ void OnUpdateToolMove();
+ void OnUpdateToolRotate();
+ void OnUpdateToolScale();
+ void OnUpdateToolGlobalManipulators();
+ void OnToolMove();
+ void OnToolRotate();
+ void OnToolScale();
+ void OnToolGlobalManipulators();
+ void OnUpdateToolChange();
+ void OnUpdateToolGroupSelection();
+ void OnUpdateToolItemSelection();
+
+ void OnViewBoundingBoxes();
+ void OnUpdateViewBoundingBoxes();
+ void OnViewPivotPoint();
+ void OnUpdateViewPivotPoint();
+ void OnViewWireframe();
+ void OnUpdateViewWireframe();
+ void OnViewHelpPalette();
+ void OnUpdateViewHelpPalette();
+ void OnUpdateViewTooltips();
+ void OnViewTooltips();
+ void OnUpdateHelpIndex();
+ void OnHelpIndex();
+ void OnHelpVisitQt();
+ void OnHelpOpenTutorial();
+ void OnHelpBehaviorReference();
+ void OnOpenMostRecentlyUsedDocument();
+
+ void OnViewAction();
+ void OnUpdateViewAction();
+ void OnViewBasicObjects();
+ void OnUpdateViewBasicObjects();
+ void OnViewInspector();
+ void OnUpdateViewInspector();
+ void OnViewProject();
+ void OnUpdateViewProject();
+ void OnViewSlide();
+ void OnUpdateViewSlide();
+ void OnViewTimeline();
+ void OnUpdateViewTimeline();
+
+ void OnEditCameraZoomExtent();
+ void OnEditCameraPan();
+ void OnEditCameraRotate();
+ void OnEditCameraZoom();
+ void OnUpdateCameraZoomExtentAndAuthorZoom();
+ void OnUpdateEditCameraPan();
+ void OnUpdateEditCameraRotate();
+ void OnUpdateEditCameraZoom();
+ void OnEditViewFillMode();
+ void OnUpdateEditViewFillMode();
+
+ void OnViewGuidesRulers();
+ void OnUpdateViewGuidesRulers();
+ void OnClearGuides();
+ void OnUpdateClearGuides();
+ void OnLockGuides();
+ void OnUpdateLockGuides();
+
+ void OnShowSlide();
+ void OnShowTimeline();
+ void OnShowBasic();
+ void OnShowProject();
+ void OnShowAction();
+ void OnShowInspector();
+
+ CTimelineControl *GetTimelineControl();
+ ITimelineTimebar *GetSelectedTimelineTimebar();
+
+ void EditPreferences(short inPageIndex);
+
+ void HandleEditViewFillModeKey();
+ void HandleEditCameraZoomExtent();
+
+ QWidget *GetActiveView();
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+
+ RemoteProject *m_remoteProject = nullptr;
+ CSceneView *m_SceneView = nullptr;
+ CRecentItems *m_RecentItems = nullptr;
+ CPaletteManager *m_PaletteManager = nullptr;
+ bool m_PlaybackFlag = false;
+
+ QScopedPointer<Ui::MainFrame> m_ui;
+ QTimer m_playbackTimer;
+};
+
+#endif // INCLUDED_MAIN_FRAME
diff --git a/src/Authoring/Studio/MainFrm.qrc b/src/Authoring/Studio/MainFrm.qrc
new file mode 100644
index 00000000..cb91ad47
--- /dev/null
+++ b/src/Authoring/Studio/MainFrm.qrc
@@ -0,0 +1,83 @@
+<RCC>
+ <qresource prefix="/">
+ <file>res/prefstab-00.png</file>
+ <file>res/prefstab-01.png</file>
+ <file>res/client_tools_hi_color-00.png</file>
+ <file>res/client_tools_hi_color-01.png</file>
+ <file>res/client_tools_hi_color-02.png</file>
+ <file>res/client_tools_hi_color-03.png</file>
+ <file>res/client_tools_hi_color-04.png</file>
+ <file>res/client_tools_hi_color-05.png</file>
+ <file>res/client_tools_hi_color-06.png</file>
+ <file>res/editcamera_tools_hi-00.png</file>
+ <file>res/editcamera_tools_hi-01.png</file>
+ <file>res/editcamera_tools_hi-02.png</file>
+ <file>res/editcamera_tools_hi-03.png</file>
+ <file>res/editcamera_tools_hi-04.png</file>
+ <file>res/editcamera_tools_hi-05.png</file>
+ <file>res/playback_tools_low-00.png</file>
+ <file>res/playback_tools_low-01.png</file>
+ <file>res/playback_tools_low-02.png</file>
+ <file>res/playback_tools_low-03.png</file>
+ <file>res/Toolbar-00.png</file>
+ <file>res/Toolbar-01.png</file>
+ <file>res/Toolbar-02.png</file>
+ <file>res/Toolbar-03.png</file>
+ <file>res/Toolbar-04.png</file>
+ <file>res/Toolbar-05.png</file>
+ <file>res/Toolbar-06.png</file>
+ <file>res/Toolbar-07.png</file>
+ <file>res/About Icon.bmp</file>
+ <file>res/Studio.png</file>
+ <file>style.qss</file>
+ <file>res/separator.png</file>
+ <file>res/editcamera_tools_hi-00_disabled.png</file>
+ <file>res/editcamera_tools_hi-01_disabled.png</file>
+ <file>res/editcamera_tools_hi-02_disabled.png</file>
+ <file>res/editcamera_tools_hi-03_disabled.png</file>
+ <file>res/editcamera_tools_hi-04_disabled.png</file>
+ <file>res/editcamera_tools_hi-05_disabled.png</file>
+ <file>res/editcamera_tools_hi-00@2x.png</file>
+ <file>res/editcamera_tools_hi-00_disabled@2x.png</file>
+ <file>res/editcamera_tools_hi-01@2x.png</file>
+ <file>res/editcamera_tools_hi-01_disabled@2x.png</file>
+ <file>res/editcamera_tools_hi-02@2x.png</file>
+ <file>res/editcamera_tools_hi-02_disabled@2x.png</file>
+ <file>res/editcamera_tools_hi-03@2x.png</file>
+ <file>res/editcamera_tools_hi-03_disabled@2x.png</file>
+ <file>res/editcamera_tools_hi-04@2x.png</file>
+ <file>res/editcamera_tools_hi-04_disabled@2x.png</file>
+ <file>res/editcamera_tools_hi-05@2x.png</file>
+ <file>res/editcamera_tools_hi-05_disabled@2x.png</file>
+ <file>res/playback_tools_low-00@2x.png</file>
+ <file>res/playback_tools_low-01@2x.png</file>
+ <file>res/playback_tools_low-02@2x.png</file>
+ <file>res/prefstab-01@2x.png</file>
+ <file>res/separator@2x.png</file>
+ <file>res/client_tools_hi_color-00@2x.png</file>
+ <file>res/client_tools_hi_color-01@2x.png</file>
+ <file>res/client_tools_hi_color-02@2x.png</file>
+ <file>res/client_tools_hi_color-03@2x.png</file>
+ <file>res/client_tools_hi_color-04@2x.png</file>
+ <file>res/client_tools_hi_color-05@2x.png</file>
+ <file>res/client_tools_hi_color-06@2x.png</file>
+ <file>res/prefstab-00@2x.png</file>
+ <file>res/arrow_down.png</file>
+ <file>res/arrow_down@2x.png</file>
+ <file>res/playback_tools_low-03@2x.png</file>
+ </qresource>
+ <qresource prefix="/startup">
+ <file alias="open_dialog.png">res/open_dialog.png</file>
+ </qresource>
+ <qresource prefix="/cursors">
+ <file alias="edit_camera_rot.png">res/edit_camera_rot.png</file>
+ <file alias="edit_camera_pan.png">res/edit_camera_pan.png</file>
+ <file alias="edit_camera_zoom.png">res/edit_camera_zoom.png</file>
+ <file alias="group_move.png">res/group_move.png</file>
+ <file alias="group_rotate.png">res/group_rotate.png</file>
+ <file alias="group_scale.png">res/group_scale.png</file>
+ <file alias="item_move.png">res/item_move.png</file>
+ <file alias="item_rotate.png">res/item_rotate.png</file>
+ <file alias="item_scale.png">res/item_scale.png</file>
+ </qresource>
+</RCC>
diff --git a/src/Authoring/Studio/MainFrm.ui b/src/Authoring/Studio/MainFrm.ui
new file mode 100644
index 00000000..3f626236
--- /dev/null
+++ b/src/Authoring/Studio/MainFrm.ui
@@ -0,0 +1,799 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainFrame</class>
+ <widget class="QMainWindow" name="MainFrame">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>973</width>
+ <height>617</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Qt 3D Studio</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Studio.png</normaloff>:/res/Studio.png</iconset>
+ </property>
+ <widget class="QWidget" name="centralwidget"/>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>973</width>
+ <height>21</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menu_File">
+ <property name="title">
+ <string>&amp;File</string>
+ </property>
+ <widget class="QMenu" name="menuRecent_Projects">
+ <property name="title">
+ <string>Recent Projects</string>
+ </property>
+ <addaction name="actionSomething"/>
+ </widget>
+ <addaction name="action_New"/>
+ <addaction name="action_Open"/>
+ <addaction name="action_Save"/>
+ <addaction name="actionSave_As"/>
+ <addaction name="actionSave_a_Copy"/>
+ <addaction name="action_Revert"/>
+ <addaction name="action_Connect_to_Device"/>
+ <addaction name="separator"/>
+ <addaction name="menuRecent_Projects"/>
+ <addaction name="separator"/>
+ <addaction name="action_Exit"/>
+ </widget>
+ <widget class="QMenu" name="menu_Edit">
+ <property name="title">
+ <string>&amp;Edit</string>
+ </property>
+ <addaction name="action_Undo"/>
+ <addaction name="action_Redo"/>
+ <addaction name="separator"/>
+ <addaction name="action_Cut"/>
+ <addaction name="action_Copy"/>
+ <addaction name="action_Paste"/>
+ <addaction name="action_Duplicate_Object"/>
+ <addaction name="separator"/>
+ <addaction name="actionStudio_Preferences"/>
+ <addaction name="actionPresentation_Settings"/>
+ <addaction name="actionSubpresentations"/>
+ </widget>
+ <widget class="QMenu" name="menu_View">
+ <property name="title">
+ <string>&amp;View</string>
+ </property>
+ <addaction name="actionAction"/>
+ <addaction name="actionBasic_Objects"/>
+ <addaction name="actionInspector"/>
+ <addaction name="actionProject"/>
+ <addaction name="actionSlide"/>
+ <addaction name="actionTimeline"/>
+ <addaction name="separator"/>
+ <addaction name="actionBounding_Boxes"/>
+ <addaction name="actionPivot_Point"/>
+ <addaction name="actionWireframe"/>
+ <addaction name="actionTooltips"/>
+ <addaction name="actionRulers_Guides"/>
+ <addaction name="actionLock_Guides"/>
+ <addaction name="actionClear_Guides"/>
+ </widget>
+ <widget class="QMenu" name="menu_Timeline">
+ <property name="title">
+ <string>&amp;Timeline</string>
+ </property>
+ <addaction name="actionSet_Changed_Keyframes"/>
+ <addaction name="actionDelete_Selected_Keyframe_s"/>
+ <addaction name="actionSet_Interpolation"/>
+ <addaction name="actionChange_Time_Bar_Color"/>
+ <addaction name="separator"/>
+ <addaction name="actionAutoset_Keyframes"/>
+ </widget>
+ <widget class="QMenu" name="menu_Help">
+ <property name="title">
+ <string>&amp;Help</string>
+ </property>
+ <addaction name="action_Reference_Manual"/>
+ <addaction name="action_Visit_Qt_Web_Site"/>
+ <addaction name="action_About_Qt_3D_Studio"/>
+ <addaction name="action_Open_Tutorial"/>
+ </widget>
+ <addaction name="menu_File"/>
+ <addaction name="menu_Edit"/>
+ <addaction name="menu_View"/>
+ <addaction name="menu_Timeline"/>
+ <addaction name="menu_Help"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <widget class="QToolBar" name="toolBar">
+ <property name="windowTitle">
+ <string>Tool Bar</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>15</height>
+ </size>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="action_New"/>
+ <addaction name="action_Open"/>
+ <addaction name="action_Save"/>
+ <addaction name="separator"/>
+ <addaction name="action_Cut"/>
+ <addaction name="action_Copy"/>
+ <addaction name="action_Paste"/>
+ <addaction name="separator"/>
+ <addaction name="action_Undo"/>
+ <addaction name="action_Redo"/>
+ </widget>
+ <widget class="QToolBar" name="m_ClientToolsBar">
+ <property name="windowTitle">
+ <string>Client Tools</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>true</bool>
+ </attribute>
+ <addaction name="actionGroup_Select_Tool"/>
+ <addaction name="actionItem_Select_Tool"/>
+ <addaction name="separator"/>
+ <addaction name="actionPosition_Tool"/>
+ <addaction name="actionScale_Tool"/>
+ <addaction name="actionRotation_Tool"/>
+ <addaction name="actionLocal_Global_Manipulators"/>
+ <addaction name="separator"/>
+ <addaction name="actionAutoset_Keyframes"/>
+ </widget>
+ <widget class="CEditCameraBar" name="m_EditCamerasBar">
+ <property name="windowTitle">
+ <string>Edit Cameras</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="separator"/>
+ <addaction name="actionFit_Selected"/>
+ <addaction name="actionPan_Tool"/>
+ <addaction name="actionZoom_Tool"/>
+ <addaction name="actionOrbit_Tool"/>
+ <addaction name="separator"/>
+ <addaction name="actionShading_Mode"/>
+ <addaction name="actionWireframe"/>
+ </widget>
+ <widget class="QToolBar" name="m_PlaybackToolbar">
+ <property name="windowTitle">
+ <string>Playback</string>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ <addaction name="actionRewind"/>
+ <addaction name="actionStop"/>
+ <addaction name="actionPlay"/>
+ <addaction name="separator"/>
+ <addaction name="actionPreview"/>
+ </widget>
+ <action name="action_Reference_Manual">
+ <property name="text">
+ <string>&amp;Reference Manual...</string>
+ </property>
+ </action>
+ <action name="action_Visit_Qt_Web_Site">
+ <property name="text">
+ <string>&amp;Visit Qt Web site...</string>
+ </property>
+ </action>
+ <action name="action_About_Qt_3D_Studio">
+ <property name="text">
+ <string>&amp;About Qt 3D Studio...</string>
+ </property>
+ </action>
+ <action name="action_Open_Tutorial">
+ <property name="text">
+ <string>&amp;Open Tutorial...</string>
+ </property>
+ </action>
+ <action name="actionSet_Changed_Keyframes">
+ <property name="text">
+ <string>Set Changed Keyframes</string>
+ </property>
+ <property name="shortcut">
+ <string>F6</string>
+ </property>
+ </action>
+ <action name="actionDelete_Selected_Keyframe_s">
+ <property name="text">
+ <string>Delete Selected Keyframe(s)</string>
+ </property>
+ </action>
+ <action name="actionSet_Interpolation">
+ <property name="text">
+ <string>Set Interpolation...</string>
+ </property>
+ </action>
+ <action name="actionChange_Time_Bar_Color">
+ <property name="text">
+ <string>Change Time Bar Color...</string>
+ </property>
+ </action>
+ <action name="actionAutoset_Keyframes">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-06.png</normaloff>:/res/client_tools_hi_color-06.png</iconset>
+ </property>
+ <property name="text">
+ <string>Autoset Keyframes</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle autoset keyframes</string>
+ </property>
+ </action>
+ <action name="actionAction">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Action</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+A</string>
+ </property>
+ </action>
+ <action name="actionBasic_Objects">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Basic Objects</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+B</string>
+ </property>
+ </action>
+ <action name="actionInspector">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Inspector</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+I</string>
+ </property>
+ </action>
+ <action name="actionProject">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Project</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+P</string>
+ </property>
+ </action>
+ <action name="actionSlide">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Slide</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+D</string>
+ </property>
+ </action>
+ <action name="actionTimeline">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Timeline</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+T</string>
+ </property>
+ </action>
+ <action name="actionBounding_Boxes">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Bounding Boxes</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+B</string>
+ </property>
+ </action>
+ <action name="actionPivot_Point">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Pivot Point</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Alt+P</string>
+ </property>
+ </action>
+ <action name="actionWireframe">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-05.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-05_disabled.png</disabledoff>:/res/editcamera_tools_hi-05.png</iconset>
+ </property>
+ <property name="text">
+ <string>Wireframe</string>
+ </property>
+ <property name="toolTip">
+ <string>Show or Hide Wireframe</string>
+ </property>
+ </action>
+ <action name="actionTooltips">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Tooltips</string>
+ </property>
+ </action>
+ <action name="actionRulers_Guides">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Rulers &amp;&amp; Guides</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+;</string>
+ </property>
+ </action>
+ <action name="actionLock_Guides">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Lock Guides</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Alt+;</string>
+ </property>
+ </action>
+ <action name="actionClear_Guides">
+ <property name="text">
+ <string>Clear Guides</string>
+ </property>
+ </action>
+ <action name="action_Undo">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-06.png</normaloff>:/res/Toolbar-06.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;Undo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Z</string>
+ </property>
+ </action>
+ <action name="action_Redo">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-07.png</normaloff>:/res/Toolbar-07.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;Redo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Y</string>
+ </property>
+ </action>
+ <action name="action_Cut">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-03.png</normaloff>:/res/Toolbar-03.png</iconset>
+ </property>
+ <property name="text">
+ <string>Cu&amp;t</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+X</string>
+ </property>
+ </action>
+ <action name="action_Copy">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-04.png</normaloff>:/res/Toolbar-04.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;Copy</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+C</string>
+ </property>
+ </action>
+ <action name="action_Paste">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-05.png</normaloff>:/res/Toolbar-05.png</iconset>
+ </property>
+ <property name="text">
+ <string>P&amp;aste</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+V</string>
+ </property>
+ </action>
+ <action name="action_Duplicate_Object">
+ <property name="text">
+ <string>&amp;Duplicate Object</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+D</string>
+ </property>
+ </action>
+ <action name="actionStudio_Preferences">
+ <property name="text">
+ <string>Studio &amp;Preferences...</string>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::PreferencesRole</enum>
+ </property>
+ </action>
+ <action name="actionPresentation_Settings">
+ <property name="text">
+ <string>Presentation &amp;Settings...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+P</string>
+ </property>
+ </action>
+ <action name="action_New">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-00.png</normaloff>:/res/Toolbar-00.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+N</string>
+ </property>
+ </action>
+ <action name="action_Open">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-01.png</normaloff>:/res/Toolbar-01.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;Open</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="action_Save">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/Toolbar-02.png</normaloff>:/res/Toolbar-02.png</iconset>
+ </property>
+ <property name="text">
+ <string>&amp;Save</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
+ </action>
+ <action name="actionSave_As">
+ <property name="text">
+ <string>Save &amp;As...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+S</string>
+ </property>
+ </action>
+ <action name="actionSave_a_Copy">
+ <property name="text">
+ <string>Save a Copy...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Alt+S</string>
+ </property>
+ </action>
+ <action name="action_Revert">
+ <property name="text">
+ <string>&amp;Revert...</string>
+ </property>
+ </action>
+ <action name="action_Connect_to_Device">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Connect to Device</string>
+ </property>
+ </action>
+ <action name="action_Exit">
+ <property name="text">
+ <string>E&amp;xit</string>
+ </property>
+ </action>
+ <action name="actionSomething">
+ <property name="text">
+ <string>Something</string>
+ </property>
+ </action>
+ <action name="actionGroup_Select_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-00.png</normaloff>:/res/client_tools_hi_color-00.png</iconset>
+ </property>
+ <property name="text">
+ <string>Group Select Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Group Select</string>
+ </property>
+ </action>
+ <action name="actionItem_Select_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-01.png</normaloff>:/res/client_tools_hi_color-01.png</iconset>
+ </property>
+ <property name="text">
+ <string>Item Select Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Select Item</string>
+ </property>
+ </action>
+ <action name="actionRotation_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-04.png</normaloff>:/res/client_tools_hi_color-04.png</iconset>
+ </property>
+ <property name="text">
+ <string>Rotation Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Rotate current selection</string>
+ </property>
+ </action>
+ <action name="actionPosition_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-02.png</normaloff>:/res/client_tools_hi_color-02.png</iconset>
+ </property>
+ <property name="text">
+ <string>Position Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Move current selection</string>
+ </property>
+ </action>
+ <action name="actionScale_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-03.png</normaloff>:/res/client_tools_hi_color-03.png</iconset>
+ </property>
+ <property name="text">
+ <string>Scale Tool</string>
+ </property>
+ <property name="toolTip">
+ <string>Scale current selection</string>
+ </property>
+ </action>
+ <action name="actionLocal_Global_Manipulators">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/client_tools_hi_color-05.png</normaloff>:/res/client_tools_hi_color-05.png</iconset>
+ </property>
+ <property name="text">
+ <string>Local/Global Manipulators</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle Manipulators to work in global/local space</string>
+ </property>
+ </action>
+ <action name="actionFit_Selected">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-00.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-00_disabled.png</disabledoff>:/res/editcamera_tools_hi-00.png</iconset>
+ </property>
+ <property name="text">
+ <string>Fit Selected (F)</string>
+ </property>
+ <property name="toolTip">
+ <string>Fit Selected</string>
+ </property>
+ </action>
+ <action name="actionPan_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-01.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-01_disabled.png</disabledoff>:/res/editcamera_tools_hi-01.png</iconset>
+ </property>
+ <property name="text">
+ <string>Pan Tool (Middle Mouse Drag)</string>
+ </property>
+ <property name="toolTip">
+ <string>Pan current edit camera</string>
+ </property>
+ </action>
+ <action name="actionZoom_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-02.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-02_disabled.png</disabledoff>:/res/editcamera_tools_hi-02.png</iconset>
+ </property>
+ <property name="text">
+ <string>Zoom Tool (Mouse Wheel)</string>
+ </property>
+ <property name="toolTip">
+ <string>Zoom current edit camera</string>
+ </property>
+ </action>
+ <action name="actionOrbit_Tool">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-03.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-03_disabled.png</disabledoff>:/res/editcamera_tools_hi-03.png</iconset>
+ </property>
+ <property name="text">
+ <string>Orbit Tool (Alt+Middle Mouse Drag)</string>
+ </property>
+ <property name="toolTip">
+ <string>Orbit current edit camera</string>
+ </property>
+ </action>
+ <action name="actionShading_Mode">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/editcamera_tools_hi-04.png</normaloff>
+ <disabledoff>:/res/editcamera_tools_hi-04_disabled.png</disabledoff>:/res/editcamera_tools_hi-04.png</iconset>
+ </property>
+ <property name="text">
+ <string>Shading Mode (F3)</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle Shading Mode</string>
+ </property>
+ <property name="shortcut">
+ <string>F3</string>
+ </property>
+ </action>
+ <action name="actionRewind">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/playback_tools_low-00.png</normaloff>:/res/playback_tools_low-00.png</iconset>
+ </property>
+ <property name="text">
+ <string>Rewind</string>
+ </property>
+ </action>
+ <action name="actionStop">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/playback_tools_low-01.png</normaloff>:/res/playback_tools_low-01.png</iconset>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </action>
+ <action name="actionPlay">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/playback_tools_low-02.png</normaloff>:/res/playback_tools_low-02.png</iconset>
+ </property>
+ <property name="text">
+ <string>Play</string>
+ </property>
+ </action>
+ <action name="actionPreview">
+ <property name="icon">
+ <iconset resource="MainFrm.qrc">
+ <normaloff>:/res/playback_tools_low-03.png</normaloff>:/res/playback_tools_low-03.png</iconset>
+ </property>
+ <property name="text">
+ <string>Preview</string>
+ </property>
+ </action>
+ <action name="actionSubpresentations">
+ <property name="text">
+ <string>Sub-presentations...</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>CEditCameraBar</class>
+ <extends>QToolBar</extends>
+ <header>Studio/_Win/UI/EditCameraBar.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="MainFrm.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/Palettes/Action/ActionContextMenu.cpp b/src/Authoring/Studio/Palettes/Action/ActionContextMenu.cpp
new file mode 100644
index 00000000..d2957e8b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionContextMenu.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2005 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ActionContextMenu.h"
+#include "StudioClipboard.h"
+
+//==============================================================================
+/**
+ * Constructor
+ *
+ * @param inActionControl Owner Action Control (commonly known as the Action Palette)
+ *that will handle all cut/copy/paste/delete of Actions.
+ */
+CActionContextMenu::CActionContextMenu(QWidget *parent)
+ : QMenu(parent)
+{
+ QAction *action = new QAction(tr("Copy Action"));
+ connect(action, &QAction::triggered, this, &CActionContextMenu::copyAction);
+ addAction(action);
+
+ action = new QAction(tr("Paste Action"));
+ action->setEnabled(CStudioClipboard::CanPasteAction());
+ connect(action, &QAction::triggered, this, &CActionContextMenu::pasteAction);
+ addAction(action);
+
+ action = new QAction(tr("Cut Action"));
+ connect(action, &QAction::triggered, this, &CActionContextMenu::cutAction);
+ addAction(action);
+
+ action = new QAction(tr("Delete Action"));
+ connect(action, &QAction::triggered, this, &CActionContextMenu::deleteAction);
+ addAction(action);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CActionContextMenu::~CActionContextMenu()
+{
+}
diff --git a/src/Authoring/Studio/Palettes/Action/ActionContextMenu.h b/src/Authoring/Studio/Palettes/Action/ActionContextMenu.h
new file mode 100644
index 00000000..f413232d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionContextMenu.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2005 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_ACTION_CONTEXT_MENU_H
+#define INCLUDED_ACTION_CONTEXT_MENU_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <QMenu>
+
+//==============================================================================
+/**
+ * Context menu that springs up on right mouseclick in the Action Palette.
+ */
+class CActionContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ explicit CActionContextMenu(QWidget *parent = nullptr);
+ virtual ~CActionContextMenu();
+
+Q_SIGNALS:
+ void cutAction();
+ void copyAction();
+ void pasteAction();
+ void deleteAction();
+};
+#endif // INCLUDED_ACTION_CONTEXT_MENU_H
diff --git a/src/Authoring/Studio/Palettes/Action/ActionModel.cpp b/src/Authoring/Studio/Palettes/Action/ActionModel.cpp
new file mode 100644
index 00000000..249aa636
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionModel.cpp
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ActionModel.h"
+
+#include "stdafx.h"
+#include "ClientDataModelBridge.h"
+#include "CmdDataModelActionSetValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "UICDMActionSystem.h"
+#include "UICDMStudioSystem.h"
+
+ActionModel::ActionModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+void ActionModel::setInstanceHandle(const UICDM::CUICDMInstanceHandle &handle)
+{
+ beginResetModel();
+ m_handle = handle;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ m_actions.clear();
+ doc->GetStudioSystem()->GetActionSystem()->GetActions(doc->GetActiveSlide(),
+ handle, m_actions);
+
+ endResetModel();
+}
+
+QHash<int, QByteArray> ActionModel::roleNames() const
+{
+ auto names = QAbstractItemModel::roleNames();
+ names.insert(DescriptionRole, "description");
+ names.insert(VisibleRole, "visible");
+
+ return names;
+}
+
+
+int ActionModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_actions.size();
+}
+
+QVariant ActionModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return {};
+
+ const auto action = m_actions.at(index.row());
+ switch (role)
+ {
+ case DescriptionRole:
+ return actionString(action);
+ case VisibleRole:
+ return actionSystem()->GetActionEyeballValue(activeSlide(), action);
+ default:
+ return {};
+ }
+
+ return {};
+}
+
+bool ActionModel::setData(const QModelIndex &index, const QVariant &data, int role)
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return false;
+
+ const auto action = m_actions.at(index.row());
+
+ if (role == VisibleRole) {
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ CCmd *theCmd = new CCmdDataModelActionSetEyeball(doc, activeSlide(), action, data.toBool());
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ Q_EMIT dataChanged(index, index, {VisibleRole});
+ }
+
+
+ return false;
+}
+
+void ActionModel::addAction(const CUICDMActionHandle &action)
+{
+ if (std::find(m_actions.begin(), m_actions.end(), action) == m_actions.end()) {
+ const auto count = rowCount();
+ beginInsertRows({}, count, count);
+ m_actions.push_back(action);
+ endInsertRows();
+ }
+}
+
+void ActionModel::removeAction(const CUICDMActionHandle &action)
+{
+ // KDAB_FIXME use beginRemoveRows
+ beginResetModel();
+ m_actions.erase(std::remove(m_actions.begin(), m_actions.end(), action), m_actions.end());
+ endResetModel();
+}
+
+void ActionModel::updateAction(const CUICDMActionHandle &action)
+{
+ for (unsigned i = 0; i < m_actions.size(); i++) {
+ if (m_actions[i] == action) {
+ auto idx = index(i, 0);
+ Q_EMIT dataChanged(idx, idx, {});
+ }
+ }
+}
+
+const CUICDMActionHandle ActionModel::actionAt(int row)
+{
+ if (row >= 0 && static_cast<unsigned>(row) < m_actions.size())
+ return m_actions[row];
+
+ return {};
+}
+
+const SActionInfo ActionModel::actionInfoAt(int row)
+{
+ const auto action = actionAt(row);
+ if (!action.Valid())
+ return {};
+ auto actionCore = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetActionCore();
+ return actionCore->GetActionInfo(action);
+}
+
+UICDM::IActionSystem *ActionModel::actionSystem() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetActionSystem();
+}
+
+UICDM::CUICDMSlideHandle ActionModel::activeSlide() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetActiveSlide();
+}
+
+QString ActionModel::actionString(const CUICDMActionHandle &action) const
+{
+ QString result;
+ if (action.Valid()) {
+ auto core = g_StudioApp.GetCore();
+ auto doc = core->GetDoc();
+ auto actionCore = doc->GetStudioSystem()->GetActionCore();
+ auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+
+ const SActionInfo &actionInfo = actionCore->GetActionInfo(action);
+
+ // Query the event name
+ QString eventFormalName(tr("[Unknown Event]"));
+ CUICDMEventHandle eventHandle = bridge->ResolveEvent(actionInfo);
+ if (eventHandle.Valid())
+ eventFormalName =
+ QString::fromWCharArray(bridge->GetEventInfo(eventHandle).m_FormalName.wide_str());
+
+ // Query the asset name
+ QString assetName = tr("[Unknown]");
+ assetName = bridge->GetName(actionInfo.m_Owner).toQString();
+
+ const auto sourceInstance =
+ bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject);
+ const auto targetInstance =
+ bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject);
+ QString sourceName = sourceInstance.Valid()
+ ? bridge->GetName(sourceInstance).toQString()
+ : tr("[Unknown Source]");
+ QString targetName = targetInstance.Valid()
+ ? bridge->GetName(targetInstance).toQString()
+ : tr("[Unknown Target]");
+
+ // Query the action name
+ QString handlerFormalName(tr("[Unknown Handler]"));
+ const auto handlerHandle = bridge->ResolveHandler(actionInfo);
+ if (handlerHandle.Valid())
+ handlerFormalName = QString::fromWCharArray(bridge->GetHandlerInfo(handlerHandle).m_FormalName.wide_str());
+
+ // Format the strings
+ if (actionInfo.m_Owner == sourceInstance) {
+ if (sourceInstance == targetInstance) {
+ result = tr("Listen for '%1', '%2'").arg(eventFormalName, handlerFormalName);
+ } else {
+ result = tr("Listen for '%1', tell %2 to '%3'").arg(eventFormalName, targetName,
+ handlerFormalName);
+ }
+ } else if (actionInfo.m_Owner == targetInstance) {
+ result = tr("Listen to '%1' for '%2', '%3'").arg(sourceName, eventFormalName,
+ handlerFormalName);
+ } else {
+ result = tr("Listen to '%1' for '%2', tell %3 to '%4'").arg(sourceName,
+ eventFormalName,
+ targetName,
+ handlerFormalName);
+ }
+ }
+ return result;
+}
diff --git a/src/Authoring/Studio/Palettes/Action/ActionModel.h b/src/Authoring/Studio/Palettes/Action/ActionModel.h
new file mode 100644
index 00000000..89a4586d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionModel.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ACTIONMODEL_H
+#define ACTIONMODEL_H
+
+#include <QAbstractListModel>
+
+#include "UICDMActionInfo.h"
+#include "UICDMHandles.h"
+
+namespace UICDM {
+ class IActionSystem;
+}
+
+class ActionModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit ActionModel(QObject *parent = nullptr);
+
+ void setInstanceHandle(const UICDM::CUICDMInstanceHandle &handle);
+
+ enum Roles {
+ DescriptionRole = Qt::DisplayRole,
+ VisibleRole = Qt::UserRole + 1,
+
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override;
+
+ void addAction(const UICDM::CUICDMActionHandle &action);
+ void removeAction(const UICDM::CUICDMActionHandle &action);
+ void updateAction(const UICDM::CUICDMActionHandle &action);
+ const UICDM::CUICDMActionHandle actionAt(int row);
+ const UICDM::SActionInfo actionInfoAt(int row);
+
+private:
+ UICDM::IActionSystem *actionSystem() const;
+ UICDM::CUICDMSlideHandle activeSlide() const;
+ QString actionString(const UICDM::CUICDMActionHandle &action) const;
+
+ UICDM::CUICDMInstanceHandle m_handle;
+ UICDM::TActionHandleList m_actions;
+};
+
+#endif // ACTIONMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Action/ActionView.cpp b/src/Authoring/Studio/Palettes/Action/ActionView.cpp
new file mode 100644
index 00000000..4aace20a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionView.cpp
@@ -0,0 +1,848 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ActionView.h"
+#include "ActionContextMenu.h"
+#include "ActionModel.h"
+#include "CmdDataModelActionSetValue.h"
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "IDocumentEditor.h"
+#include "IDocumentReader.h"
+#include "IObjectReferenceHelper.h"
+#include "Literals.h"
+#include "ObjectListModel.h"
+#include "StudioUtils.h"
+#include "StudioApp.h"
+#include "StudioClipboard.h"
+#include "StudioObjectTypes.h"
+#include "StudioPreferences.h"
+#include "UICFileTools.h"
+#include "UICDMActionCore.h"
+#include "UICDMDataTypes.h"
+#include "UICDMSlides.h"
+
+#include <QCoreApplication>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QTimer>
+
+ActionView::ActionView(QWidget *parent)
+ : QQuickWidget(parent)
+ , TabNavigable()
+ , m_actionsModel(new ActionModel(this))
+{
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ActionView::initialize);
+
+ g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this);
+
+ connect(this, &ActionView::actionChanged, this, [this] {
+ if (!m_propertyModel)
+ m_propertyModel = new PropertyModel(this);
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ if (actionInfo.m_Handler == L"Set Property") {
+ m_currentPropertyNameHandle = actionInfo.m_HandlerArgs.at(0);
+ m_currentPropertyValueHandle = actionInfo.m_HandlerArgs.at(1);
+ m_propertyModel->setAction(m_actionsModel->actionAt(m_currentActionIndex));
+ m_propertyModel->setNameHandle(m_currentPropertyNameHandle);
+ m_propertyModel->setValueHandle(m_currentPropertyValueHandle);
+
+ Q_EMIT propertyModelChanged();
+ }
+
+ });
+
+ m_actionChangedCompressionTimer.setInterval(500);
+ m_actionChangedCompressionTimer.setSingleShot(true);
+ connect(&m_actionChangedCompressionTimer, &QTimer::timeout, this, [this] {
+ updateHandlerArguments();
+ updateFiredEvent();
+ Q_EMIT actionChanged();
+ });
+}
+
+ActionView::~ActionView()
+{
+ m_connections.clear();
+}
+
+QSize ActionView::sizeHint() const
+{
+ return {500, 500};
+}
+
+void ActionView::setItem(const UICDM::CUICDMInstanceHandle &handle)
+{
+ m_objRefHelper = GetDoc()->GetDataModelObjectReferenceHelper();
+ m_itemHandle = handle;
+ m_actionsModel->setInstanceHandle(handle);
+ emitActionChanged();
+ Q_EMIT itemChanged();
+}
+
+QString ActionView::itemIcon() const
+{
+ if (!m_itemHandle.Valid())
+ return {};
+
+ auto info = m_objRefHelper->GetInfo(m_itemHandle);
+ return CStudioObjectTypes::GetNormalIconName(info.m_Type);
+}
+
+QString ActionView::itemText() const
+{
+ if (!m_itemHandle.Valid())
+ return tr("No Object Selected");
+
+ const auto data = m_objRefHelper->GetInfo(m_itemHandle);
+ return data.m_Name.toQString();
+}
+
+QColor ActionView::itemColor() const
+{
+ if (!m_itemHandle.Valid())
+ return Qt::white;
+
+ auto info = m_objRefHelper->GetInfo(m_itemHandle);
+ if (info.m_Master)
+ return CStudioPreferences::masterColor();
+ else
+ return CStudioPreferences::textColor();
+}
+
+QAbstractItemModel *ActionView::actionsModel() const
+{
+ return m_actionsModel;
+}
+
+QAbstractItemModel *ActionView::propertyModel() const
+{
+ return m_propertyModel;
+}
+
+QString ActionView::targetObjectName() const
+{
+ if (!GetDoc()->IsValid())
+ return {};
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+
+ const auto targetInstance =
+ GetBridge()->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject);
+
+ QString targetName = targetInstance.Valid()
+ ? GetBridge()->GetName(targetInstance).toQString()
+ : tr("[Unknown Target]");
+
+ return targetName;
+}
+
+QString ActionView::triggerObjectName() const
+{
+ if (!GetDoc()->IsValid())
+ return {};
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+
+ const auto sourceInstance =
+ GetBridge()->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject);
+
+ QString sourceName = sourceInstance.Valid()
+ ? GetBridge()->GetName(sourceInstance).toQString()
+ : tr("[Unknown Source]");
+
+ return sourceName;
+}
+
+QString ActionView::eventName() const
+{
+ if (!GetDoc()->IsValid())
+ return {};
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto eventHandle = bridge->ResolveEvent(actionInfo);
+ const auto eventInfo = bridge->GetEventInfo(eventHandle);
+
+ const QString formalName = QString::fromWCharArray(eventInfo.m_FormalName.wide_str());
+ return formalName.isEmpty() ? tr("[Unknown Event]") : formalName;
+}
+
+QString ActionView::handlerName() const
+{
+ if (!GetDoc()->IsValid())
+ return {};
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto handlerHandle = bridge->ResolveHandler(actionInfo);
+
+ if (handlerHandle.Valid()) {
+ const auto handlerInfo = bridge->GetHandlerInfo(handlerHandle);
+ return QString::fromWCharArray(handlerInfo.m_FormalName.wide_str());
+ }
+
+ return tr("[Unknown Handler]");
+}
+
+QVariantList ActionView::handlerArguments() const
+{
+ return m_handlerArguments;
+}
+
+PropertyInfo ActionView::property() const
+{
+ if (!m_propertyModel)
+ return {};
+ return m_propertyModel->property(m_currentPropertyIndex);
+}
+
+void ActionView::setCurrentActionIndex(int index)
+{
+ if (index == m_currentActionIndex)
+ return;
+
+ m_currentActionIndex = index;
+ emitActionChanged();
+}
+
+void ActionView::setCurrentPropertyIndex(int handle, int index)
+{
+ if (index == m_currentPropertyIndex)
+ return;
+
+ m_currentPropertyValueHandle = 0;
+ m_currentPropertyNameHandle = handle;
+ for (int i = 0; i < m_handlerArguments.size(); ++i) {
+ auto handlerArg = m_handlerArguments[i].value<HandlerArgument>();
+ if (handlerArg.m_handle.GetHandleValue() == handle && i < m_handlerArguments.size() - 1) {
+ m_currentPropertyValueHandle = m_handlerArguments[i + 1].value<HandlerArgument>().m_handle;
+ if (m_propertyModel) {
+ m_propertyModel->setNameHandle(m_currentPropertyNameHandle);
+ m_propertyModel->setValueHandle(m_currentPropertyValueHandle);
+ }
+ }
+ }
+
+ m_currentPropertyIndex = index;
+
+ // set the property for the handler
+ if (m_propertyModel && handle != 0) {
+ UICDM::SValue sValue(QVariant(m_propertyModel->property(index).m_nameId));
+ UICDM::SValue oldValue;
+ GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, oldValue);
+
+ if (!Equals(oldValue, sValue)) {
+ CCmd *theCmd =
+ new CCmdDataModelActionSetArgumentValue(GetDoc(), handle, sValue);
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ }
+ }
+
+ Q_EMIT propertyChanged();
+}
+
+void ActionView::addAction()
+{
+ if (m_itemHandle.Valid()) {
+ // Query data model bridge to see the applicable events and actions for this instance.
+ CClientDataModelBridge *theBridge = GetBridge();
+
+ std::wstring theEventName = theBridge->GetDefaultEvent(m_itemHandle);
+ std::wstring theHandlerName = theBridge->GetDefaultHandler(m_itemHandle);
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Add Action"))
+ ->AddAction(GetDoc()->GetActiveSlide(), m_itemHandle, theEventName,
+ theHandlerName);
+ }
+}
+
+void ActionView::deleteAction(int index)
+{
+ const auto action = m_actionsModel->actionAt(index);
+ if (action.Valid()) {
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Delete Action"))->DeleteAction(action);
+ }
+}
+
+QObject *ActionView::showTriggerObjectBrowser(const QPoint &point)
+{
+ if (!m_objectsModel) {
+ m_objectsModel = new ObjectListModel(g_StudioApp.GetCore(),
+ GetDoc()->GetActiveRootInstance(), this);
+ }
+
+ if (!m_triggerObjectBrowser)
+ m_triggerObjectBrowser = new ObjectBrowserView(this);
+
+ m_triggerObjectBrowser->setModel(m_objectsModel);
+
+ showBrowser(m_triggerObjectBrowser, point);
+
+ connect(m_triggerObjectBrowser, &ObjectBrowserView::selectionChanged,
+ this, [this] {
+ auto selectedItem = m_triggerObjectBrowser->selectedHandle();
+ setTriggerObject(m_objRefHelper->GetAssetRefValue(selectedItem,
+ m_itemHandle,
+ (CRelativePathTools::EPathType)(m_triggerObjectBrowser->pathType())));
+ });
+
+ return m_triggerObjectBrowser;
+}
+
+QObject *ActionView::showTargetObjectBrowser(const QPoint &point)
+{
+ if (!m_objectsModel) {
+ m_objectsModel = new ObjectListModel(g_StudioApp.GetCore(),
+ GetDoc()->GetActiveRootInstance(), this);
+ }
+
+ if (!m_targetObjectBrowser)
+ m_targetObjectBrowser = new ObjectBrowserView(this);
+
+ m_targetObjectBrowser->setModel(m_objectsModel);
+
+ showBrowser(m_targetObjectBrowser, point);
+
+ connect(m_targetObjectBrowser, &ObjectBrowserView::selectionChanged,
+ this, [this] {
+ auto selectedItem = m_targetObjectBrowser->selectedHandle();
+ setTargetObject(m_objRefHelper->GetAssetRefValue(selectedItem,
+ m_itemHandle,
+ (CRelativePathTools::EPathType)(m_targetObjectBrowser->pathType())));
+ resetFiredEvent();
+ });
+
+ return m_targetObjectBrowser;
+}
+
+void ActionView::showContextMenu(int x, int y)
+{
+ CActionContextMenu contextMenu(this);
+
+ connect(&contextMenu, &CActionContextMenu::copyAction, this, &ActionView::copyAction);
+ connect(&contextMenu, &CActionContextMenu::pasteAction, this, &ActionView::pasteAction);
+ connect(&contextMenu, &CActionContextMenu::cutAction, this, &ActionView::cutAction);
+ connect(&contextMenu, &CActionContextMenu::deleteAction, this, [this] {
+ deleteAction(m_currentActionIndex);
+ });
+
+ contextMenu.exec(mapToGlobal({x, y}));
+}
+
+QObject *ActionView::showEventBrowser(const QPoint &point)
+{
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject);
+
+ if (!instanceHandle.Valid())
+ return nullptr;
+
+ if (!m_eventsModel) {
+ m_eventsModel = new EventsModel(this);
+ }
+
+ UICDM::TEventHandleList eventList;
+ bridge->GetEvents(instanceHandle, eventList);
+ m_eventsModel->setEventList(eventList);
+
+ if (!m_eventsBrowser)
+ m_eventsBrowser = new EventsBrowserView(this);
+
+ m_eventsBrowser->setModel(m_eventsModel);
+
+ showBrowser(m_eventsBrowser, point);
+
+ connect(m_eventsBrowser, &EventsBrowserView::selectionChanged,
+ this, [this] {
+ setEvent(UICDM::CUICDMEventHandle(m_eventsBrowser->selectedHandle()));
+ });
+
+ return m_eventsBrowser;
+}
+
+QObject *ActionView::showHandlerBrowser(const QPoint &point)
+{
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject);
+
+ if (!instanceHandle.Valid())
+ return nullptr;
+
+ if (!m_handlersModel) {
+ m_handlersModel = new EventsModel(this);
+ }
+
+ UICDM::THandlerHandleList handlerList;
+ bridge->GetHandlers(instanceHandle, handlerList);
+ m_handlersModel->setHandlerList(handlerList);
+
+ if (!m_handlerBrowser)
+ m_handlerBrowser = new EventsBrowserView(this);
+
+ m_handlerBrowser->setModel(m_handlersModel);
+
+ showBrowser(m_handlerBrowser, point);
+
+ connect(m_handlerBrowser, &EventsBrowserView::selectionChanged,
+ this, [this] {
+ setHandler(UICDM::CUICDMHandlerHandle(m_handlerBrowser->selectedHandle()));
+ });
+
+ return m_handlerBrowser;
+}
+
+QObject *ActionView::showEventBrowserForArgument(int handle, const QPoint &point)
+{
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject);
+
+ if (!instanceHandle.Valid())
+ return nullptr;
+
+ if (!m_fireEventsModel) {
+ m_fireEventsModel = new EventsModel(this);
+ }
+
+ UICDM::TEventHandleList eventList;
+ bridge->GetEvents(instanceHandle, eventList);
+ m_fireEventsModel->setEventList(eventList);
+
+ if (!m_fireEventsBrowser)
+ m_fireEventsBrowser = new EventsBrowserView(this);
+
+ m_fireEventsBrowser->setModel(m_fireEventsModel);
+
+ showBrowser(m_fireEventsBrowser, point);
+
+ connect(m_fireEventsBrowser, &EventsBrowserView::selectionChanged,
+ this, [this, handle] {
+ setArgumentValue(handle, UICDM::CUICDMEventHandle(m_fireEventsBrowser->selectedHandle()).GetHandleValue());
+ });
+
+ return m_fireEventsBrowser;
+}
+
+void ActionView::showBrowser(QQuickWidget *browser, const QPoint &point)
+{
+ QSize popupSize = CStudioPreferences::browserPopupSize();
+ browser->disconnect();
+ browser->resize(popupSize);
+ browser->move(point - QPoint(popupSize.width(), popupSize.height()));
+
+ // Show asynchronously to avoid flashing blank window on first show
+ QTimer::singleShot(0, this, [browser] {
+ browser->show();
+ browser->activateWindow();
+ browser->setFocus();
+ });
+}
+
+void ActionView::updateFiredEvent()
+{
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ if (actionInfo.m_Handler != L"Fire Event") {
+ m_firedEvent = tr("[Unknown event]");
+ return;
+ }
+
+ const auto doc = GetDoc();
+ if (!doc->IsValid())
+ return;
+
+ const auto bridge = GetBridge();
+ const auto handlerHandle = bridge->ResolveHandler(actionInfo);
+ IActionCore *actionCore = doc->GetStudioSystem()->GetActionCore();
+
+ if (handlerHandle.Valid()) {
+ for (const auto &argHandle: actionInfo.m_HandlerArgs) {
+ const auto &argumentInfo = actionCore->GetHandlerArgumentInfo(argHandle);
+ DataModelDataType::Value theArgType(GetValueType(argumentInfo.m_Value));
+ SValue theArgValue(argumentInfo.m_Value);
+ if (argumentInfo.m_ArgType == HandlerArgumentType::Event) {
+ theArgType = DataModelDataType::String;
+ auto theEventHandle = get<qt3ds::QT3DSI32>(argumentInfo.m_Value);
+ theArgValue = SValue(std::make_shared<CDataStr>(
+ bridge->GetEventInfo(theEventHandle).m_Name.wide_str()));
+ m_firedEvent = theArgValue.toQVariant().toString();
+ Q_EMIT firedEventChanged();
+ }
+ }
+ }
+}
+
+void ActionView::updateFiredEventFromHandle(int handle)
+{
+ m_firedEvent = QString::fromWCharArray(GetBridge()->GetEventInfo(handle).m_FormalName.wide_str());
+ Q_EMIT firedEventChanged();
+}
+
+void ActionView::resetFiredEvent()
+{
+ m_firedEvent = tr("[Unknown Event]");
+ Q_EMIT firedEventChanged();
+}
+
+void ActionView::OnNewPresentation()
+{
+ // Register callback
+ UICDM::IStudioFullSystemSignalProvider *theSignalProvider =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider();
+ m_connections.push_back(theSignalProvider->ConnectActionCreated(
+ std::bind(&ActionView::OnActionAdded, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_connections.push_back(theSignalProvider->ConnectActionDeleted(
+ std::bind(&ActionView::OnActionDeleted, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_connections.push_back(theSignalProvider->ConnectTriggerObjectSet(
+ std::bind(&ActionView::OnActionModified, this, std::placeholders::_1)));
+ m_connections.push_back(theSignalProvider->ConnectTargetObjectSet(
+ std::bind(&ActionView::OnActionModified, this, std::placeholders::_1)));
+ m_connections.push_back(theSignalProvider->ConnectEventSet(
+ std::bind(&ActionView::OnActionModified, this, std::placeholders::_1)));
+ m_connections.push_back(theSignalProvider->ConnectHandlerSet(
+ std::bind(&ActionView::OnActionModified, this, std::placeholders::_1)));
+ m_connections.push_back(theSignalProvider->ConnectHandlerArgumentValueSet(
+ std::bind(&ActionView::OnHandlerArgumentModified, this, std::placeholders::_1)));
+ m_connections.push_back(theSignalProvider->ConnectInstancePropertyValue(
+ std::bind(&ActionView::OnInstancePropertyValueChanged, this, std::placeholders::_1,
+ std::placeholders::_2)));
+ CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch();
+ m_connections.push_back(theDispatch->ConnectSelectionChange(
+ std::bind(&ActionView::OnSelectionSet, this,
+ std::placeholders::_1)));
+}
+
+void ActionView::OnSelectionSet(Q3DStudio::SSelectedValue inSelectable)
+{
+ UICDM::CUICDMInstanceHandle theInstance;
+ std::vector<UICDM::CUICDMInstanceHandle> instances;
+
+ switch (inSelectable.getType()) {
+ case Q3DStudio::SelectedValueTypes::Instance:
+ theInstance = inSelectable.getData<UICDM::CUICDMInstanceHandle>();
+ break;
+ case Q3DStudio::SelectedValueTypes::MultipleInstances:
+ instances = inSelectable.getData<std::vector<UICDM::CUICDMInstanceHandle>>();
+ // handling only if we have one selected element.
+ if (instances.size() == 1)
+ theInstance = instances[0];
+ break;
+ case Q3DStudio::SelectedValueTypes::Slide: {
+ UICDM::CUICDMSlideHandle theSlideHandle =
+ inSelectable.getData<Q3DStudio::SSlideInstanceWrapper>().m_Slide;
+ // Get the owning component instance
+ CClientDataModelBridge *theBridge = GetBridge();
+ UICDM::SLong4 theComponentGuid = theBridge->GetComponentGuid(theSlideHandle);
+ Q_ASSERT(GuidValid(theComponentGuid));
+ theInstance = theBridge->GetInstanceByGUID(theComponentGuid);
+ Q_ASSERT(theInstance.Valid());
+ } break;
+ };
+
+ setItem(theInstance);
+}
+
+void ActionView::OnActionAdded(UICDM::CUICDMActionHandle inAction, UICDM::CUICDMSlideHandle inSlide, UICDM::CUICDMInstanceHandle inOwner)
+{
+ CDoc *theDoc = GetDoc();
+ UICDM::CStudioSystem *theStudioSystem = theDoc->GetStudioSystem();
+
+ UICDM::CUICDMSlideHandle theCurrentSlide = theDoc->GetActiveSlide();
+ UICDM::CUICDMSlideHandle theMasterSlideOfAction =
+ theStudioSystem->GetSlideSystem()->GetMasterSlide(inSlide);
+ UICDM::CUICDMSlideHandle theMasterOfCurrentSlide =
+ theStudioSystem->GetSlideSystem()->GetMasterSlide(theCurrentSlide);
+
+ if (inOwner == m_itemHandle && // the action is added to current viewed instance
+ (theCurrentSlide == inSlide || // and is added to the current viewed slide
+ (theMasterSlideOfAction == inSlide
+ && theMasterOfCurrentSlide == theMasterSlideOfAction))) // or it is added to the master of
+ // the current viewed slide
+ {
+ m_actionsModel->addAction(inAction);
+// KDAB_TODO SortActions();
+ }
+}
+
+void ActionView::OnActionDeleted(UICDM::CUICDMActionHandle inAction, UICDM::CUICDMSlideHandle inSlide, UICDM::CUICDMInstanceHandle inOwner)
+{
+ Q_UNUSED(inSlide);
+ Q_UNUSED(inOwner);
+ m_actionsModel->removeAction(inAction);
+}
+
+void ActionView::OnActionModified(UICDM::CUICDMActionHandle inAction)
+{
+ if (GetDoc()->GetStudioSystem()->GetActionCore()->HandleValid(inAction)) {
+ m_actionsModel->updateAction(inAction);
+ emitActionChanged();
+ }
+}
+
+void ActionView::OnHandlerArgumentModified(UICDM::CUICDMHandlerArgHandle inHandlerArgument)
+{
+ emitActionChanged();
+}
+
+void ActionView::OnInstancePropertyValueChanged(UICDM::CUICDMInstanceHandle inInstance, UICDM::CUICDMPropertyHandle inProperty)
+{
+ emitActionChanged();
+}
+
+void ActionView::copyAction()
+{
+ auto theTempAPFile =
+ GetDoc()->GetDocumentReader().CopyAction(m_actionsModel->actionAt(m_currentActionIndex),
+ GetDoc()->GetActiveSlide());
+ CUICFile theFile(theTempAPFile);
+ CStudioClipboard::CopyActionToClipboard(theFile);
+}
+
+void ActionView::cutAction()
+{
+ copyAction();
+ auto action = m_actionsModel->actionAt(m_currentActionIndex);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Cut Action"))->DeleteAction(action);
+}
+
+void ActionView::pasteAction()
+{
+ CUICFile theTempAPFile = CStudioClipboard::GetActionFromClipboard();
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Paste Action"))
+ ->PasteAction(theTempAPFile.GetAbsolutePath(), m_itemHandle);
+}
+
+void ActionView::setTriggerObject(const UICDM::SObjectRefType &object)
+{
+ auto action = m_actionsModel->actionAt(m_currentActionIndex);
+ if (!action.Valid())
+ return;
+
+ auto core = g_StudioApp.GetCore();
+ auto theBridge = GetBridge();
+
+ auto theCmd = new CCmdDataModelActionSetTriggerObject(GetDoc(), action, object);
+ const SActionInfo &theActionInfo = GetDoc()->GetStudioSystem()->GetActionCore()->GetActionInfo(action);
+
+ CUICDMInstanceHandle theBaseInstance = theActionInfo.m_Owner;
+ CUICDMInstanceHandle theObjectInstance = theBridge->GetInstance(theBaseInstance, object);
+ CUICDMInstanceHandle theOldInstance = theBridge->GetInstance(theBaseInstance, theActionInfo.m_TargetObject);
+ // old instance and object instance could be the same, for example if user changes the type
+ // from Absolute to Path. In this case we don't need to reset handler or event.
+ if (theOldInstance != theObjectInstance)
+ theCmd->ResetEvent(
+ theBridge->GetDefaultEvent(theObjectInstance, theActionInfo.m_Event));
+
+ core->ExecuteCommand(theCmd);
+ emitActionChanged();
+}
+
+void ActionView::setTargetObject(const UICDM::SObjectRefType &object)
+{
+ auto action = m_actionsModel->actionAt(m_currentActionIndex);
+ if (!action.Valid())
+ return;
+
+ auto core = g_StudioApp.GetCore();
+ auto doc = GetDoc();
+ auto theBridge = GetBridge();
+
+ auto theCmd = new CCmdDataModelActionSetTargetObject(doc, action, object);
+ const SActionInfo &theActionInfo = doc->GetStudioSystem()->GetActionCore()->GetActionInfo(action);
+
+ CUICDMInstanceHandle theBaseInstance = theActionInfo.m_Owner;
+ CUICDMInstanceHandle theObjectInstance = theBridge->GetInstance(theBaseInstance, object);
+ CUICDMInstanceHandle theOldInstance = theBridge->GetInstance(theBaseInstance, theActionInfo.m_TargetObject);
+ // old instance and object instance could be the same, for example if user changes the type
+ // from Absolute to Path. In this case we don't need to reset handler or event.
+ if (theOldInstance != theObjectInstance)
+ theCmd->ResetHandler(
+ theBridge->GetDefaultHandler(theObjectInstance, theActionInfo.m_Handler));
+
+ core->ExecuteCommand(theCmd);
+ emitActionChanged();
+}
+
+void ActionView::setEvent(const CUICDMEventHandle &event)
+{
+ if (!event.Valid())
+ return;
+
+ auto doc = GetDoc();
+ const auto action = m_actionsModel->actionAt(m_currentActionIndex);
+ CCmd *theCmd = new CCmdDataModelActionSetEvent(doc, action,
+ doc->GetStudioSystem()
+ ->GetActionMetaData()
+ ->GetEventInfo(event)
+ ->m_Name.wide_str());
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+}
+
+void ActionView::setHandler(const CUICDMHandlerHandle &handler)
+{
+ if (!handler.Valid())
+ return;
+
+ auto doc = GetDoc();
+ const auto action = m_actionsModel->actionAt(m_currentActionIndex);
+ wstring handlerName(doc->GetStudioSystem()->GetActionMetaData()->GetHandlerInfo(handler)
+ ->m_Name.wide_str());
+ CCmdDataModelActionSetHandler *theCmd =
+ new CCmdDataModelActionSetHandler(doc, action, handlerName);
+ theCmd->ResetHandler(handlerName); // reset the handler args
+
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+}
+
+QVariant ActionView::handlerArgumentValue(int handle) const
+{
+ UICDM::SValue value;
+ GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, value);
+ return value.toQVariant();
+}
+
+void ActionView::updateHandlerArguments()
+{
+ m_currentPropertyValueHandle = 0;
+ m_currentPropertyNameHandle = 0;
+ m_handlerArguments.clear();
+ const auto doc = GetDoc();
+ if (!doc->IsValid())
+ return;
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ const auto bridge = GetBridge();
+ const auto handlerHandle = bridge->ResolveHandler(actionInfo);
+ IActionCore *actionCore = doc->GetStudioSystem()->GetActionCore();
+
+ if (handlerHandle.Valid()) {
+ auto newMetaData = doc->GetStudioSystem()->GetActionMetaData();
+
+ for (const auto &argHandle: actionInfo.m_HandlerArgs) {
+ const auto &argumentInfo = actionCore->GetHandlerArgumentInfo(argHandle);
+ Option<SMetaDataHandlerArgumentInfo> argMetaData(
+ newMetaData->FindHandlerArgumentByName(handlerHandle, argumentInfo.m_Name));
+
+
+ HandlerArgument argument;
+ argument.m_handle = argHandle;
+ argument.m_type = argMetaData->m_ArgType;
+ argument.m_name = QString::fromWCharArray(argumentInfo.m_Name.wide_str());
+ argument.m_value = argumentInfo.m_Value.toQVariant();
+ argument.m_completeType = argMetaData->m_CompleteType;
+ m_handlerArguments.append(QVariant::fromValue(argument));
+ }
+ }
+}
+
+void ActionView::emitActionChanged()
+{
+ m_actionChangedCompressionTimer.start();
+}
+
+void ActionView::setArgumentValue(int handle, const QVariant &value)
+{
+ if (handle == 0)
+ return;
+
+ UICDM::SValue sValue(value);
+ UICDM::SValue oldValue;
+ GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, oldValue);
+
+ if (!Equals(oldValue, sValue)) {
+ CCmd *theCmd =
+ new CCmdDataModelActionSetArgumentValue(GetDoc(), handle, sValue);
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ }
+
+ const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex);
+ if (actionInfo.m_Handler == L"Fire Event") {
+ if (value.toInt())
+ updateFiredEventFromHandle(value.toInt());
+ }
+}
+
+CDoc *ActionView::GetDoc()
+{
+ return g_StudioApp.GetCore()->GetDoc();
+}
+
+CClientDataModelBridge *ActionView::GetBridge()
+{
+ return GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+void ActionView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_actionView"_L1, this);
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+ rootContext()->setContextProperty("_tabOrderHandler"_L1, tabOrderHandler());
+ qmlRegisterUncreatableType<UICDM::HandlerArgumentType>("Qt3DStudio", 1, 0, "HandlerArgumentType",
+ "HandlerArgumentType is an enum container"_L1);
+ qmlRegisterUncreatableType<UICDM::DataModelDataType>("Qt3DStudio", 1, 0, "DataModelDataType",
+ "DataModelDataType is an enum container"_L1);
+ qmlRegisterUncreatableType<UICDM::AdditionalMetaDataType>("Qt3DStudio", 1, 0, "AdditionalMetaDataType",
+ "AdditionalMetaDataType is an enum container"_L1);
+ qmlRegisterUncreatableType<PropertyInfo>("Qt3DStudio", 1, 0, "PropertyInfo",
+ "PropertyInfo is anot creatable in QML"_L1);
+ qmlRegisterUncreatableType<UICDM::CompleteMetaDataType>("Qt3DStudio", 1, 0, "CompleteMetaDataType",
+ "CompleteMetaDataType is an enum container"_L1);
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Action/ActionView.qml"_L1));
+}
+
+QStringList ActionView::slideNames()
+{
+ std::list<Q3DStudio::CString> outSlideNames;
+ QStringList slideNames;
+ CClientDataModelBridge *theBridge = GetBridge();
+ const auto action = m_actionsModel->actionAt(m_currentActionIndex);
+
+ theBridge->GetSlideNamesOfAction(action, outSlideNames);
+
+ for (auto slideName : outSlideNames) {
+ slideNames.append(slideName.toQString());
+ }
+
+ return slideNames;
+}
+
+int ActionView::slideNameToIndex(const QString &name)
+{
+ const auto slides = slideNames(); // KDAB_TODO cache it
+ return slides.indexOf(name);
+}
diff --git a/src/Authoring/Studio/Palettes/Action/ActionView.h b/src/Authoring/Studio/Palettes/Action/ActionView.h
new file mode 100644
index 00000000..dec7b078
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionView.h
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef ACTIONVIEW_H
+#define ACTIONVIEW_H
+
+#include <QQuickWidget>
+#include <QColor>
+#include <QPointer>
+#include <QTimer>
+
+#include "stdafx.h"
+#include "DispatchListeners.h"
+#include "EventsBrowserView.h"
+#include "EventsModel.h"
+#include "ObjectBrowserView.h"
+#include "ObjectListModel.h"
+#include "PropertyModel.h"
+#include "SelectedValueImpl.h"
+#include "UICDMHandles.h"
+#include "UICDMSignals.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMMetaDataTypes.h"
+#include "TabOrderHandler.h"
+
+class ActionModel;
+class CClientDataModelBridge;
+class CCore;
+class CDoc;
+class IObjectReferenceHelper;
+
+class QAbstractItemModel;
+
+struct HandlerArgument {
+ Q_PROPERTY(UICDM::HandlerArgumentType::Value type MEMBER m_type FINAL)
+ Q_PROPERTY(QString name MEMBER m_name FINAL)
+ Q_PROPERTY(int handle MEMBER m_handle FINAL)
+ Q_PROPERTY(QVariant value MEMBER m_value FINAL)
+ Q_PROPERTY(UICDM::CompleteMetaDataType::Enum completeType MEMBER m_completeType FINAL)
+
+ UICDM::CUICDMHandlerArgHandle m_handle;
+ UICDM::HandlerArgumentType::Value m_type;
+ UICDM::CompleteMetaDataType::Enum m_completeType;
+ QString m_name;
+ QVariant m_value;
+
+ Q_GADGET
+};
+
+Q_DECLARE_METATYPE(HandlerArgument)
+
+class ActionView : public QQuickWidget,
+ public CPresentationChangeListener,
+ public TabNavigable
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QAbstractItemModel *actionsModel READ actionsModel NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QAbstractItemModel *propertyModel READ propertyModel NOTIFY propertyModelChanged FINAL)
+ Q_PROPERTY(QString itemIcon READ itemIcon NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QString itemText READ itemText NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QColor itemColor READ itemColor NOTIFY itemChanged FINAL)
+ Q_PROPERTY(QString triggerObjectName READ triggerObjectName NOTIFY actionChanged FINAL)
+ Q_PROPERTY(QString targetObjectName READ targetObjectName NOTIFY actionChanged FINAL)
+ Q_PROPERTY(QString eventName READ eventName NOTIFY actionChanged FINAL)
+ Q_PROPERTY(QString handlerName READ handlerName NOTIFY actionChanged FINAL)
+ Q_PROPERTY(QVariantList handlerArguments READ handlerArguments NOTIFY actionChanged FINAL)
+ Q_PROPERTY(PropertyInfo property READ property NOTIFY propertyChanged FINAL)
+ Q_PROPERTY(QString firedEvent MEMBER m_firedEvent NOTIFY firedEventChanged FINAL)
+
+public:
+ ActionView(QWidget *parent = nullptr);
+ ~ActionView();
+
+ QSize sizeHint() const override;
+
+ void setItem(const UICDM::CUICDMInstanceHandle &handle);
+ QString itemIcon() const;
+ QString itemText() const;
+ QColor itemColor() const;
+ QAbstractItemModel *actionsModel() const;
+ QAbstractItemModel *propertyModel() const;
+ QString targetObjectName() const;
+ QString triggerObjectName() const;
+ QString eventName() const;
+ QString handlerName() const;
+ QVariantList handlerArguments() const;
+ PropertyInfo property() const;
+
+ Q_INVOKABLE void setCurrentActionIndex(int index);
+ Q_INVOKABLE void setCurrentPropertyIndex(int handle, int index);
+ Q_INVOKABLE void addAction();
+ Q_INVOKABLE void deleteAction(int index);
+ Q_INVOKABLE QObject *showTriggerObjectBrowser(const QPoint &point);
+ Q_INVOKABLE QObject *showTargetObjectBrowser(const QPoint &point);
+ Q_INVOKABLE void showContextMenu(int x, int y);
+ Q_INVOKABLE QObject *showEventBrowser(const QPoint &point);
+ Q_INVOKABLE QObject *showHandlerBrowser(const QPoint &point);
+ Q_INVOKABLE QObject *showEventBrowserForArgument(int handle, const QPoint &point);
+ Q_INVOKABLE void setArgumentValue(int handle, const QVariant &value);
+ Q_INVOKABLE QStringList slideNames();
+ Q_INVOKABLE int slideNameToIndex(const QString &name);
+
+
+ // CPresentationChangeListener
+ void OnNewPresentation() override;
+
+ // ISelectionChangeListener
+ void OnSelectionSet(Q3DStudio::SSelectedValue inSelectable);
+
+ // Action callback
+ void OnActionAdded(UICDM::CUICDMActionHandle inAction, UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inOwner);
+ void OnActionDeleted(UICDM::CUICDMActionHandle inAction, UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inOwner);
+ void OnActionModified(UICDM::CUICDMActionHandle inAction);
+ void OnHandlerArgumentModified(UICDM::CUICDMHandlerArgHandle inHandlerArgument);
+ void OnInstancePropertyValueChanged(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+
+Q_SIGNALS:
+ void itemChanged();
+ void actionChanged();
+ void propertyModelChanged();
+ void propertyChanged();
+ void firedEventChanged();
+
+private Q_SLOTS:
+ void copyAction();
+ void cutAction();
+ void pasteAction();
+
+private:
+ void setTriggerObject(const UICDM::SObjectRefType &object);
+ void setTargetObject(const UICDM::SObjectRefType &object);
+ void setEvent(const UICDM::CUICDMEventHandle &event);
+ void setHandler(const UICDM::CUICDMHandlerHandle &handler);
+ QVariant handlerArgumentValue(int handle) const;
+ void updateHandlerArguments();
+ void emitActionChanged();
+ void updateFiredEvent();
+ void resetFiredEvent();
+ void updateFiredEventFromHandle(int handle);
+ void showBrowser(QQuickWidget *browser, const QPoint &point);
+
+ static CDoc *GetDoc();
+ static CClientDataModelBridge *GetBridge();
+
+ void initialize();
+ QColor m_baseColor = QColor::fromRgb(75, 75, 75);
+ QColor m_selectColor = Qt::transparent;
+ UICDM::CUICDMInstanceHandle m_itemHandle;
+ IObjectReferenceHelper *m_objRefHelper = nullptr;
+ ActionModel *m_actionsModel = nullptr;
+ PropertyModel *m_propertyModel = nullptr;
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>>
+ m_connections; /// connections to the UICDM
+ QPointer<ObjectListModel> m_objectsModel;
+ QPointer<ObjectBrowserView> m_triggerObjectBrowser;
+ QPointer<ObjectBrowserView> m_targetObjectBrowser;
+ QPointer<EventsModel> m_eventsModel;
+ QPointer<EventsModel> m_handlersModel;
+ QPointer<EventsModel> m_fireEventsModel;
+ QPointer<EventsBrowserView> m_eventsBrowser;
+ QPointer<EventsBrowserView> m_handlerBrowser;
+ QPointer<EventsBrowserView> m_fireEventsBrowser;
+ int m_currentActionIndex = -1;
+ int m_currentPropertyIndex = -1;
+ UICDM::CUICDMHandlerArgHandle m_currentPropertyNameHandle;
+ UICDM::CUICDMHandlerArgHandle m_currentPropertyValueHandle;
+ QVariantList m_handlerArguments;
+ QTimer m_actionChangedCompressionTimer;
+ QString m_firedEvent;
+};
+
+#endif // ACTIONVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Action/ActionView.qml b/src/Authoring/Studio/Palettes/Action/ActionView.qml
new file mode 100644
index 00000000..c1debc3a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/ActionView.qml
@@ -0,0 +1,426 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import Qt3DStudio 1.0
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+
+ Flickable {
+ id: actionFlickable
+ ScrollBar.vertical: ScrollBar {
+ id: scrollBar
+ visible: size < 1.0
+ }
+
+ anchors.fill: parent
+ contentHeight: contentColumn.height
+
+ property bool scrollToBottom: false
+
+ onContentHeightChanged: {
+ if (scrollToBottom) {
+ scrollToBottom = false;
+ if (contentHeight > height)
+ contentY = contentHeight - height;
+ }
+ }
+
+ Column {
+ id: contentColumn
+ width: parent.width
+ spacing: 4
+
+ RowLayout {
+ height: _controlBaseHeight + anchors.margins * 2
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 4
+ anchors.rightMargin: 12
+
+ Image {
+ id: headerImage
+ source: _actionView.itemIcon !== "" ? _resDir + _actionView.itemIcon : ""
+ }
+
+ StyledLabel {
+ Layout.fillWidth: true
+ text: _actionView.itemText
+ color: _actionView.itemColor
+ }
+
+ StyledToolButton {
+ enabled: actionsList.currentIndex !== -1
+ enabledImage: "Action-Trash-Normal.png"
+ disabledImage: "Action-Trash-Disabled.png"
+ toolTipText: qsTr("Delete selected action")
+
+ onClicked: _actionView.deleteAction(actionsList.currentIndex)
+ }
+
+ StyledToolButton {
+ enabledImage: "add.png"
+ toolTipText: qsTr("Add new action")
+
+ onClicked: _actionView.addAction()
+ }
+ }
+ ListView {
+ id: actionsList
+ width: parent.width
+ height: count * _controlBaseHeight
+ clip: true
+
+ boundsBehavior: Flickable.StopAtBounds
+ model: _actionView.actionsModel
+
+ delegate: Rectangle {
+ id: delegateItem
+
+ width: actionsList.width
+ height: _controlBaseHeight
+ color: model.index === actionsList.currentIndex ? _selectionColor
+ : "transparent"
+
+ Row {
+ x: 10
+ y: 5
+ height: parent.height
+ width: parent.width - x
+ spacing: 4
+
+ Image {
+ id: visibilityIcon
+
+ source: model.visible ? _resDir + "Toggle-HideShow.png"
+ : _resDir + "Toggle-HideShow-disabled.png"
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: model.visible = !model.visible
+ }
+ }
+
+ StyledLabel {
+ text: model.description
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
+
+ onClicked: {
+ actionFlickable.scrollToBottom = false;
+ actionsList.currentIndex = model.index;
+ _actionView.setCurrentActionIndex(model.index);
+ if (mouse.button == Qt.LeftButton && mouse.x < visibilityIcon.width + 10)
+ model.visible = !model.visible;
+
+ if (mouse.button == Qt.RightButton) {
+ var updateMousePosition = mapToItem(actionsList, mouse.x, mouse.y)
+ _actionView.showContextMenu(updateMousePosition.x, updateMousePosition.y);
+ }
+ }
+ onDoubleClicked: {
+ actionFlickable.scrollToBottom = false;
+ if (mouse.button == Qt.LeftButton && mouse.x > visibilityIcon.width + 10) {
+ // Scroll down to bottom to show properties on double click
+ if (actionFlickable.contentHeight > actionFlickable.height) {
+ actionFlickable.contentY = (actionFlickable.contentHeight
+ - actionFlickable.height)
+ }
+ // Since loading new property fields takes a moment, we want
+ // to keep the view scrolled to bottom
+ // when the content height changes the next time.
+ actionFlickable.scrollToBottom = true;
+ }
+ }
+ }
+ }
+
+ onCountChanged: {
+ if (currentIndex >= count)
+ currentIndex = count - 1;
+ }
+
+ onCurrentIndexChanged: _actionView.setCurrentActionIndex(currentIndex);
+ }
+
+ StyledMenuSeparator {
+ leftPadding: 12
+ rightPadding: 12
+ }
+
+ Column {
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: childrenRect.height
+ visible: actionsList.currentIndex !== -1
+ spacing: 4
+
+ RowLayout {
+ x: 12
+ StyledLabel {
+ text: qsTr("Trigger Object")
+ }
+ BrowserCombo {
+ value: _actionView.triggerObjectName
+ onShowBrowser: activeBrowser = _actionView.showTriggerObjectBrowser(
+ mapToGlobal(width, 0));
+ }
+ }
+
+ RowLayout {
+ x: 12
+ StyledLabel {
+ text: qsTr("Event")
+ }
+ BrowserCombo {
+ value: _actionView.eventName
+ onShowBrowser: activeBrowser = _actionView.showEventBrowser(
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ StyledMenuSeparator {
+ visible: actionsList.currentIndex !== -1
+ leftPadding: 12
+ rightPadding: 12
+ }
+
+ Column {
+ visible: actionsList.currentIndex !== -1
+ width: parent.width
+ height: childrenRect.height
+ spacing: 4
+
+ RowLayout {
+ x: 12
+ StyledLabel {
+ text: qsTr("Target Object")
+ }
+
+ BrowserCombo {
+ value: _actionView.targetObjectName
+ onShowBrowser: activeBrowser = _actionView.showTargetObjectBrowser(
+ mapToGlobal(width, 0))
+ }
+ }
+
+ RowLayout {
+ x: 12
+ StyledLabel {
+ text: qsTr("Handler")
+ }
+
+ BrowserCombo {
+ value: _actionView.handlerName
+ onShowBrowser: activeBrowser = _actionView.showHandlerBrowser(
+ mapToGlobal(width, 0))
+ }
+ }
+
+ Component {
+ id: genericHandlerComponent
+
+ HandlerGenericText {
+ label: parent && parent.argument.name ? parent.argument.name : ""
+ value: parent && parent.argument.value ? parent.argument.value : ""
+
+ onEditingFinished: {
+ if (parent)
+ _actionView.setArgumentValue(parent.argument.handle, value)
+ }
+ }
+ }
+
+ Component {
+ id: floatHandlerComponent
+
+ HandlerGenericText {
+ label: parent && parent.argument.name ? parent.argument.name : ""
+ value: parent && parent.argument.value ? parent.argument.value : 0.0
+ validator: DoubleValidator {
+ decimals: 3
+ notation: DoubleValidator.StandardNotation
+ }
+
+ onEditingFinished: {
+ if (parent)
+ _actionView.setArgumentValue(parent.argument.handle, value)
+ }
+ }
+ }
+
+ Component {
+ id: signalHandlerComponent
+
+ HandlerGenericText {
+ label: parent && parent.argument.name ? parent.argument.name : ""
+ value: parent && parent.argument.value ? parent.argument.value : ""
+
+ onEditingFinished: {
+ if (parent)
+ _actionView.setArgumentValue(parent.argument.handle, value);
+ }
+ }
+ }
+
+ Component {
+ id: eventHandlerComponent
+
+ HandlerFireEvent {
+ label: parent && parent.argument.name ? parent.argument.name : ""
+ value: _actionView.firedEvent === "" ? qsTr("[Unknown Event]")
+ : _actionView.firedEvent
+
+ onShowBrowser: {
+ if (parent && parent.argument.handle) {
+ activeBrowser = _actionView.showEventBrowserForArgument(
+ parent.argument.handle, mapToGlobal(width, 0))
+ }
+ }
+ }
+ }
+
+ Component {
+ id: slideHandlerComponent
+
+ HandlerGoToSlide {
+ slideModel: _actionView.slideNames()
+ defaultSlideIndex: parent && parent.argument.value ? _actionView.slideNameToIndex(parent.argument.value)
+ : 0
+
+ onIndexChanged: {
+ if (parent && parent.argument.handle && currentSlide)
+ _actionView.setArgumentValue(parent.argument.handle, currentSlide)
+ }
+ }
+ }
+
+ Component {
+ id: checkboxHandlerComponent
+
+ HandlerGenericCheckbox {
+ label: parent && parent.argument.name ? parent.argument.name : ""
+ checked: parent && parent.argument.value ? parent.argument.value : false
+
+ onClicked: {
+ if (parent && parent.argument.handle)
+ _actionView.setArgumentValue(parent.argument.handle, !checked)
+ }
+ }
+ }
+
+ Component {
+ id: propertyHandlerComponent
+
+ HandlerProperty {
+ propertyModel: _actionView.propertyModel
+ defaultPropertyIndex: propertyModel ? propertyModel.defaultPropertyIndex : 0
+
+ onPropertySelected: {
+ if (parent && parent.argument.handle)
+ _actionView.setCurrentPropertyIndex(parent.argument.handle, index);
+ }
+ }
+ }
+
+ Repeater {
+ model: _actionView.handlerArguments.length
+
+ Loader {
+ x: 12
+
+ readonly property var argument:_actionView.handlerArguments[model.index]
+
+ onLoaded: {
+ // HandlerProperty does its own tab order handling
+ if (argument.type !== HandlerArgumentType.Property) {
+ // Dynamic actions use group 0.
+ // We assume there is always just one tabbable argument per action,
+ // and the rest are dependent types.
+ _tabOrderHandler.clear();
+ if (item.tabItem1 !== undefined) {
+ _tabOrderHandler.addItem(0, item.tabItem1)
+ if (item.tabItem2 !== undefined) {
+ _tabOrderHandler.addItem(0, item.tabItem2)
+ if (item.tabItem3 !== undefined)
+ _tabOrderHandler.addItem(0, item.tabItem3)
+ }
+ }
+ }
+ }
+
+ sourceComponent: {
+ const handlerType = argument.type;
+ switch (handlerType) {
+ case HandlerArgumentType.None:
+ switch (argument.completeType) {
+ case CompleteMetaDataType.Boolean:
+ return checkboxHandlerComponent;
+ case CompleteMetaDataType.Float:
+ return floatHandlerComponent;
+ default:
+ return genericHandlerComponent;
+ }
+ case HandlerArgumentType.Event:
+ return eventHandlerComponent;
+ case HandlerArgumentType.Property:
+ return propertyHandlerComponent;
+ case HandlerArgumentType.Dependent:
+ return null; // no UI for Dependent type, they are the value for a property
+ case HandlerArgumentType.Signal:
+ return signalHandlerComponent;
+ case HandlerArgumentType.Slide:
+ return slideHandlerComponent
+ default: console.warn("KDAB_TODO implement handler for type: ", handlerType)
+ }
+ return null;
+ }
+ }
+ }
+ }
+
+ StyledMenuSeparator {
+ visible: actionsList.count > 0 && actionsList.currentIndex !== -1
+ leftPadding: 12
+ rightPadding: 12
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/EventsBrowser.qml b/src/Authoring/Studio/Palettes/Action/EventsBrowser.qml
new file mode 100644
index 00000000..63973ec7
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/EventsBrowser.qml
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ ListView {
+ id: eventsList
+
+ Layout.margins: 10
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.minimumHeight: 80
+ Layout.preferredHeight: count * 20
+ Layout.preferredWidth: root.width
+
+ ScrollBar.vertical: ScrollBar {}
+
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+
+ model: _eventsBrowserView.model
+
+ delegate: Item {
+ id: delegateItem
+
+ readonly property bool isCategory: model.isCategory
+
+ x: isCategory ? 0 : 50
+ width: parent.width
+ height: model.parentExpanded ? 30 : 0
+ visible: height > 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.OutQuad
+ }
+ }
+
+ Row {
+ id: row
+
+ height: categoryIcon.height
+ spacing: 5
+
+ Image {
+ source: {
+ if (!delegateItem.isCategory)
+ return "";
+ model.expanded ? _resDir + "arrow_down.png"
+ : _resDir + "arrow.png";
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: model.expanded = !model.expanded
+ }
+ }
+
+ Rectangle {
+ height: name.height
+ width: categoryIcon.width + name.width + 10
+
+ color: model.index === eventsList.currentIndex ? _selectionColor
+ : "transparent"
+
+ Row {
+ id: textRow
+
+ spacing: 10
+ Image {
+ id: categoryIcon
+
+ source: model.icon
+ }
+
+ StyledLabel {
+ id: name
+ anchors.verticalCenter: textRow.verticalCenter
+ text: model.name
+ }
+ }
+
+ MouseArea {
+ id: delegateArea
+
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: eventsList.currentIndex = model.index
+ onEntered: itemDescription.text = model.description
+ onExited: itemDescription.text = ""
+ onDoubleClicked: {
+ eventsList.currentIndex = model.index;
+ _eventsBrowserView.close();
+ }
+ }
+ }
+ }
+
+ }
+ onCurrentIndexChanged: _eventsBrowserView.selection = currentIndex
+ }
+
+ StyledMenuSeparator {
+ bottomPadding: 0
+ }
+
+ Item {
+ Layout.fillWidth: true
+ Layout.preferredHeight: _controlBaseHeight + 4
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 2
+
+ color: _backgroundColor
+
+ StyledLabel {
+ id: itemDescription
+ leftPadding: 6
+ anchors.fill: parent
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/EventsBrowserView.cpp b/src/Authoring/Studio/Palettes/Action/EventsBrowserView.cpp
new file mode 100644
index 00000000..047a7b82
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/EventsBrowserView.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "EventsBrowserView.h"
+
+#include "CColor.h"
+#include "EventsModel.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "StudioPreferences.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+EventsBrowserView::EventsBrowserView(QWidget *parent) : QQuickWidget(parent)
+{
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &EventsBrowserView::initialize);
+}
+
+QAbstractItemModel *EventsBrowserView::model() const
+{
+ return m_model;
+}
+
+void EventsBrowserView::setModel(EventsModel *model)
+{
+ if (m_model != model) {
+ m_model = model;
+ Q_EMIT modelChanged();
+ }
+}
+
+QSize EventsBrowserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+UICDM::CDataModelHandle EventsBrowserView::selectedHandle() const
+{
+ const auto handleId = m_model->handleForRow(m_selection);
+ return handleId;
+}
+
+void EventsBrowserView::setSelection(int index)
+{
+ auto handleId = m_model->handleForRow(index);
+ if (!handleId.Valid()) {
+ m_selection = -1;
+ Q_EMIT selectionChanged();
+ } else if (m_selection != index) {
+ m_selection = index;
+ Q_EMIT selectionChanged();
+ }
+}
+
+void EventsBrowserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &EventsBrowserView::close);
+}
+
+void EventsBrowserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_eventsBrowserView"_L1, this);
+ rootContext()->setContextProperty("_resDir"_L1,
+ resourceImageUrl());
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Action/EventsBrowser.qml"_L1));
+}
+
diff --git a/src/Authoring/Studio/Palettes/Action/EventsBrowserView.h b/src/Authoring/Studio/Palettes/Action/EventsBrowserView.h
new file mode 100644
index 00000000..3902ca12
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/EventsBrowserView.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef EVENTSBROWSERVIEW_H
+#define EVENTSBROWSERVIEW_H
+
+#include <QQuickWidget>
+
+#include "UICDMHandles.h"
+
+class EventsModel;
+
+class QAbstractItemModel;
+
+class EventsBrowserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged FINAL)
+ Q_PROPERTY(int selection READ selection WRITE setSelection NOTIFY selectionChanged FINAL)
+public:
+ explicit EventsBrowserView(QWidget *parent = nullptr);
+
+ QAbstractItemModel *model() const;
+ void setModel(EventsModel *model);
+ QSize sizeHint() const override;
+ UICDM::CDataModelHandle selectedHandle() const;
+
+ int selection() const { return m_selection; }
+ void setSelection(int index);
+
+Q_SIGNALS:
+ void modelChanged();
+ void selectionChanged();
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void initialize();
+ EventsModel *m_model = nullptr;
+ QColor m_baseColor = QColor::fromRgb(75, 75, 75);
+ QColor m_selectColor;
+ int m_selection = -1;
+};
+
+#endif // EVENTSBROWSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Action/EventsModel.cpp b/src/Authoring/Studio/Palettes/Action/EventsModel.cpp
new file mode 100644
index 00000000..aa14ad81
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/EventsModel.cpp
@@ -0,0 +1,262 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "EventsModel.h"
+
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioUtils.h"
+#include "StudioApp.h"
+#include "UICDMStudioSystem.h"
+
+EventsModel::EventsModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+void EventsModel::setEventList(const UICDM::TEventHandleList &eventList)
+{
+ beginResetModel();
+
+ m_rowCount = 0;
+ m_events.clear();
+ m_categories.clear();
+
+ auto studioSystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ auto theBridge = studioSystem->GetClientDataModelBridge();
+ auto thePos = eventList.begin();
+ for (; thePos != eventList.end(); ++thePos) {
+ UICDM::SEventInfo theEvent = theBridge->GetEventInfo(*thePos);
+
+ CategoryInfo category;
+ category.name = QString::fromWCharArray(theEvent.m_Category.wide_str());
+ if (!m_events.contains(category.name)) {
+ UICDM::SCategoryInfo theCategoryMetaData = studioSystem->GetActionMetaData()
+ ->GetEventCategory(theEvent.m_Category);
+ category.icon = QString::fromWCharArray(theCategoryMetaData.m_Icon.wide_str());
+ category.highlightIcon = QString::fromWCharArray(theCategoryMetaData.m_HighlightIcon.wide_str());
+ category.description = QString::fromWCharArray(theCategoryMetaData.m_Description.wide_str());
+ m_categories.append(category);
+ m_rowCount++;
+ }
+
+ EventInfo eventInfo;
+ // Use the formal name to display, but if the formal name is not set, use the name instead
+ eventInfo.name = QString::fromWCharArray(theEvent.m_FormalName.wide_str());
+ if (eventInfo.name.isEmpty())
+ eventInfo.name = QString::fromWCharArray(theEvent.m_Name.wide_str());
+ eventInfo.handle = *thePos;
+
+ eventInfo.description = QString::fromWCharArray(theEvent.m_Description.wide_str());
+ m_events[category.name].append(eventInfo);
+ m_rowCount++;
+
+ //KDAB_TODO set the selection to the current event
+ }
+
+ endResetModel();
+}
+
+void EventsModel::setHandlerList(const UICDM::THandlerHandleList &handlerList)
+{
+ beginResetModel();
+ m_rowCount = 0;
+ m_events.clear();
+ m_categories.clear();
+
+ auto studioSystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ auto theBridge = studioSystem->GetClientDataModelBridge();
+ auto thePos = handlerList.begin();
+ for (; thePos != handlerList.end(); ++thePos) {
+ UICDM::SHandlerInfo handlerInfo = theBridge->GetHandlerInfo(*thePos);
+
+ CategoryInfo category;
+ category.name = QString::fromWCharArray(handlerInfo.m_Category.wide_str());
+ if (!m_events.contains(category.name)) {
+ UICDM::SCategoryInfo theCategoryMetaData = studioSystem->GetActionMetaData()
+ ->GetHandlerCategory(handlerInfo.m_Category);
+ category.icon = QString::fromWCharArray(theCategoryMetaData.m_Icon.wide_str());
+ category.highlightIcon = QString::fromWCharArray(theCategoryMetaData.m_HighlightIcon.wide_str());
+ category.description = QString::fromWCharArray(theCategoryMetaData.m_Description.wide_str());
+ m_categories.append(category);
+ m_rowCount++;
+ }
+
+ EventInfo eventInfo;
+ // Use the formal name to display, but if the formal name is not set, use the name instead
+ eventInfo.name = QString::fromWCharArray(handlerInfo.m_FormalName.wide_str());
+ if (eventInfo.name.isEmpty())
+ eventInfo.name = QString::fromWCharArray(handlerInfo.m_Name.wide_str());
+ eventInfo.handle = *thePos;
+
+ eventInfo.description = QString::fromWCharArray(handlerInfo.m_Description.wide_str());
+ m_events[category.name].append(eventInfo);
+ m_rowCount++;
+
+ //KDAB_TODO set the selection to the current event
+ }
+
+ endResetModel();
+}
+
+int EventsModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_rowCount;
+}
+
+QVariant EventsModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return {};
+
+ const auto row = index.row();
+ auto category = categoryForRow(row);
+
+ bool isCategory = category.isValid();
+ EventInfo event;
+ if (!isCategory)
+ event = eventForRow(row);
+
+ switch (role) {
+ case NameRole:
+ return isCategory ? category.name : event.name;
+ case DescriptionRole:
+ return isCategory ? category.description: event.description;
+ case IconRole:
+ return isCategory ? resourceImageUrl() + category.icon : "";
+ case HighlightedIconRole:
+ return isCategory ? resourceImageUrl() + category.highlightIcon : "";
+ case ExpandedRole:
+ return isCategory ? category.expanded : false;
+ case ParentExpandedRole: {
+ if (isCategory)
+ return true;
+ for (int i = row - 1; i >= 0; i--) {
+ auto parentCategory = categoryForRow(i);
+ if (parentCategory.isValid())
+ return parentCategory.expanded;
+ }
+ return false;
+ }
+ case IsCategoryRole:
+ return isCategory;
+ }
+
+ return QVariant();
+}
+
+bool EventsModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (role == ExpandedRole) {
+ int catRow = categoryRowForRow(index.row());
+ if (catRow != -1) {
+ auto category = &m_categories[catRow];
+ category->expanded = value.toBool();
+ Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {});
+ return true;
+ }
+ }
+ return false;
+}
+
+QHash<int, QByteArray> EventsModel::roleNames() const
+{
+ auto names = QAbstractItemModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(DescriptionRole, "description");
+ names.insert(IconRole, "icon");
+ names.insert(HighlightedIconRole, "highlightedIcon");
+ names.insert(IsCategoryRole, "isCategory");
+ names.insert(ExpandedRole, "expanded");
+ names.insert(ParentExpandedRole, "parentExpanded");
+
+ return names;
+}
+
+UICDM::CDataModelHandle EventsModel::handleForRow(int row) const
+{
+ if (row < 0 || row >= m_rowCount)
+ return {};
+
+ auto event = eventForRow(row);
+ if (event.isValid())
+ return event.handle;
+
+ return {};
+}
+
+EventsModel::CategoryInfo EventsModel::categoryForRow(int row) const
+{
+ int i = 0;
+ for (const auto &category: m_categories) {
+ if (i == row)
+ return category;
+ i += m_events[category.name].size();
+ i++;
+ }
+
+ return {};
+}
+
+int EventsModel::categoryRowForRow(int row) const
+{
+ int i = 0;
+ int catRow = 0;
+ for (const auto &category: m_categories) {
+ if (i == row)
+ return catRow;
+ i += m_events[category.name].size();
+ i++;
+ catRow++;
+ }
+
+ return -1;
+}
+
+EventsModel::EventInfo EventsModel::eventForRow(int row) const
+{
+ if (row == 0) // first line is not an event, but a category
+ return {};
+
+ int i = 0;
+ for (const auto &category: m_categories) {
+ i++;
+ const auto events = m_events[category.name];
+ const int index = (row - i);
+ if (row < i + events.size() && (index >= 0) ) {
+ return events[index];
+ }
+ i += events.size();
+ }
+
+ return {};
+}
diff --git a/src/Authoring/Studio/Palettes/Action/EventsModel.h b/src/Authoring/Studio/Palettes/Action/EventsModel.h
new file mode 100644
index 00000000..ef458877
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/EventsModel.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EVENTSMODEL_H
+#define EVENTSMODEL_H
+
+#include <QAbstractListModel>
+
+#include "UICDMHandles.h"
+
+/** Model for both action events and action handlers */
+class EventsModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit EventsModel(QObject *parent = nullptr);
+
+ void setEventList(const UICDM::TEventHandleList &eventList);
+ void setHandlerList(const UICDM::THandlerHandleList &handlerList);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::EditRole) override;
+
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ DescriptionRole = Qt::UserRole + 1,
+ IconRole,
+ HighlightedIconRole,
+ ExpandedRole,
+ ParentExpandedRole,
+ IsCategoryRole
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ UICDM::CDataModelHandle handleForRow(int row) const;
+
+private:
+ struct EventInfo {
+ UICDM::CDataModelHandle handle;
+ QString name;
+ QString description;
+
+ bool isValid() const { return handle.Valid(); }
+ };
+
+ struct CategoryInfo {
+ QString name;
+ QString icon;
+ QString description;
+ QString highlightIcon;
+ bool expanded = true;
+
+ bool isValid() const { return !name.isEmpty(); }
+ };
+
+ CategoryInfo categoryForRow(int row) const;
+ int categoryRowForRow(int row) const;
+ EventInfo eventForRow(int row) const;
+
+ QHash<QString, QVector<EventInfo> > m_events;
+ QVector<CategoryInfo> m_categories;
+ int m_rowCount = 0;
+};
+
+#endif // EVENTSMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerEmitSignal.qml b/src/Authoring/Studio/Palettes/Action/HandlerEmitSignal.qml
new file mode 100644
index 00000000..5917a9bd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerEmitSignal.qml
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias value: textField.text
+ property Item tabItem1: textfield
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Signal Name")
+ }
+
+ StyledTextField {
+ id: textField
+ Layout.preferredWidth: _valueWidth
+ }
+}
+
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerFireEvent.qml b/src/Authoring/Studio/Palettes/Action/HandlerFireEvent.qml
new file mode 100644
index 00000000..e92d2265
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerFireEvent.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias value: comboField.value
+ property alias activeBrowser: comboField.activeBrowser
+
+ signal showBrowser
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Event")
+ }
+
+ BrowserCombo {
+ id: comboField
+ Layout.preferredWidth: _valueWidth
+ value: qsTr("[Unknown Event]")
+ onShowBrowser: root.showBrowser()
+ }
+}
+
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerGenericBaseColor.qml b/src/Authoring/Studio/Palettes/Action/HandlerGenericBaseColor.qml
new file mode 100644
index 00000000..dbe097cb
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerGenericBaseColor.qml
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.3
+
+RowLayout {
+ id: root
+
+ property alias color: rect.color
+ property alias selectedColor: colorDialog.color
+
+ signal colorSelected()
+
+ Rectangle {
+ id: rect
+
+ width: _valueWidth / 4
+ height: _controlBaseHeight
+
+ border {
+ width: 1
+ color: _studioColor2
+ }
+
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ onClicked: colorDialog.open()
+ }
+
+ Image {
+ id: img
+ x: parent.width - sourceSize.width - 3
+ y: (parent.height - sourceSize.height) / 2
+ source: _resDir + "arrow_down.png"
+ }
+ }
+
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ ColorDialog {
+ id: colorDialog
+
+ color: rect.color
+
+ onAccepted: root.colorSelected()
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerGenericCheckbox.qml b/src/Authoring/Studio/Palettes/Action/HandlerGenericCheckbox.qml
new file mode 100644
index 00000000..4b3b0ba4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerGenericCheckbox.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property bool checked: false
+ property alias label: labelField.text
+
+ signal clicked()
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Pause")
+ }
+
+ Image {
+ source: _resDir + (checked ? "checkbox-checked.png" : "checkbox-unchecked.png")
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: root.clicked()
+ }
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerGenericColor.qml b/src/Authoring/Studio/Palettes/Action/HandlerGenericColor.qml
new file mode 100644
index 00000000..378aa7b0
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerGenericColor.qml
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias color: handlerGenericColor.color
+ property alias selectedColor: handlerGenericColor.selectedColor
+
+ signal colorSelected()
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("New Value")
+ }
+
+ HandlerGenericBaseColor {
+ id: handlerGenericColor
+
+ onColorSelected: root.colorSelected();
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerGenericText.qml b/src/Authoring/Studio/Palettes/Action/HandlerGenericText.qml
new file mode 100644
index 00000000..e33e170f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerGenericText.qml
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias value: textField.text
+ property alias validator: textField.validator
+ property Item tabItem1: textField
+
+ signal editingFinished()
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Argument")
+ }
+
+ StyledTextField {
+ id: textField
+ onEditingFinished: root.editingFinished()
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerGoToSlide.qml b/src/Authoring/Studio/Palettes/Action/HandlerGoToSlide.qml
new file mode 100644
index 00000000..ef76dfcb
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerGoToSlide.qml
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias slideModel: comboSlide.model
+ property string currentSlide
+ property int defaultSlideIndex: 0
+
+ signal indexChanged()
+
+ onDefaultSlideIndexChanged: comboSlide.currentIndex = defaultSlideIndex
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Slide")
+ }
+
+ StyledComboBox {
+ id: comboSlide
+ Layout.preferredWidth: _valueWidth
+ model: slideModel
+
+ onCurrentIndexChanged: {
+ currentSlide = comboSlide.textAt(currentIndex);
+ indexChanged();
+ }
+ }
+}
+
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerMultilineText.qml b/src/Authoring/Studio/Palettes/Action/HandlerMultilineText.qml
new file mode 100644
index 00000000..2c7704a4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerMultilineText.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias value: textArea.text
+ property Item tabItem1: textArea
+
+ signal editingFinished()
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("Argument")
+ }
+
+ Flickable {
+ Layout.preferredWidth: _valueWidth
+ Layout.preferredHeight: textArea.height
+
+ ScrollBar.vertical: ScrollBar {}
+
+ TextArea.flickable: TextArea {
+ id: textArea
+
+ horizontalAlignment: TextInput.AlignLeft
+ verticalAlignment: TextInput.AlignVCenter
+ implicitWidth: _valueWidth
+ font.pixelSize: _fontSize
+ color: _textColor
+
+ topPadding: 6
+ bottomPadding: 6
+ rightPadding: 6
+
+ wrapMode: TextArea.Wrap
+ background: Rectangle {
+ color: textArea.enabled ? _studioColor2 : "transparent"
+ border.width: textArea.activeFocus ? 1 : 0
+ border.color: textArea.activeFocus ? _selectionColor : _disabledColor
+ }
+
+ onEditingFinished: root.editingFinished()
+ }
+
+ MouseArea {
+ id: mouseArea
+
+ anchors.fill: parent
+ onPressed: parent.forceActiveFocus()
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerProperty.qml b/src/Authoring/Studio/Palettes/Action/HandlerProperty.qml
new file mode 100644
index 00000000..52d6265a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerProperty.qml
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import Qt3DStudio 1.0
+import "../controls"
+
+ColumnLayout {
+ id: root
+
+ property alias propertyModel: propertyCombo.model
+ property int defaultPropertyIndex: 0
+
+ signal propertySelected(int index)
+
+ onDefaultPropertyIndexChanged: propertyCombo.currentIndex = defaultPropertyIndex
+
+ RowLayout {
+
+ Layout.fillWidth: true
+
+ StyledLabel {
+ text: qsTr("Property")
+ }
+
+ StyledComboBox {
+ id: propertyCombo
+ textRole: "name"
+ onCurrentIndexChanged: root.propertySelected(currentIndex)
+ onModelChanged: currentIndex = root.defaultPropertyIndex
+ }
+ }
+
+ Component {
+ id: multiLineComponent
+
+ HandlerMultilineText {
+ readonly property var actionProperty: parent ? _actionView.property : null
+
+ label: parent ? parent.label : ""
+ value: propertyModel && actionProperty && actionProperty.type === DataModelDataType.String
+ ? propertyModel.value : ""
+ onEditingFinished: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+ }
+ }
+
+ Component {
+ id: fontSizeComponent
+
+ HandlerPropertyCombo {
+ readonly property var actionProperty: parent ? _actionView.property : null
+
+ label: parent ? parent.label : ""
+ comboModel: ["8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72", "96", "120"];
+
+ onValueChanged: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+ }
+ }
+
+ Component {
+ id: xyzPropertyComponent
+
+ HandlerPropertyXYZ {
+ readonly property var propValue: propertyModel && propertyModel.value.x ?
+ propertyModel.value
+ : undefined
+ label: parent ? parent.label : ""
+ valueX: propValue !== undefined ? propValue.x : 0
+ valueY: propValue !== undefined ? propValue.y : 0
+ valueZ: propValue !== undefined ? propValue.z : 0
+
+ onEditingFinished: _actionView.setArgumentValue(propertyModel.valueHandle,
+ Qt.vector3d(valueX, valueY, valueZ))
+ }
+ }
+
+ Component {
+ id: sliderPropertyComponent
+
+ HandlerPropertySlider {
+ readonly property var actionProperty: parent ? _actionView.property : null
+
+ sliderMin: actionProperty ? actionProperty.min : 0
+ sliderMax: actionProperty ? actionProperty.max : 100
+ intSlider: actionProperty ? actionProperty.type === DataModelDataType.Long : false
+ value: propertyModel ? propertyModel.value : 0
+
+ label: parent ? parent.label : ""
+
+ onValueChanged: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+ }
+ }
+
+ Component {
+ id: comboPropertyComponent
+
+ HandlerPropertyCombo {
+ readonly property var actionProperty: parent ? _actionView.property : null
+
+ label: parent ? parent.label : ""
+ comboModel: actionProperty ? actionProperty.possibleValues : null
+
+ onValueChanged: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+
+ }
+ }
+
+ Component {
+ id: booleanComponent
+
+ HandlerGenericCheckbox {
+ label: parent ? parent.label : ""
+ checked: propertyModel ? propertyModel.value : false
+
+ onClicked: {
+ _actionView.setArgumentValue(propertyModel.valueHandle, !checked)
+ }
+ }
+ }
+
+ Component {
+ id: colorBox
+
+ HandlerGenericColor {
+ readonly property var propValue: propertyModel ? propertyModel.value : undefined
+
+ label: parent ? parent.label : ""
+ color: propValue ? Qt.rgba(propValue.x, propValue.y, propValue.z, 1) : "black"
+ onColorSelected: {
+ _actionView.setArgumentValue(propertyModel.valueHandle, selectedColor)
+ }
+ }
+ }
+
+ Component {
+ id: genericTextComponent
+
+ HandlerGenericText {
+ label: parent ? parent.label : ""
+ value: propertyModel ? propertyModel.value : ""
+ onEditingFinished: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+ }
+ }
+
+ Component {
+ id: floatPropertyComponent
+
+ HandlerGenericText {
+ label: parent ? parent.label : ""
+ value: propertyModel ? propertyModel.value : ""
+ validator: DoubleValidator {
+ decimals: 3
+ notation: DoubleValidator.StandardNotation
+ }
+
+ onEditingFinished: _actionView.setArgumentValue(propertyModel.valueHandle, value)
+ }
+ }
+
+ Loader {
+ readonly property string label: qsTr("New Value")
+ readonly property var actionProperty: _actionView.property
+
+ Layout.fillWidth: true
+
+ onLoaded: {
+ _tabOrderHandler.clear();
+ if (item.tabItem1 !== undefined) {
+ _tabOrderHandler.addItem(0, item.tabItem1)
+ if (item.tabItem2 !== undefined) {
+ _tabOrderHandler.addItem(0, item.tabItem2)
+ if (item.tabItem3 !== undefined)
+ _tabOrderHandler.addItem(0, item.tabItem3)
+ }
+ }
+ }
+
+ sourceComponent: {
+ // KDAB_TODO Handle additionaltype
+ switch (actionProperty.type) {
+ case DataModelDataType.Float:
+ switch (actionProperty.additionalType) {
+ case AdditionalMetaDataType.FontSize:
+ return fontSizeComponent;
+ case AdditionalMetaDataType.Range:
+ return sliderPropertyComponent;
+ default:
+ return floatPropertyComponent;
+ }
+ case DataModelDataType.Long:
+ return sliderPropertyComponent;
+ case DataModelDataType.Float3:
+ switch (actionProperty.additionalType) {
+ case AdditionalMetaDataType.None:
+ case AdditionalMetaDataType.Rotation:
+ return xyzPropertyComponent;
+ case AdditionalMetaDataType.Color:
+ return colorBox;
+ default:
+ console.warn("KDAB_TODO implement property handler for additional typeDataModelDataType.Float3: ", actionProperty.additionalType);
+ return xyzPropertyComponent;
+ }
+ case DataModelDataType.String:
+ switch (actionProperty.additionalType) {
+ case AdditionalMetaDataType.StringList:
+ return comboPropertyComponent;
+ case AdditionalMetaDataType.MultiLine:
+ return multiLineComponent;
+ case AdditionalMetaDataType.Font:
+ return comboPropertyComponent;
+ case AdditionalMetaDataType.Import:
+ case AdditionalMetaDataType.Renderable:
+ return genericTextComponent;
+ default:
+ console.warn("KDAB_TODO implement property handler for additional type: ", actionProperty.additionalType)
+ return null;
+ }
+ case DataModelDataType.Bool:
+ return booleanComponent;
+ case DataModelDataType.None:
+ return null;
+ default: console.warn("KDAB_TODO implement property handler for type: ", actionProperty.type)
+
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseSlider.qml b/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseSlider.qml
new file mode 100644
index 00000000..8fcf3511
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseSlider.qml
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+/*
+* Use for: Opacity, Edge Tesselation Value, Inner Tesselation Value ...
+* For the latter two set sliderMax to 64
+*/
+
+Row {
+ id: root
+
+ property alias value: slider.value
+ property int intValue: slider.value
+ property alias sliderMin: slider.from
+ property alias sliderMax: slider.to
+ property bool intSlider: false
+ property int decimalSlider: 3
+ property Item tabItem1: textField
+
+ signal editingFinished
+ signal sliderMoved
+ signal editingStarted
+
+ spacing: 5
+ width: _valueWidth
+
+ Slider {
+ id: slider
+
+ leftPadding: 0
+
+ background: Rectangle {
+ x: slider.leftPadding
+ y: slider.topPadding + slider.availableHeight / 2 - height / 2
+ implicitWidth: _valueWidth / 2 - 5
+ implicitHeight: 6
+ height: implicitHeight
+ radius: 2
+ color: _studioColor2
+ }
+ handle: Rectangle {
+ x: slider.leftPadding + slider.visualPosition * slider.availableWidth
+ y: slider.topPadding + slider.availableHeight / 2 - height / 2
+ implicitWidth: 6
+ implicitHeight: 12
+ color: _studioColor3
+ radius: 2
+ }
+
+ from: 0
+ to: 100
+ stepSize: 2
+
+ onMoved: {
+ if (!rateLimiter.running) {
+ rateLimiter.start();
+ }
+ }
+
+ onPressedChanged: {
+ if (pressed)
+ root.editingStarted();
+ else
+ root.editingFinished();
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.NoButton
+
+ onWheel: {
+ var delta = (wheel.angleDelta.x != 0) ? wheel.angleDelta.x
+ : wheel.angleDelta.y;
+
+ if (delta > 0) {
+ slider.increase();
+ } else {
+ slider.decrease();
+ }
+ if (!rateLimiter.running) {
+ rateLimiter.start();
+ }
+ }
+ }
+ }
+
+ Timer {
+ id: rateLimiter
+ interval: 50
+ onTriggered: {
+ root.sliderMoved();
+ }
+ }
+
+ DoubleValidator {
+ id: doubleValidator
+
+ decimals: decimalSlider
+ bottom: slider.from
+ top: slider.to
+ locale: "C"
+ }
+
+ IntValidator {
+ id: intValidator
+
+ bottom: slider.from
+ top: slider.to
+ }
+
+ StyledTextField {
+ id: textField
+
+ height: _controlBaseHeight
+ width: _valueWidth / 2
+ text: intSlider ? slider.value.toFixed(0) : slider.value.toFixed(decimalSlider)
+
+ validator: intSlider ? intValidator : doubleValidator
+
+ onActiveFocusChanged: {
+ if (activeFocus)
+ root.editingStarted();
+ }
+
+ onEditingFinished: {
+ if (textField.text > sliderMax)
+ textField.text = sliderMax
+ else if (textField.text < sliderMin)
+ textField.text = sliderMin
+ slider.value = textField.text
+ root.editingFinished()
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseXYZ.qml b/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseXYZ.qml
new file mode 100644
index 00000000..fcabf271
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerPropertyBaseXYZ.qml
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+/* Use for: Position, Rotation, Scale, Pivot ... */
+
+GridLayout {
+ id: root
+
+ property alias valueX: textFieldX.text
+ property alias valueY: textFieldY.text
+ property alias valueZ: textFieldZ.text
+ property int numberOfDecimal: 3
+ property Item tabItem1: textFieldX
+ property Item tabItem2: textFieldY
+ property Item tabItem3: textFieldZ
+
+ columns: 2
+ rowSpacing: 1
+
+ signal editingFinished
+
+ StyledLabel {
+ Layout.preferredWidth: 10
+ text: qsTr("X")
+ }
+
+ FloatTextField {
+ id: textFieldX
+
+ decimalValue: numberOfDecimal
+ onEditingFinished: root.editingFinished()
+ onWheelEventFinished: root.editingFinished()
+ }
+
+ StyledLabel {
+ Layout.preferredWidth: 10
+ text: qsTr("Y")
+ }
+
+ FloatTextField {
+ id: textFieldY
+
+ decimalValue: numberOfDecimal
+ onEditingFinished: root.editingFinished()
+ onWheelEventFinished: root.editingFinished()
+ }
+
+ StyledLabel {
+ Layout.preferredWidth: 10
+ text: qsTr("Z")
+ }
+
+ FloatTextField {
+ id: textFieldZ
+
+ decimalValue: numberOfDecimal
+ onEditingFinished: root.editingFinished()
+ onWheelEventFinished: root.editingFinished()
+ }
+}
+
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerPropertyCombo.qml b/src/Authoring/Studio/Palettes/Action/HandlerPropertyCombo.qml
new file mode 100644
index 00000000..1e60cf44
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerPropertyCombo.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+/* Use for Tesselation mode, Horizontal alignment, Vertical alignment ... */
+
+RowLayout {
+ id: root
+
+ property alias label: labelField.text
+ property alias comboModel : comboBox.model
+ property alias comboTextRole: comboBox.textRole
+ property string value
+
+ StyledLabel {
+ id: labelField
+ text: qsTr("New Value")
+ }
+
+ StyledComboBox {
+ id: comboBox
+
+ Layout.fillWidth: true
+ onCurrentIndexChanged: value = comboBox.textAt(currentIndex)
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerPropertySlider.qml b/src/Authoring/Studio/Palettes/Action/HandlerPropertySlider.qml
new file mode 100644
index 00000000..aecdcfd4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerPropertySlider.qml
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+/*
+* Use for: Opacity, Edge Tesselation Value, Inner Tesselation Value ...
+* For the latter two set sliderMax to 64
+*/
+
+GridLayout {
+ id: root
+
+ property alias value: propertySlider.value
+ property alias sliderMin: propertySlider.sliderMin
+ property alias sliderMax: propertySlider.sliderMax
+ property alias label: labelItem.text
+ property bool intSlider: propertySlider.intSlider
+ property int decimalSlider: propertySlider.decimalSlider
+ property alias tabItem1: propertySlider.tabItem1
+
+ signal editingFinished
+
+ columns: 3
+
+ StyledLabel {
+ id: labelItem
+ text: label
+ }
+
+ HandlerPropertyBaseSlider {
+ id: propertySlider
+ // proxy the signal upwards
+ onEditingFinished: root.editingFinished()
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/HandlerPropertyXYZ.qml b/src/Authoring/Studio/Palettes/Action/HandlerPropertyXYZ.qml
new file mode 100644
index 00000000..eef611ce
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/HandlerPropertyXYZ.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+/* Use for: Position, Rotation, Scale, Pivot ... */
+
+RowLayout {
+ id: root
+
+ property alias valueX: propertyXYZ.valueX
+ property alias valueY: propertyXYZ.valueY
+ property alias valueZ: propertyXYZ.valueZ
+ property alias label: labelItem.text
+ property alias tabItem1: propertyXYZ.tabItem1
+ property alias tabItem2: propertyXYZ.tabItem2
+ property alias tabItem3: propertyXYZ.tabItem3
+
+ signal editingFinished
+
+ StyledLabel {
+ id: labelItem
+ Layout.alignment: Qt.AlignTop | Qt.AlignLeft
+ text: qsTr("New Value")
+ }
+
+ HandlerPropertyBaseXYZ {
+ id: propertyXYZ
+ Layout.alignment: Qt.AlignRight
+
+ onEditingFinished: {
+ root.editingFinished();
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/PropertyModel.cpp b/src/Authoring/Studio/Palettes/Action/PropertyModel.cpp
new file mode 100644
index 00000000..5ee896c8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/PropertyModel.cpp
@@ -0,0 +1,219 @@
+#include "PropertyModel.h"
+
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+
+#include "UICDMActionCore.h"
+#include "UICDMActionInfo.h"
+#include "UICDMDataCore.h"
+#include "UICDMMetaData.h"
+#include "UICDMStudioSystem.h"
+
+
+PropertyModel::PropertyModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+}
+
+void PropertyModel::setAction(const UICDM::CUICDMActionHandle &action)
+{
+ beginResetModel();
+ m_action = action;
+ m_valueHandle = 0;
+ m_nameHandle = 0;
+ m_properties.clear();
+
+ if (action.Valid()) {
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ auto propertySystem = studioSystem->GetPropertySystem();
+ auto bridge = studioSystem->GetClientDataModelBridge();
+
+ auto actionInfo = studioSystem->GetActionCore()->GetActionInfo(action);
+
+ UICDM::IMetaData &metaData(*studioSystem->GetActionMetaData());
+ UICDM::TMetaDataPropertyHandleList metaProperties;
+ const auto instance = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject);
+ metaData.GetMetaDataProperties(instance, metaProperties);
+
+ for (const auto &metaProperty: metaProperties) {
+ auto propertyMetaInfo = metaData.GetMetaDataPropertyInfo(metaProperty);
+ if (propertyMetaInfo->m_IsHidden == false) {
+ PropertyInfo property;
+ property.m_handle = propertyMetaInfo->m_Property;
+ property.m_name = QString::fromWCharArray(propertySystem->GetFormalName(instance, property.m_handle).wide_str());
+ property.m_nameId = QString::fromWCharArray(propertySystem->GetName(property.m_handle).wide_str());
+ property.m_type = propertyMetaInfo->GetDataType();
+ property.m_additionalType = propertyMetaInfo->GetAdditionalType();
+
+ const auto additionalMetaDataType = propertySystem->GetAdditionalMetaDataType(instance, property.m_handle);
+ switch (additionalMetaDataType) {
+ case UICDM::AdditionalMetaDataType::Range: {
+ const UICDM::TMetaDataData &metaDataData =
+ propertySystem->GetAdditionalMetaDataData(instance, property.m_handle);
+ UICDM::SMetaDataRange minMax = UICDM::get<UICDM::SMetaDataRange>(metaDataData);
+ property.m_min = minMax.m_Min;
+ property.m_max = minMax.m_Max;
+ break;
+ }
+ case UICDM::AdditionalMetaDataType::StringList: {
+ const UICDM::TMetaDataData &metaDataData =
+ propertySystem->GetAdditionalMetaDataData(instance, property.m_handle);
+ auto values = UICDM::get<UICDM::TMetaDataStringList>(metaDataData);
+ QStringList possibleValues;
+ for (const auto &value: values) {
+ possibleValues.append(QString::fromWCharArray(value.wide_str()));
+ }
+ property.m_possibleValues = possibleValues;
+ break;
+ }
+ case UICDM::AdditionalMetaDataType::Font: {
+ std::vector<Q3DStudio::CString> fontNames;
+ doc->GetProjectFonts(fontNames);
+ QStringList possibleValues;
+ for (const auto &fontName: fontNames) {
+ possibleValues.append(fontName.toQString());
+ }
+ property.m_possibleValues = possibleValues;
+ break;
+ }
+ default:;
+ }
+ m_properties.append(property);
+ }
+ }
+ }
+ endResetModel();
+
+ Q_EMIT valueHandleChanged();
+}
+
+void PropertyModel::setNameHandle(const UICDM::CUICDMHandlerArgHandle &handle)
+{
+ m_nameHandle = handle;
+}
+
+void PropertyModel::setValueHandle(const UICDM::CUICDMHandlerArgHandle &handle)
+{
+ m_valueHandle = handle;
+
+ updateDefaultPropertyIndex();
+ updateValue();
+ if (m_valueHandle != handle)
+ Q_EMIT valueHandleChanged();
+}
+
+int PropertyModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_properties.size();
+}
+
+
+QVariant PropertyModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return {};
+
+ const auto property = m_properties.at(index.row());
+
+ switch (role)
+ {
+ case NameRole:
+ return property.m_name;
+ case HandleRole:
+ return property.m_handle.GetHandleValue();
+
+ default:
+ return {};
+ }
+
+ return QVariant();
+}
+
+QHash<int, QByteArray> PropertyModel::roleNames() const
+{
+ auto names = QAbstractItemModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(HandleRole, "handle");
+
+ return names;
+}
+
+PropertyInfo PropertyModel::property(int index) const
+{
+ if (index < 0 || index >= m_properties.size() )
+ return {};
+ return m_properties[index];
+}
+
+int PropertyModel::valueHandle() const
+{
+ return m_valueHandle;
+}
+
+QVariant PropertyModel::value() const
+{
+ return m_value;
+}
+
+void PropertyModel::updateDefaultPropertyIndex()
+{
+ if (!m_nameHandle.Valid()) {
+ m_defaultPropertyIndex = -1;
+ Q_EMIT defaultPropertyIndexChanged();
+ return;
+ }
+
+ UICDM::SValue sValue;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ studioSystem->GetActionCore()->GetHandlerArgumentValue(m_nameHandle, sValue);
+
+ if (sValue.getType() != UICDM::DataModelDataType::String) {
+ m_defaultPropertyIndex = -1;
+ Q_EMIT defaultPropertyIndexChanged();
+ return;
+ }
+
+ auto propertyName = UICDM::get<QString>(sValue);
+ auto iter = std::find_if(m_properties.constBegin(), m_properties.constEnd(),
+ [&propertyName](const PropertyInfo &info)
+ {
+ return (info.m_nameId == propertyName);
+ });
+
+ auto index = std::distance(m_properties.constBegin(), iter);
+
+ if (m_defaultPropertyIndex != index) {
+ m_defaultPropertyIndex = index;
+ Q_EMIT defaultPropertyIndexChanged();
+ }
+}
+
+int PropertyModel::defaultPropertyIndex() const
+{
+ return m_defaultPropertyIndex;
+}
+
+void PropertyModel::updateValue()
+{
+ const auto oldValue = m_value;
+ if (!m_valueHandle.Valid()) {
+ m_value.clear();
+ } else {
+ UICDM::SValue sValue;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ studioSystem->GetActionCore()->GetHandlerArgumentValue(m_valueHandle, sValue);
+ m_value = sValue.toQVariant();
+ }
+ if (oldValue != m_value) {
+
+ Q_EMIT valueChanged();
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Action/PropertyModel.h b/src/Authoring/Studio/Palettes/Action/PropertyModel.h
new file mode 100644
index 00000000..293e682e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Action/PropertyModel.h
@@ -0,0 +1,80 @@
+#ifndef PROPERTYMODEL_H
+#define PROPERTYMODEL_H
+
+#include <QAbstractListModel>
+
+#include "UICDMHandles.h"
+#include "UICDMDataTypes.h"
+#include "UICDMMetaDataTypes.h"
+
+struct PropertyInfo {
+ Q_PROPERTY(QString name MEMBER m_name CONSTANT FINAL)
+ Q_PROPERTY(float min MEMBER m_min CONSTANT FINAL)
+ Q_PROPERTY(float max MEMBER m_max CONSTANT FINAL)
+ Q_PROPERTY(UICDM::DataModelDataType::Value type MEMBER m_type CONSTANT FINAL)
+ Q_PROPERTY(UICDM::AdditionalMetaDataType::Value additionalType MEMBER m_additionalType CONSTANT FINAL)
+ Q_PROPERTY(QStringList possibleValues MEMBER m_possibleValues CONSTANT FINAL)
+
+ UICDM::CUICDMPropertyHandle m_handle;
+ QString m_name;
+ QString m_nameId;
+ UICDM::DataModelDataType::Value m_type;
+ UICDM::AdditionalMetaDataType::Value m_additionalType;
+ QStringList m_possibleValues;
+ float m_min = 0.0f;
+ float m_max = 0.0f;
+
+ Q_GADGET
+};
+
+class PropertyModel : public QAbstractListModel
+{
+ Q_PROPERTY(int valueHandle READ valueHandle NOTIFY valueHandleChanged FINAL)
+ Q_PROPERTY(QVariant value READ value NOTIFY valueChanged FINAL)
+ Q_PROPERTY(int defaultPropertyIndex READ defaultPropertyIndex NOTIFY defaultPropertyIndexChanged FINAL)
+ Q_OBJECT
+
+public:
+ explicit PropertyModel(QObject *parent = nullptr);
+
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ HandleRole = Qt::UserRole + 1
+ };
+
+ void setAction(const UICDM::CUICDMActionHandle &action);
+ void setNameHandle(const UICDM::CUICDMHandlerArgHandle &valueHandle);
+ void setValueHandle(const UICDM::CUICDMHandlerArgHandle &valueHandle);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ PropertyInfo property(int index) const;
+ UICDM::CUICDMActionHandle action() const { return m_action; }
+ int valueHandle() const;
+
+ QVariant value() const;
+ int defaultPropertyIndex() const;
+
+Q_SIGNALS:
+ void valueHandleChanged();
+ void valueChanged();
+ void defaultPropertyIndexChanged();
+
+private:
+ void updateValue();
+ void updateDefaultPropertyIndex();
+
+ QVector<PropertyInfo> m_properties;
+ UICDM::CUICDMActionHandle m_action;
+ UICDM::CUICDMHandlerArgHandle m_nameHandle;
+ UICDM::CUICDMHandlerArgHandle m_valueHandle;
+ int m_defaultPropertyIndex = -1;
+ QVariant m_value;
+};
+
+Q_DECLARE_METATYPE(PropertyInfo)
+
+#endif // PROPERTYMODEL_H
diff --git a/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.cpp b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.cpp
new file mode 100644
index 00000000..9ceee2d2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "BasicObjectsModel.h"
+#include "DropSource.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+
+#include <QCoreApplication>
+#include <QDataStream>
+#include <QMimeData>
+
+BasicObjectsModel::BasicObjectsModel(QObject *parent) : QAbstractListModel(parent)
+{
+ initialize();
+}
+
+void BasicObjectsModel::initialize()
+{
+ m_ObjectItems = {
+ {tr("Rectangle"), "Asset-Rectangle-Normal.png"_L1, OBJTYPE_MODEL, PRIMITIVETYPE_RECT},
+ {tr("Sphere"), "Asset-Sphere-Normal.png"_L1, OBJTYPE_MODEL, PRIMITIVETYPE_SPHERE},
+ {tr("Cube"), "Asset-Cube-Normal.png"_L1, OBJTYPE_MODEL, PRIMITIVETYPE_BOX},
+ {tr("Cylinder"), "Asset-Cylinder-Normal.png"_L1, OBJTYPE_MODEL, PRIMITIVETYPE_CYLINDER},
+ {tr("Cone"), "Asset-Cone-Normal.png"_L1, OBJTYPE_MODEL, PRIMITIVETYPE_CONE},
+ {tr("Component"), "Asset-Component-Normal.png"_L1, OBJTYPE_COMPONENT, PRIMITIVETYPE_UNKNOWN},
+ {tr("Group"), "Asset-Group-Normal.png"_L1, OBJTYPE_GROUP, PRIMITIVETYPE_UNKNOWN},
+ {tr("Text"), "Asset-Text-Normal.png"_L1, OBJTYPE_TEXT, PRIMITIVETYPE_UNKNOWN},
+ {tr("Layer"), "Asset-Layer-Normal.png"_L1, OBJTYPE_LAYER, PRIMITIVETYPE_UNKNOWN},
+ {tr("Camera"), "Asset-Camera-Normal.png"_L1, OBJTYPE_CAMERA, PRIMITIVETYPE_UNKNOWN},
+ {tr("Light"), "Asset-Light-Normal.png"_L1, OBJTYPE_LIGHT, PRIMITIVETYPE_UNKNOWN},
+ {tr("Alias"), "Asset-Alias-Normal.png"_L1, OBJTYPE_ALIAS, PRIMITIVETYPE_UNKNOWN}
+ };
+}
+
+QVariant BasicObjectsModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return {};
+
+ const auto row = index.row();
+
+ switch (role) {
+ case NameRole: return m_ObjectItems.at(row).name();
+ case IconRole: return resourceImageUrl() +
+ m_ObjectItems.at(row).icon();
+ case ObjectTypeRole: return m_ObjectItems.at(row).objectType();
+ case PrimitiveTypeRole: return m_ObjectItems.at(row).primitiveType();
+ }
+
+ return {};
+}
+
+int BasicObjectsModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+ return m_ObjectItems.count();
+}
+
+QHash<int, QByteArray> BasicObjectsModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(IconRole, "icon");
+
+ return names;
+}
+
+Qt::ItemFlags BasicObjectsModel::flags(const QModelIndex &index) const {
+ if (index.isValid())
+ return Qt::ItemIsDragEnabled;
+
+ return QAbstractListModel::flags(index);
+}
+
+QStringList BasicObjectsModel::mimeTypes() const
+{
+ return { m_MimeType };
+}
+
+QMimeData *BasicObjectsModel::mimeData(const QModelIndexList &indexes) const
+{
+
+ const auto row = indexes.first().row(); // we support only one item for D&D
+ auto object = m_ObjectItems.at(row);
+
+ auto *data = CDropSourceFactory::Create(object.GetFlavor(), &object);
+ return data;
+}
+
+BasicObjectItem::~BasicObjectItem()
+{
+}
diff --git a/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.h b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.h
new file mode 100644
index 00000000..ccaf07d6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsModel.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BASICOBJECTSMODEL_H
+#define BASICOBJECTSMODEL_H
+
+#include <QAbstractListModel>
+
+#include "IDragable.h"
+#include "StudioObjectTypes.h"
+
+class BasicObjectItem : public IDragable {
+
+public:
+ BasicObjectItem() {}
+ BasicObjectItem(const QString &name, const QString &icon,
+ EStudioObjectType objectType, EPrimitiveType primitiveType)
+ : m_name(name), m_icon(icon)
+ , m_objectType(objectType), m_primitiveType(primitiveType)
+ { }
+
+ virtual ~BasicObjectItem();
+
+ QString name() const { return m_name; }
+ QString icon() const { return m_icon; }
+
+ EStudioObjectType objectType() const { return m_objectType; }
+ EPrimitiveType primitiveType() const { return m_primitiveType; }
+
+ void setName(const QString &name) { m_name = name; }
+ void setIcon(const QString &icon) { m_icon = icon; }
+ void setObjectType(EStudioObjectType type) { m_objectType = type; }
+ void setPrimitveType(EPrimitiveType type) { m_primitiveType = type; }
+
+ long GetFlavor() const override {return EUIC_FLAVOR_BASIC_OBJECTS;}
+
+private:
+ QString m_name;
+ QString m_icon;
+ EStudioObjectType m_objectType;
+ EPrimitiveType m_primitiveType;
+};
+
+class BasicObjectsModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ BasicObjectsModel(QObject *parent = nullptr);
+
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ IconRole = Qt::DecorationRole,
+ ObjectTypeRole = Qt::UserRole + 1,
+ PrimitiveTypeRole
+ };
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QStringList mimeTypes() const override;
+ QMimeData *mimeData(const QModelIndexList &indexes) const override;
+
+private:
+ void initialize();
+
+ QVector<BasicObjectItem> m_ObjectItems;
+
+ const QString m_MimeType = QLatin1String("application/x-basic-object");
+};
+
+#endif // BASICOBJECTSMODEL_H
diff --git a/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.cpp b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.cpp
new file mode 100644
index 00000000..6834352a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "BasicObjectsView.h"
+#include "BasicObjectsModel.h"
+#include "CColor.h"
+#include "Literals.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qdrag.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+BasicObjectsView::BasicObjectsView(QWidget *parent) : QQuickWidget(parent)
+ , m_ObjectsModel(new BasicObjectsModel(this))
+
+{
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &BasicObjectsView::initialize);
+}
+
+QSize BasicObjectsView::sizeHint() const
+{
+ return {120, 600};
+}
+
+void BasicObjectsView::startDrag(int row)
+{
+ const auto index = m_ObjectsModel->index(row);
+
+ QDrag drag(this);
+ drag.setMimeData(m_ObjectsModel->mimeData({index}));
+ drag.setPixmap(QPixmap(index.data(BasicObjectsModel::IconRole).toUrl().toLocalFile()));
+ drag.exec(Qt::CopyAction);
+}
+
+void BasicObjectsView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_basicObjectsModel"_L1, m_ObjectsModel);
+ rootContext()->setContextProperty("_basicObjectsView"_L1, this);
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/BasicObjects/BasicObjectsView.qml"_L1));
+}
diff --git a/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.h b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.h
new file mode 100644
index 00000000..9317fc8f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BASICOBJECTSVIEW_H
+#define BASICOBJECTSVIEW_H
+
+#include <QQuickWidget>
+
+class BasicObjectsModel;
+
+class BasicObjectsView : public QQuickWidget
+{
+ Q_OBJECT
+public:
+ explicit BasicObjectsView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+
+ Q_INVOKABLE void startDrag(int row);
+private:
+ void initialize();
+
+ BasicObjectsModel *m_ObjectsModel = nullptr;
+ QColor m_BaseColor = QColor::fromRgb(75, 75, 75);
+};
+
+#endif // BASICOBJECTSVIEW_H
diff --git a/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.qml b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.qml
new file mode 100644
index 00000000..ec2a0f6e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/BasicObjects/BasicObjectsView.qml
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import "../controls"
+
+Rectangle {
+
+ color: _backgroundColor
+
+ ListView {
+ anchors {
+ fill: parent
+ leftMargin: 8
+ }
+ boundsBehavior: Flickable.StopAtBounds
+
+ model: _basicObjectsModel
+ spacing: 2
+
+ ScrollBar.vertical: ScrollBar {}
+
+ delegate: Item {
+ height: contentRow.height
+ width: contentRow.width
+ Item {
+ id: dragItem
+ anchors.fill: parent
+
+ Drag.active: dragArea.drag.active
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+ Drag.dragType: Drag.Automatic
+ Drag.supportedActions: Qt.CopyAction
+
+ MouseArea {
+ id: dragArea
+ property bool dragging: false
+ anchors.fill: parent
+ drag.target: dragItem
+ }
+
+ Drag.onDragStarted: _basicObjectsView.startDrag(model.index)
+ }
+ Row {
+ id: contentRow
+ spacing: 4
+ Image {
+ id: assetIcon
+ width: 24
+ height: 24
+ fillMode: Image.Pad
+ source: model.icon
+ }
+ StyledLabel {
+ y: (assetIcon.height - height) / 2
+ text: model.name
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ChooserDelegate.qml b/src/Authoring/Studio/Palettes/Inspector/ChooserDelegate.qml
new file mode 100644
index 00000000..6ee4445c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ChooserDelegate.qml
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: item
+
+ signal clicked(string filePath)
+ signal doubleClicked(string filePath)
+
+ width: parent.width
+ height: 20
+ color: isCurrentFile ? _selectionColor : "transparent"
+
+ Row {
+ x: depth*28
+ anchors.verticalCenter: item.verticalCenter
+
+ Image {
+ source: _resDir + (expanded ? "arrow_down.png" : "arrow.png")
+ opacity: isExpandable ? 1 : 0
+
+ MouseArea {
+ visible: isExpandable
+ anchors.fill: parent
+ onClicked: {
+ if (expanded)
+ listView.model.collapse(index)
+ else
+ listView.model.expand(index)
+ }
+ }
+ }
+
+ Image {
+ source: fileIcon
+ }
+
+ StyledLabel {
+ text: fileName
+ color: _textColor
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.LeftButton
+ visible: isSelectable
+ onClicked: item.clicked(filePath)
+ onDoubleClicked: item.doubleClicked(filePath)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp
new file mode 100644
index 00000000..87df1b45
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp
@@ -0,0 +1,592 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QSet>
+
+#include "stdafx.h"
+
+#include "ChooserModelBase.h"
+#include "Core.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "StudioUtils.h"
+#include "UICFileTools.h"
+#include "ImportUtils.h"
+#include "StudioApp.h"
+
+ChooserModelBase::ChooserModelBase(QObject *parent) : QAbstractListModel(parent)
+ , m_model(new QFileSystemModel(this))
+{
+ connect(m_model, &QAbstractItemModel::rowsInserted, this, &ChooserModelBase::modelRowsInserted);
+ connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ChooserModelBase::modelRowsRemoved);
+ connect(m_model, &QAbstractItemModel::layoutChanged, this, &ChooserModelBase::modelLayoutChanged);
+
+ g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this);
+
+ rebuild();
+}
+
+ChooserModelBase::~ChooserModelBase()
+{
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+}
+
+QHash<int, QByteArray> ChooserModelBase::roleNames() const
+{
+ auto modelRoleNames = m_model->roleNames();
+ modelRoleNames.insert(IsExpandableRole, "isExpandable");
+ modelRoleNames.insert(DepthRole, "depth");
+ modelRoleNames.insert(ExpandedRole, "expanded");
+ modelRoleNames.insert(IsSelectableRole, "isSelectable");
+ modelRoleNames.insert(IsCurrentFile, "isCurrentFile");
+ return modelRoleNames;
+}
+
+int ChooserModelBase::rowCount(const QModelIndex &) const
+{
+ return getFixedItems().count() + m_items.count();
+}
+
+QVariant ChooserModelBase::data(const QModelIndex &index, int role) const
+{
+ const int row = index.row();
+
+ const auto fixedItems = getFixedItems();
+ const int fixedItemCount = fixedItems.count();
+
+ if (row < fixedItemCount) {
+ const auto &item = fixedItems.at(row);
+
+ switch (role) {
+ case Qt::DecorationRole:
+ return resourceImageUrl() + CStudioObjectTypes::GetNormalIconName(item.iconType);
+
+ case IsExpandableRole:
+ return false;
+
+ case DepthRole:
+ return 0;
+
+ case ExpandedRole:
+ return false;
+
+ case IsSelectableRole:
+ return true;
+
+ case IsCurrentFile:
+ return item.name == m_currentFile;
+
+ default:
+ return item.name;
+ }
+ } else {
+ const auto &item = m_items.at(row - fixedItemCount);
+
+ switch (role) {
+ case Qt::DecorationRole: {
+ QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return resourceImageUrl() + getIconName(path);
+ }
+
+ case IsExpandableRole: {
+ QFileInfo fileInfo(item.index.data(QFileSystemModel::FilePathRole).toString());
+ return fileInfo.isDir();
+ }
+
+ case DepthRole:
+ return item.depth;
+
+ case ExpandedRole:
+ return item.expanded;
+
+ case IsSelectableRole: {
+ QFileInfo fileInfo(item.index.data(QFileSystemModel::FilePathRole).toString());
+ return fileInfo.isFile();
+ }
+
+ case IsCurrentFile: {
+ QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return path == m_currentFile;
+ }
+
+ default:
+ return m_model->data(item.index, role);
+ }
+ }
+}
+
+void ChooserModelBase::setCurrentFile(const QString &path)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const QDir documentDir(doc->GetDocumentDirectory().toQString());
+ const QString fullPath = QDir::cleanPath(documentDir.filePath(path));
+
+ if (fullPath != m_currentFile) {
+ const auto fixedItems = getFixedItems();
+
+ const auto fileRow = [this, &fixedItems](const QString &path)
+ {
+ const int fixedItemCount = fixedItems.count();
+
+ for (int i = 0; i < fixedItemCount; ++i) {
+ const auto &item = fixedItems.at(i);
+
+ if (item.name == path)
+ return i;
+ }
+
+ const int itemCount = m_items.count();
+
+ for (int i = 0; i < itemCount; ++i) {
+ const auto &item = m_items.at(i);
+
+ if (item.index.data(QFileSystemModel::FilePathRole).toString() == path)
+ return fixedItemCount + i;
+ }
+
+ return -1;
+ };
+
+ int previousRow = fileRow(m_currentFile);
+ int currentRow = fileRow(fullPath);
+
+ m_currentFile = fullPath;
+
+ const int fixedItemCount = fixedItems.count();
+
+ if (previousRow != -1)
+ Q_EMIT dataChanged(index(previousRow), index(previousRow));
+
+ if (currentRow != -1)
+ Q_EMIT dataChanged(index(currentRow), index(currentRow));
+ }
+
+ // expand parent folder if current file is hidden
+ auto matched = m_model->match(m_rootIndex, QFileSystemModel::FilePathRole, path, 1,
+ Qt::MatchExactly|Qt::MatchRecursive);
+ if (!matched.isEmpty()) {
+ auto modelIndex = matched.first();
+ if (modelIndexRow(modelIndex) == -1)
+ expand(m_model->parent(modelIndex));
+ }
+}
+
+void ChooserModelBase::expand(const QModelIndex &modelIndex)
+{
+ if (modelIndex == m_rootIndex)
+ return;
+
+ int row = modelIndexRow(modelIndex);
+ if (row == -1) {
+ QModelIndex parentIndex = m_model->parent(modelIndex);
+ expand(parentIndex);
+
+ row = modelIndexRow(modelIndex);
+ Q_ASSERT(row != -1);
+ }
+
+ if (!m_items.at(row).expanded)
+ expand(row + getFixedItems().count());
+}
+
+void ChooserModelBase::setRootPath(const QString &path)
+{
+ setRootIndex(m_model->setRootPath(path));
+}
+
+void ChooserModelBase::setRootIndex(const QModelIndex &rootIndex)
+{
+ if (rootIndex != m_rootIndex) {
+ clearModelData();
+ m_rootIndex = rootIndex;
+ showModelTopLevelItems();
+ }
+}
+
+void ChooserModelBase::clearModelData()
+{
+ if (!m_items.isEmpty()) {
+ const auto fixedItemCount = getFixedItems().count();
+ beginRemoveRows({}, fixedItemCount, fixedItemCount + m_items.count() - 1);
+ m_items.clear();
+ endRemoveRows();
+ }
+}
+
+void ChooserModelBase::showModelTopLevelItems()
+{
+ int rowCount = m_model->rowCount(m_rootIndex);
+
+ if (rowCount == 0) {
+ if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex))
+ m_model->fetchMore(m_rootIndex);
+ } else {
+ showModelChildItems(m_rootIndex, 0, rowCount - 1);
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, m_rootIndex);
+ if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
+ m_model->fetchMore(childIndex);
+ }
+ }
+}
+
+void ChooserModelBase::showModelChildItems(const QModelIndex &parentIndex, int start, int end)
+{
+ QVector<QModelIndex> rowsToInsert;
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex))
+ rowsToInsert.append(childIndex);
+ }
+
+ const int insertCount = rowsToInsert.count();
+
+ if (insertCount != 0) {
+ TreeItem *parent;
+ int depth, startRow;
+
+ if (parentIndex == m_rootIndex) {
+ parent = nullptr;
+ depth = 0;
+ startRow = 0;
+ } else {
+ const int parentRow = modelIndexRow(parentIndex);
+ Q_ASSERT(parentRow != -1 && isVisible(parentIndex));
+ parent = &m_items[parentRow];
+ depth = parent->depth + 1;
+ startRow = parentRow + parent->childCount + 1;
+ }
+
+ const int fixedItemCount = getFixedItems().count();
+ beginInsertRows({}, startRow + fixedItemCount, startRow + fixedItemCount + insertCount - 1);
+
+ for (auto it = rowsToInsert.rbegin(); it != rowsToInsert.rend(); ++it)
+ m_items.insert(startRow, { *it, depth, false, parent, 0 });
+
+ for (; parent != nullptr; parent = parent->parent)
+ parent->childCount += insertCount;
+
+ endInsertRows();
+ }
+}
+
+void ChooserModelBase::expand(int row)
+{
+ const int fixedItemCount = getFixedItems().count();
+ Q_ASSERT(row >= fixedItemCount && row < fixedItemCount + m_items.count());
+
+ auto &item = m_items[row - fixedItemCount];
+ Q_ASSERT(item.expanded == false);
+
+ const auto &modelIndex = item.index;
+
+ const int rowCount = m_model->rowCount(modelIndex);
+ if (rowCount == 0) {
+ if (m_model->hasChildren(modelIndex) && m_model->canFetchMore(modelIndex))
+ m_model->fetchMore(modelIndex);
+ } else {
+ showModelChildItems(modelIndex, 0, rowCount - 1);
+ }
+
+ item.expanded = true;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+void ChooserModelBase::collapse(int row)
+{
+ const int fixedItemCount = getFixedItems().count();
+ Q_ASSERT(row >= fixedItemCount && row < fixedItemCount + m_items.count());
+
+ auto &item = m_items[row - fixedItemCount];
+ Q_ASSERT(item.expanded == true);
+
+ const int childCount = item.childCount;
+
+ if (childCount > 0) {
+ beginRemoveRows({}, row + 1, row + childCount);
+
+ auto first = std::begin(m_items) + row - fixedItemCount + 1;
+ m_items.erase(first, first + childCount);
+
+ for (auto parent = &item; parent != nullptr; parent = parent->parent)
+ parent->childCount -= childCount;
+
+ endRemoveRows();
+ }
+
+ item.expanded = false;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+void ChooserModelBase::OnNewPresentation()
+{
+ rebuild();
+}
+
+int ChooserModelBase::modelIndexRow(const QModelIndex &modelIndex) const
+{
+ auto it = std::find_if(std::begin(m_items), std::end(m_items),
+ [&modelIndex](const TreeItem &item)
+ {
+ return item.index == modelIndex;
+ });
+
+ return it != std::end(m_items) ? std::distance(std::begin(m_items), it) : -1;
+}
+
+bool ChooserModelBase::isExpanded(const QModelIndex &modelIndex) const
+{
+ if (modelIndex == m_rootIndex) {
+ return true;
+ } else {
+ const int row = modelIndexRow(modelIndex);
+ return row != -1 && m_items.at(row).expanded;
+ }
+}
+
+EStudioObjectType ChooserModelBase::getIconType(const QString &path) const
+{
+ return Q3DStudio::ImportUtils::GetObjectFileTypeForFile(Q3DStudio::CFilePath::fromQString(path)).m_IconType;
+}
+
+QString ChooserModelBase::getIconName(const QString &path) const
+{
+ QString iconName;
+
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile()) {
+ EStudioObjectType type = getIconType(path);
+ if (type != OBJTYPE_UNKNOWN)
+ iconName = CStudioObjectTypes::GetNormalIconName(type);
+ else
+ iconName = QStringLiteral("Objects-Layer-Normal.png");
+ } else {
+ iconName = QStringLiteral("Objects-Folder-Normal.png");
+ }
+
+ return iconName;
+}
+
+bool ChooserModelBase::isVisible(const QModelIndex &modelIndex) const
+{
+ QString path = modelIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+
+ if (fileInfo.isFile()) {
+ return isVisible(path);
+ } else {
+ return hasVisibleChildren(modelIndex);
+ }
+}
+
+bool ChooserModelBase::hasVisibleChildren(const QModelIndex &modelIndex) const
+{
+ int rowCount = m_model->rowCount(modelIndex);
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, modelIndex);
+
+ if (m_model->hasChildren(childIndex)) {
+ if (hasVisibleChildren(childIndex))
+ return true;
+ } else {
+ QString path = childIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile() && isVisible(path))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ChooserModelBase::modelRowsInserted(const QModelIndex &parentIndex, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parentIndex)) {
+ showModelChildItems(parentIndex, start, end);
+ } else {
+ if (modelIndexRow(parentIndex) == -1) {
+ // parent wasn't inserted in model yet, check if any of the new rows is visible
+
+ bool visible = false;
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ QString path = childIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile() && isVisible(path)) {
+ visible = true;
+ break;
+ }
+ }
+
+ // if any of the new rows is visible, insert parent folder index into model
+
+ if (visible) {
+ QModelIndex index = parentIndex, parent = m_model->parent(parentIndex);
+
+ while (parent != m_rootIndex && modelIndexRow(parent) == -1) {
+ index = parent;
+ parent = m_model->parent(parent);
+ }
+
+ if (isExpanded(parent) && modelIndexRow(index) == -1) {
+ const int row = index.row();
+ showModelChildItems(parent, row, row);
+ }
+ }
+ }
+
+ // if one of the new rows is the current file expand parent folder
+
+ bool containsCurrent = false;
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (childIndex.data(QFileSystemModel::FilePathRole).toString() == m_currentFile) {
+ containsCurrent = true;
+ break;
+ }
+ }
+
+ if (containsCurrent)
+ expand(parentIndex);
+ }
+
+ // fetch children so we're notified when files are added or removed
+
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
+ m_model->fetchMore(childIndex);
+ }
+}
+
+void ChooserModelBase::modelRowsRemoved(const QModelIndex &parentIndex, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parentIndex)) {
+ const auto fixedItems = getFixedItems();
+
+ const auto removeRow = [this, &fixedItems](int row)
+ {
+ const auto &item = m_items.at(row);
+
+ const int fixedItemCount = fixedItems.count();
+ beginRemoveRows({}, row + fixedItemCount, row + fixedItemCount + item.childCount);
+
+ for (auto parent = item.parent; parent != nullptr; parent = parent->parent)
+ parent->childCount -= 1 + item.childCount;
+
+ m_items.erase(std::begin(m_items) + row, std::begin(m_items) + row + item.childCount + 1);
+
+ endRemoveRows();
+ };
+
+ // remove rows
+
+ for (int i = start; i <= end; ++i) {
+ const int row = modelIndexRow(m_model->index(i, 0, parentIndex));
+ if (row != -1)
+ removeRow(row);
+ }
+
+ // also remove folder row if there are no more visible children
+
+ QModelIndex index = parentIndex;
+
+ while (index != m_rootIndex && !hasVisibleChildren(index)) {
+ const int row = modelIndexRow(index);
+ Q_ASSERT(row != -1);
+ removeRow(row);
+ index = m_model->parent(index);
+ }
+ }
+}
+
+void ChooserModelBase::modelLayoutChanged()
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ QSet<QPersistentModelIndex> expandedItems;
+ for (const auto &item : m_items) {
+ if (item.expanded)
+ expandedItems.insert(item.index);
+ }
+
+ const std::function<int(const QModelIndex &, TreeItem *)> insertChildren =
+ [this, &expandedItems, &insertChildren](const QModelIndex &parentIndex, TreeItem *parent)
+ {
+ Q_ASSERT(parentIndex == m_rootIndex || isVisible(parentIndex));
+
+ const int rowCount = m_model->rowCount(parentIndex);
+ const int depth = parent == nullptr ? 0 : parent->depth + 1;
+
+ int childCount = 0;
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex)) {
+ const bool expanded = expandedItems.contains(childIndex);
+ m_items.append({ childIndex, depth, expanded, parent, 0 });
+ auto &item = m_items.last();
+ if (expanded) {
+ item.childCount = insertChildren(childIndex, &item);
+ childCount += item.childCount;
+ }
+ ++childCount;
+ }
+ }
+
+ return childCount;
+ };
+
+ const int itemCount = m_items.count();
+
+ m_items.clear();
+ m_items.reserve(itemCount);
+
+ insertChildren(m_rootIndex, nullptr);
+ Q_ASSERT(m_items.count() == itemCount);
+
+ const int fixedItemCount = getFixedItems().count();
+ Q_EMIT dataChanged(index(fixedItemCount), index(fixedItemCount + itemCount - 1));
+}
+
+void ChooserModelBase::rebuild()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const Q3DStudio::CFilePath path(doc->GetDocumentPath().GetAbsolutePath());
+ setRootPath(path.GetDirectory().toQString());
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.h b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.h
new file mode 100644
index 00000000..547822ea
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef CHOOSERMODELBASE_H
+#define CHOOSERMODELBASE_H
+
+#include "DispatchListeners.h"
+#include "StudioObjectTypes.h"
+
+#include <QFileSystemModel>
+#include <QAbstractListModel>
+#include <QList>
+#include <QVector>
+
+class QFileSystemModel;
+
+class ChooserModelBase : public QAbstractListModel, public CPresentationChangeListener
+{
+ Q_OBJECT
+
+public:
+ explicit ChooserModelBase(QObject *parent = nullptr);
+ ~ChooserModelBase();
+
+ enum {
+ IsExpandableRole = QFileSystemModel::FilePermissions + 1,
+ DepthRole,
+ ExpandedRole,
+ IsSelectableRole,
+ IsCurrentFile
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ Q_INVOKABLE void expand(int row);
+ Q_INVOKABLE void collapse(int row);
+
+ void setCurrentFile(const QString &path);
+
+ // CPresentationChangeListener
+ void OnNewPresentation() override;
+
+Q_SIGNALS:
+ void modelChanged(QAbstractItemModel *model);
+
+protected:
+ EStudioObjectType getIconType(const QString &path) const;
+
+ virtual bool isVisible(const QString &path) const = 0;
+
+ struct FixedItem
+ {
+ EStudioObjectType iconType;
+ QString name;
+ };
+
+ virtual const QVector<FixedItem> getFixedItems() const = 0;
+
+private:
+ void setRootPath(const QString &path);
+ void setRootIndex(const QModelIndex &rootIndex);
+ void clearModelData();
+ void showModelTopLevelItems();
+ void showModelChildItems(const QModelIndex &parentItem, int start, int end);
+ int modelIndexRow(const QModelIndex &modelIndex) const;
+ bool isExpanded(const QModelIndex &modelIndex) const;
+ QString getIconName(const QString &path) const;
+ bool isVisible(const QModelIndex &modelIndex) const;
+ bool hasVisibleChildren(const QModelIndex &modelIndex) const;
+ void expand(const QModelIndex &modelIndex);
+
+ void modelRowsInserted(const QModelIndex &parent, int start, int end);
+ void modelRowsRemoved(const QModelIndex &parent, int start, int end);
+ void modelRowsMoved(const QModelIndex &parent, int start, int end);
+ void modelLayoutChanged();
+
+ void rebuild();
+
+ struct TreeItem {
+ QPersistentModelIndex index;
+ int depth;
+ bool expanded;
+ TreeItem *parent;
+ int childCount;
+ };
+
+ QFileSystemModel *m_model;
+ QPersistentModelIndex m_rootIndex;
+ QList<TreeItem> m_items;
+ QString m_currentFile;
+};
+
+#endif // CHOOSERMODELBASE_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.cpp b/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.cpp
new file mode 100644
index 00000000..44b89904
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "EasyInspectorGroup.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CEasyInspectorGroup::CEasyInspectorGroup(const QString &inName)
+ : CInspectorGroup()
+{
+ SetName(inName);
+}
+
+//==============================================================================
+/**
+ *
+ */
+CEasyInspectorGroup::~CEasyInspectorGroup()
+{
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.h b/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.h
new file mode 100644
index 00000000..62cc35a5
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/EasyInspectorGroup.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_EASYINSPECTORGROUP_H
+#define INCLUDED_EASYINSPECTORGROUP_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "InspectorGroup.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+/**
+ * Simplest possible inspector group
+ */
+class CEasyInspectorGroup : public CInspectorGroup
+{
+public: // Construction
+ CEasyInspectorGroup(const QString &inName);
+ ~CEasyInspectorGroup();
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Inspector/FileChooser.qml b/src/Authoring/Studio/Palettes/Inspector/FileChooser.qml
new file mode 100644
index 00000000..2762af3a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/FileChooser.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ anchors {
+ fill: parent
+ leftMargin: 8
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _fileChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: _fileChooserView.fileSelected(_fileChooserView.handle, _fileChooserView.instance, filePath)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.cpp b/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.cpp
new file mode 100644
index 00000000..6c57635b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "FileChooserModel.h"
+
+FileChooserModel::FileChooserModel(QObject *parent)
+ : ChooserModelBase(parent)
+{
+
+}
+
+FileChooserModel::~FileChooserModel()
+{
+}
+
+bool FileChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_GROUP;
+}
+
+const QVector<ChooserModelBase::FixedItem> FileChooserModel::getFixedItems() const
+{
+ static const QVector<FixedItem> items = { { OBJTYPE_GROUP, tr("[None]") } };
+ return items;
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.h b/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.h
new file mode 100644
index 00000000..c019c4a1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/FileChooserModel.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FILECHOOSERMODEL_H
+#define FILECHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class FileChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit FileChooserModel(QObject *parent = nullptr);
+ virtual ~FileChooserModel();
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+};
+
+#endif // IMAGECHOOSERMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/FileChooserView.cpp b/src/Authoring/Studio/Palettes/Inspector/FileChooserView.cpp
new file mode 100644
index 00000000..d5951e7c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/FileChooserView.cpp
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "FileChooserView.h"
+#include "FileChooserModel.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtCore/qtimer.h>
+
+FileChooserView::FileChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new FileChooserModel(this))
+{
+ setWindowTitle(tr("Imports"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &FileChooserView::initialize);
+}
+
+void FileChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_resDir"_L1,
+ resourceImageUrl());
+ rootContext()->setContextProperty("_fileChooserView"_L1, this);
+ rootContext()->setContextProperty("_fileChooserModel"_L1, m_model);
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/FileChooser.qml"_L1));
+}
+
+QSize FileChooserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+void FileChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int FileChooserView::handle() const
+{
+ return m_handle;
+}
+
+void FileChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int FileChooserView::instance() const
+{
+ return m_instance;
+}
+
+void FileChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &FileChooserView::close);
+}
+
+void FileChooserView::showEvent(QShowEvent *event)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ UICDM::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ m_model->setCurrentFile(UICDM::get<QString>(value));
+
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/FileChooserView.h b/src/Authoring/Studio/Palettes/Inspector/FileChooserView.h
new file mode 100644
index 00000000..400f9f1e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/FileChooserView.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FILECHOOSERVIEW_H
+#define FILECHOOSERVIEW_H
+
+#include <QtQuickWidgets/qquickwidget.h>
+
+class FileChooserModel;
+
+class FileChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit FileChooserView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+
+Q_SIGNALS:
+ void fileSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ FileChooserModel *m_model = nullptr;
+};
+
+#endif // IMAGECHOOSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.cpp b/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.cpp
new file mode 100644
index 00000000..f840d92f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "GuideInspectable.h"
+#include "InspectableBase.h"
+#include "Core.h"
+#include "Doc.h"
+#include "UICDMGuides.h"
+#include "EasyInspectorGroup.h"
+#include "IDocumentEditor.h"
+#include "UICDMDataTypes.h"
+#include "IInspectableItem.h"
+#include "UICDMValue.h"
+
+typedef std::function<UICDM::SValue()> TGetterFunc;
+typedef std::function<void(UICDM::SValue)> TSetterFunc;
+typedef std::function<void()> TCommitFunc;
+typedef std::function<void()> TCancelFunc;
+
+struct SInspectableDataInfo
+{
+ Q3DStudio::CString m_Name;
+ Q3DStudio::CString m_FormalName;
+ Q3DStudio::CString m_Description;
+ TGetterFunc m_Getter;
+ TSetterFunc m_Setter;
+ TCommitFunc m_Commit;
+ TCancelFunc m_Cancel;
+
+ SInspectableDataInfo(const Q3DStudio::CString &name, const Q3DStudio::CString &formalName,
+ const Q3DStudio::CString &description, TGetterFunc getter, TSetterFunc setter,
+ TCommitFunc commit, TCancelFunc inCancel)
+ : m_Name(name)
+ , m_FormalName(formalName)
+ , m_Description(description)
+ , m_Getter(getter)
+ , m_Setter(setter)
+ , m_Commit(commit)
+ , m_Cancel(inCancel)
+ {
+ }
+};
+
+struct SComboAttItem : public IInspectableAttributeItem
+{
+ SInspectableDataInfo m_BaseInspectableInfo;
+ UICDM::TMetaDataStringList m_MetaDataTypes;
+ SComboAttItem(const SInspectableDataInfo &inInfo, const UICDM::TMetaDataStringList &inTypes)
+ : m_BaseInspectableInfo(inInfo)
+ , m_MetaDataTypes(inTypes)
+ {
+ }
+ UICDM::HandlerArgumentType::Value GetInspectableSubType() const override
+ {
+ return UICDM::HandlerArgumentType::Property;
+ }
+ Q3DStudio::CString GetInspectableName() const override { return m_BaseInspectableInfo.m_Name; }
+ Q3DStudio::CString GetInspectableFormalName() const override
+ {
+ return m_BaseInspectableInfo.m_FormalName;
+ }
+ Q3DStudio::CString GetInspectableDescription() const override
+ {
+ return m_BaseInspectableInfo.m_Description;
+ }
+
+ UICDM::SValue GetInspectableData() const override { return m_BaseInspectableInfo.m_Getter(); }
+ void SetInspectableData(const UICDM::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ m_BaseInspectableInfo.m_Commit();
+ }
+
+ float GetInspectableMin() const override { return 0; }
+ float GetInspectableMax() const override { return 0; }
+ UICDM::TMetaDataStringList GetInspectableList() const override { return m_MetaDataTypes; }
+ UICDM::DataModelDataType::Value GetInspectableType() const override
+ {
+ return UICDM::DataModelDataType::String;
+ }
+ UICDM::AdditionalMetaDataType::Value GetInspectableAdditionalType() const override
+ {
+ return UICDM::AdditionalMetaDataType::StringList;
+ }
+};
+
+struct SFloatIntItem : public IInspectableAttributeItem
+{
+ SInspectableDataInfo m_BaseInspectableInfo;
+ UICDM::DataModelDataType::Value m_DataType;
+ float m_Min;
+ float m_Max;
+ SFloatIntItem(const SInspectableDataInfo &inInfo, UICDM::DataModelDataType::Value inType,
+ float inMin = 0, float inMax = 0)
+ : m_BaseInspectableInfo(inInfo)
+ , m_DataType(inType)
+ , m_Min(inMin)
+ , m_Max(inMax)
+ {
+ }
+ UICDM::HandlerArgumentType::Value GetInspectableSubType() const override
+ {
+ return UICDM::HandlerArgumentType::Property;
+ }
+ Q3DStudio::CString GetInspectableName() const override { return m_BaseInspectableInfo.m_Name; }
+ Q3DStudio::CString GetInspectableFormalName() const override
+ {
+ return m_BaseInspectableInfo.m_FormalName;
+ }
+ Q3DStudio::CString GetInspectableDescription() const override
+ {
+ return m_BaseInspectableInfo.m_Description;
+ }
+
+ UICDM::SValue GetInspectableData() const override { return m_BaseInspectableInfo.m_Getter(); }
+ void SetInspectableData(const UICDM::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ m_BaseInspectableInfo.m_Commit();
+ }
+
+ void ChangeInspectableData(const UICDM::SValue &inValue) override
+ {
+ m_BaseInspectableInfo.m_Setter(inValue);
+ }
+ void CancelInspectableData() override { m_BaseInspectableInfo.m_Cancel(); }
+
+ float GetInspectableMin() const override { return m_Min; }
+ float GetInspectableMax() const override { return m_Max; }
+ UICDM::TMetaDataStringList GetInspectableList() const override
+ {
+ return UICDM::TMetaDataStringList();
+ }
+ UICDM::DataModelDataType::Value GetInspectableType() const override { return m_DataType; }
+ UICDM::AdditionalMetaDataType::Value GetInspectableAdditionalType() const override
+ {
+ return UICDM::AdditionalMetaDataType::None;
+ }
+};
+
+
+CInspectableBase *CGuideInspectable::CreateInspectable(CCore &inCore,
+ UICDM::CUICDMGuideHandle inGuide)
+{
+ return new SGuideInspectableImpl(inCore, inGuide);
+}
+
+SGuideInspectableImpl::SGuideInspectableImpl(CCore &inCore, UICDM::CUICDMGuideHandle inGuide)
+ : CInspectableBase(&inCore)
+ , m_Guide(inGuide)
+ , m_Editor(*inCore.GetDoc())
+{
+}
+
+Q3DStudio::IDocumentReader &SGuideInspectableImpl::Reader() const
+{
+ return m_Core->GetDoc()->GetDocumentReader();
+}
+
+EStudioObjectType SGuideInspectableImpl::GetObjectType()
+{
+ return OBJTYPE_GUIDE;
+}
+
+Q3DStudio::CString SGuideInspectableImpl::GetName()
+{
+ return L"Guide";
+}
+
+long SGuideInspectableImpl::GetGroupCount()
+{
+ return 1;
+}
+
+CInspectorGroup *SGuideInspectableImpl::GetGroup(long)
+{
+ CDoc *theDoc = m_Core->GetDoc();
+ TCommitFunc theCommiter = std::bind(&SGuideInspectableImpl::Commit, this);
+ TCancelFunc theCanceler = std::bind(&SGuideInspectableImpl::Rollback, this);
+ m_Properties.push_back(std::make_shared<SFloatIntItem>(
+ SInspectableDataInfo("Position", "Position", "Position of the guide",
+ std::bind(&SGuideInspectableImpl::GetPosition, this),
+ std::bind(&SGuideInspectableImpl::SetPosition, this,
+ std::placeholders::_1),
+ theCommiter, theCanceler),
+ UICDM::DataModelDataType::Float));
+ UICDM::TMetaDataStringList theComboItems;
+ theComboItems.push_back(L"Horizontal");
+ theComboItems.push_back(L"Vertical");
+
+ m_Properties.push_back(std::make_shared<SComboAttItem>(
+ SInspectableDataInfo("Direction", "Direction", "Direction of the guide",
+ std::bind(&SGuideInspectableImpl::GetDirection, this),
+ std::bind(&SGuideInspectableImpl::SetDirection, this,
+ std::placeholders::_1),
+ theCommiter, theCanceler),
+ theComboItems));
+
+ m_Properties.push_back(std::make_shared<SFloatIntItem>(
+ SInspectableDataInfo("Width", "Width", "Width of the guide",
+ std::bind(&SGuideInspectableImpl::GetWidth, this),
+ std::bind(&SGuideInspectableImpl::SetWidth, this, std::placeholders::_1),
+ theCommiter, theCanceler),
+ UICDM::DataModelDataType::Long, 1.0f, 50.0f));
+
+ CEasyInspectorGroup *theNewGroup = new CEasyInspectorGroup(QObject::tr("Basic"));
+ return theNewGroup;
+}
+
+bool SGuideInspectableImpl::IsValid() const
+{
+ return Reader().IsGuideValid(m_Guide);
+}
+
+bool SGuideInspectableImpl::IsMaster()
+{
+ return true;
+}
+
+void SGuideInspectableImpl::SetDirection(const UICDM::SValue &inValue)
+{
+ UICDM::TDataStrPtr theData = inValue.getData<UICDM::TDataStrPtr>();
+ UICDM::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ if (theData) {
+ if (UICDM::AreEqual(theData->GetData(), L"Horizontal"))
+ theSetter.m_Direction = UICDM::GuideDirections::Horizontal;
+ else if (UICDM::AreEqual(theData->GetData(), L"Vertical"))
+ theSetter.m_Direction = UICDM::GuideDirections::Vertical;
+ }
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+}
+
+UICDM::SValue SGuideInspectableImpl::GetDirection()
+{
+ switch (Reader().GetGuideInfo(m_Guide).m_Direction) {
+ case UICDM::GuideDirections::Horizontal:
+ return std::make_shared<UICDM::CDataStr>(L"Horizontal");
+ case UICDM::GuideDirections::Vertical:
+ return std::make_shared<UICDM::CDataStr>(L"Vertical");
+ default:
+ return std::make_shared<UICDM::CDataStr>(L"");
+ }
+}
+
+void SGuideInspectableImpl::SetPosition(const UICDM::SValue &inValue)
+{
+ float thePos = inValue.getData<float>();
+ UICDM::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ theSetter.m_Position = thePos;
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+}
+
+UICDM::SValue SGuideInspectableImpl::GetPosition()
+{
+ UICDM::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ return theSetter.m_Position;
+}
+
+void SGuideInspectableImpl::SetWidth(const UICDM::SValue &inValue)
+{
+ auto theData = inValue.getData<qt3ds::QT3DSI32>();
+
+ UICDM::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ theSetter.m_Width = theData;
+ Editor().UpdateGuide(m_Guide, theSetter);
+ FireRefresh();
+
+}
+
+UICDM::SValue SGuideInspectableImpl::GetWidth()
+{
+ UICDM::SGuideInfo theSetter(Reader().GetGuideInfo(m_Guide));
+ return theSetter.m_Width;
+}
+
+Q3DStudio::IDocumentEditor &SGuideInspectableImpl::Editor()
+{
+ return m_Editor.EnsureEditor(L"Set Property", __FILE__, __LINE__);
+}
+
+void SGuideInspectableImpl::Commit()
+{
+ m_Editor.CommitEditor();
+}
+
+void SGuideInspectableImpl::Rollback()
+{
+ m_Editor.RollbackEditor();
+}
+
+void SGuideInspectableImpl::FireRefresh()
+{
+ m_Editor.FireImmediateRefresh(UICDM::CUICDMInstanceHandle());
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.h b/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.h
new file mode 100644
index 00000000..77ff7a62
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/GuideInspectable.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef __GUIDEINSPECTABLE_H__
+#define __GUIDEINSPECTABLE_H__
+#include "UICDMHandles.h"
+#include "Core.h"
+#include "InspectableBase.h"
+#include "Doc.h"
+#include "IDocumentEditor.h"
+#include "IInspectableItem.h"
+
+class CInspectableBase;
+
+class CGuideInspectable
+{
+public:
+ static CInspectableBase *CreateInspectable(CCore &inCore, UICDM::CUICDMGuideHandle inGuide);
+};
+
+class SGuideInspectableImpl : public CInspectableBase
+{
+public:
+ SGuideInspectableImpl(CCore &inCore, UICDM::CUICDMGuideHandle inGuide);
+
+ Q3DStudio::IDocumentReader &Reader() const;
+ // Interface
+ EStudioObjectType GetObjectType() override;
+ Q3DStudio::CString GetName() override;
+ long GetGroupCount() override;
+ CInspectorGroup *GetGroup(long) override;
+ bool IsValid() const override;
+ bool IsMaster() override;
+
+ // Implementation to get/set properties
+
+ void SetDirection(const UICDM::SValue &inValue);
+
+ UICDM::SValue GetDirection();
+
+ void SetPosition(const UICDM::SValue &inValue);
+
+ UICDM::SValue GetPosition();
+
+ void SetWidth(const UICDM::SValue &inValue);
+
+ UICDM::SValue GetWidth();
+
+ Q3DStudio::IDocumentEditor &Editor();
+ void Commit();
+ void Rollback();
+ void FireRefresh();
+private:
+ UICDM::CUICDMGuideHandle m_Guide;
+ Q3DStudio::CUpdateableDocumentEditor m_Editor;
+ std::vector<std::shared_ptr<IInspectableAttributeItem>> m_Properties;
+};
+
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Inspector/HandlerFilesChooser.qml b/src/Authoring/Studio/Palettes/Inspector/HandlerFilesChooser.qml
new file mode 100644
index 00000000..65e0d6be
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/HandlerFilesChooser.qml
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+import "../controls"
+
+RowLayout {
+ id: root
+
+ signal showBrowser
+ property string value: ""
+ property alias activeBrowser: browser.activeBrowser
+
+ BrowserCombo {
+ id: browser
+ Layout.preferredWidth: _valueWidth
+ Layout.fillWidth: true
+ value: root.value === "" ? qsTr("Select...") : root.value
+ onShowBrowser: root.showBrowser()
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/HandlerGenericChooser.qml b/src/Authoring/Studio/Palettes/Inspector/HandlerGenericChooser.qml
new file mode 100644
index 00000000..9a8f1688
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/HandlerGenericChooser.qml
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.1
+import "../controls"
+
+RowLayout {
+ id: root
+
+ signal showBrowser
+ property alias value: browser.value
+ property alias activeBrowser: browser.activeBrowser
+
+ BrowserCombo {
+ id: browser
+ Layout.preferredWidth: _valueWidth
+ Layout.fillWidth: true
+ onShowBrowser: root.showBrowser()
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/IEasyInspectorRowListener.h b/src/Authoring/Studio/Palettes/Inspector/IEasyInspectorRowListener.h
new file mode 100644
index 00000000..ced2099b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/IEasyInspectorRowListener.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#include "ToggleButton.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Interface for getting callbacks from the EasyInspectorRow
+ */
+class IEasyInspectorRowListener
+{
+public: // IEasyInspectorRowListener
+ virtual void OnAnimateToggle(CToggleButton::EButtonState inState) = 0;
+ virtual void OnLinkToggle() = 0;
+};
+
+} // namespace Q3DStudio
diff --git a/src/Authoring/Studio/Palettes/Inspector/IInspectableItem.h b/src/Authoring/Studio/Palettes/Inspector/IInspectableItem.h
new file mode 100644
index 00000000..341e46f0
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/IInspectableItem.h
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __IINSPECTABLEITEM_H__
+#define __IINSPECTABLEITEM_H__
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMDataTypes.h"
+#include "UICDMHandles.h"
+#include "UICDMActionInfo.h"
+#include "UICDMMetaData.h"
+#include "UICString.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+class IInspectableItem;
+
+//==============================================================================
+// Abstract Base Classes
+//==============================================================================
+
+enum EInspectableItemTypes {
+ INSPECTABLEITEMTYPE_VANILLA = 1,
+ INSPECTABLEITEMTYPE_PROPERTY,
+ INSPECTABLEITEMTYPE_DEPENDENT,
+ INSPECTABLEITEMTYPE_SLIDE,
+ INSPECTABLEITEMTYPE_OBJECTREFERENCE,
+ INSPECTABLEITEMTYPE_EVENTSOURCE,
+ INSPECTABLEITEMTYPE_ACTION,
+ INSPECTABLEITEMTYPE_CONDITIONS,
+};
+
+//==============================================================================
+/**
+ * @class IInspectableItemChangeListener
+ * @brief Listener class for inspectable item changes.
+ */
+class IInspectableItemChangeListener
+{
+public:
+ virtual void OnInspectablePropertyChanged(IInspectableItem *inProperty) = 0;
+};
+
+class IInspectableObject
+{
+public:
+ virtual UICDM::CUICDMInstanceHandle GetInspectableBaseInstance() = 0;
+ virtual void SetInspectableObject(const UICDM::SObjectRefType &) = 0;
+ virtual UICDM::SObjectRefType GetInspectableObject() = 0;
+};
+
+class IInspectableEvent
+{
+public:
+ virtual UICDM::CUICDMInstanceHandle GetInspectableInstance() = 0;
+ virtual UICDM::CUICDMEventHandle GetInspectableEvent() = 0;
+ virtual void SetInspectableEvent(const UICDM::CUICDMEventHandle &inEventHandle) = 0;
+};
+
+class IInspectableTargetSection : public IInspectableObject
+{
+public:
+ virtual UICDM::CUICDMActionHandle GetInspectableAction() const = 0;
+};
+
+class IInspectableEventSection : public IInspectableObject, public IInspectableEvent
+{
+public:
+ virtual UICDM::CUICDMActionHandle GetInspectableAction() const = 0;
+};
+
+class IInspectableHandlerSection
+{
+public:
+ virtual UICDM::CUICDMActionHandle GetInspectableAction() const = 0;
+ virtual UICDM::CUICDMHandlerHandle GetInspectableHandler() = 0;
+ virtual void SetInspectableHandler(const UICDM::CUICDMHandlerHandle &inHandlerHandle) = 0;
+
+ virtual UICDM::THandlerHandleList GetInspectableHandlerList() = 0;
+ virtual long GetArgumentCount() = 0;
+ virtual IInspectableItem *GetArgument(long inIndex) = 0;
+ virtual Q3DStudio::CString GetInspectableDescription() = 0;
+};
+
+//==============================================================================
+/**
+ * @class IInspectableItem
+ * @brief Abstract base class for inspectable items.
+ */
+class IInspectableItem
+{
+public:
+ virtual ~IInspectableItem() {}
+ virtual EInspectableItemTypes GetInspectableKind() { return INSPECTABLEITEMTYPE_VANILLA; }
+
+ virtual UICDM::HandlerArgumentType::Value
+ GetInspectableSubType() const = 0; // TODO : Make this method name correct
+ virtual Q3DStudio::CString GetInspectableName() const = 0;
+ virtual Q3DStudio::CString GetInspectableFormalName() const = 0;
+ virtual Q3DStudio::CString GetInspectableDescription() const = 0;
+
+ virtual UICDM::SValue GetInspectableData() const = 0;
+ virtual void SetInspectableData(const UICDM::SValue &) = 0;
+
+ // TODO: Remove from here onwards after cleaning up the rest of the UI classes
+ // This is the non-commital version of SetInspectableData, which must be called
+ // after ChangeInspectableData to commit the action.
+ virtual bool GetInspectableReadOnly() const { return false; }
+
+ virtual void ChangeInspectableData(const UICDM::SValue & /*inAttr*/){};
+ virtual void CancelInspectableData(){}
+
+ virtual void AddInspectableChangeListener(IInspectableItemChangeListener * /*inListener*/){};
+ virtual void RemoveInspectableChangeListener(IInspectableItemChangeListener * /*inListener*/){};
+};
+
+//==============================================================================
+/**
+ * Property specialization
+ */
+class IInspectablePropertyItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_PROPERTY; }
+ virtual void GetInspectablePropertyList(UICDM::TPropertyHandleList &outList) = 0;
+ virtual UICDM::CUICDMInstanceHandle GetInspectableInstance() = 0;
+};
+
+//==============================================================================
+/**
+ * Attribute specialization
+ */
+class IInspectableAttributeItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_DEPENDENT; }
+ virtual float GetInspectableMin() const = 0;
+ virtual float GetInspectableMax() const = 0;
+ virtual UICDM::TMetaDataStringList GetInspectableList() const = 0;
+ virtual UICDM::DataModelDataType::Value GetInspectableType() const = 0;
+ virtual UICDM::AdditionalMetaDataType::Value GetInspectableAdditionalType() const = 0;
+};
+
+//==============================================================================
+/**
+ * Slide specialization
+ */
+class IInspectableSlideItem : public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_SLIDE; }
+ virtual void GetSlideNames(std::list<Q3DStudio::CString> &outSlideNames) = 0;
+};
+
+//==============================================================================
+/**
+ * ObjectReference specialiaztion
+ */
+class IInspectableObjectRefItem : public IInspectableObject, public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override
+ {
+ return INSPECTABLEITEMTYPE_OBJECTREFERENCE;
+ }
+};
+
+//==============================================================================
+/**
+ * Event specialization
+ */
+class IInspectableEventItem : public IInspectableEvent, public IInspectableItem
+{
+public:
+ EInspectableItemTypes GetInspectableKind() override { return INSPECTABLEITEMTYPE_EVENTSOURCE; }
+};
+
+#endif // #ifndef __IINSPECTABLEITEM_H__
diff --git a/src/Authoring/Studio/Palettes/Inspector/ImageChooser.qml b/src/Authoring/Studio/Palettes/Inspector/ImageChooser.qml
new file mode 100644
index 00000000..7621c5ef
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ImageChooser.qml
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ anchors {
+ fill: parent
+ leftMargin: 8
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _imageChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: _imageChooserView.imageSelected(_imageChooserView.handle, _imageChooserView.instance, filePath);
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.cpp b/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.cpp
new file mode 100644
index 00000000..98732f74
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ImageChooserModel.h"
+
+ImageChooserModel::ImageChooserModel(QObject *parent)
+ : ChooserModelBase(parent)
+{
+}
+
+ImageChooserModel::~ImageChooserModel()
+{
+}
+
+bool ImageChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_IMAGE;
+}
+
+const QVector<ChooserModelBase::FixedItem> ImageChooserModel::getFixedItems() const
+{
+ static const QVector<FixedItem> items = { { OBJTYPE_IMAGE, tr("[None]") } };
+ return items;
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.h b/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.h
new file mode 100644
index 00000000..288846fd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ImageChooserModel.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IMAGECHOOSERMODEL_H
+#define IMAGECHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class ImageChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit ImageChooserModel(QObject *parent = nullptr);
+ virtual ~ImageChooserModel();
+
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+};
+
+#endif // IMAGECHOOSERMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.cpp b/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.cpp
new file mode 100644
index 00000000..a3a35217
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ImageChooserView.h"
+#include "ImageChooserModel.h"
+#include "StudioPreferences.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ImageChooserView::ImageChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new ImageChooserModel(this))
+{
+ setWindowTitle(tr("Images"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ImageChooserView::initialize);
+}
+
+void ImageChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_resDir"_L1,
+ resourceImageUrl());
+ rootContext()->setContextProperty("_imageChooserView"_L1, this);
+ rootContext()->setContextProperty("_imageChooserModel"_L1, m_model);
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/ImageChooser.qml"_L1));
+}
+
+QSize ImageChooserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+void ImageChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int ImageChooserView::handle() const
+{
+ return m_handle;
+}
+
+void ImageChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int ImageChooserView::instance() const
+{
+ return m_instance;
+}
+
+void ImageChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &ImageChooserView::close);
+}
+
+void ImageChooserView::showEvent(QShowEvent *event)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ UICDM::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ const auto guid = UICDM::get<UICDM::SLong4>(value);
+
+ const auto imageInstance = doc->GetDocumentReader().GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ const QString path = doc->GetDocumentReader().GetSourcePath(imageInstance).toQString();
+ m_model->setCurrentFile(path);
+ } else {
+ m_model->setCurrentFile(tr("[None]"));
+ }
+
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.h b/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.h
new file mode 100644
index 00000000..f1738aa9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ImageChooserView.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef IMAGECHOOSERVIEW_H
+#define IMAGECHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+class ImageChooserModel;
+
+class ImageChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit ImageChooserView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+
+Q_SIGNALS:
+ void imageSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ ImageChooserModel *m_model = nullptr;
+};
+
+#endif // IMAGECHOOSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectableBase.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectableBase.cpp
new file mode 100644
index 00000000..1c33eb23
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectableBase.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "InspectableBase.h"
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectableBase.h b/src/Authoring/Studio/Palettes/Inspector/InspectableBase.h
new file mode 100644
index 00000000..34973393
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectableBase.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef __INSPECTABLEBASE_H__
+#define __INSPECTABLEBASE_H__
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CInspectorGroup;
+
+#include "Core.h"
+#include "StudioObjectTypes.h"
+
+//==============================================================================
+/**
+ * Parent class of Inspectable types that will appear in the Inspector Palette.
+ */
+class CInspectableBase
+{
+protected:
+ CCore *m_Core; ///<
+
+public:
+ CInspectableBase(CCore *inCore)
+ : m_Core(inCore)
+ {
+ }
+ virtual ~CInspectableBase() {}
+
+ // Interface
+ virtual EStudioObjectType GetObjectType() = 0;
+ // virtual std::wstring GetTypeString() const { return L""; }
+ virtual Q3DStudio::CString GetName() = 0;
+ virtual long GetGroupCount() = 0;
+ virtual CInspectorGroup *GetGroup(long inIndex) = 0;
+ virtual bool IsValid() const = 0;
+ virtual bool IsMaster() = 0;
+};
+
+#endif // #ifndef __INSPECTABLEBASE_H__
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp
new file mode 100644
index 00000000..474fc3e4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp
@@ -0,0 +1,862 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QFileInfo>
+
+#include <functional>
+
+#include "InspectorControlModel.h"
+#include "Core.h"
+#include "Doc.h"
+#include "ControlGraphIterators.h"
+#include "InspectorGroup.h"
+#include "UICDMInspectorGroup.h"
+#include "UICDMInspectorRow.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMInspectable.h"
+#include "UICDMDataCore.h"
+#include "StudioApp.h"
+#include "IDocumentEditor.h"
+#include "Control.h"
+#include "ControlData.h"
+#include "UICDMMetaData.h"
+#include "UICDMSignals.h"
+#include "CmdDataModelDeanimate.h"
+#include "GuideInspectable.h"
+#include "UICDMDataTypes.h"
+#include "IObjectReferenceHelper.h"
+#include "UICDMXML.h"
+#include "UICDMStringTable.h"
+#include "UICFileTools.h"
+#include "UICDMSlideCore.h"
+#include "SlideSystem.h"
+#include "UICDMMaterialInspectable.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentReader.h"
+#include "IStudioRenderer.h"
+
+static QStringList renderableItems()
+{
+ QStringList renderables;
+ renderables.push_back(QObject::tr("No renderable item"));
+ const CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ Q3DStudio::CString docDir = doc->GetDocumentDirectory();
+ Q3DStudio::CFilePath fullDocPath = doc->GetDocumentPath().GetAbsolutePath();
+ Q3DStudio::CString docFilename = fullDocPath.GetFileName();
+ // First step, find uia file, parse and pull out renderable asset id's but ignoring the
+ // current presentation.
+ std::vector<Q3DStudio::CFilePath> dirFiles;
+ Q3DStudio::CFilePath thePath(docDir);
+ thePath.ListFilesAndDirectories(dirFiles);
+ for (size_t idx = 0, end = dirFiles.size(); idx < end; ++idx) {
+ if (!dirFiles[idx].IsFile())
+ continue;
+
+ Q3DStudio::CString ext = dirFiles[idx].GetExtension();
+ if (!ext.CompareNoCase("uia"))
+ continue;
+
+ UICDM::TStringTablePtr theStringTable
+ = UICDM::IStringTable::CreateStringTable();
+ std::shared_ptr<UICDM::IDOMFactory> theDomFact =
+ UICDM::IDOMFactory::CreateDOMFactory(theStringTable);
+ Q3DStudio::CString fullFile = dirFiles[idx];
+ qt3ds::foundation::CFileSeekableIOStream theStream(
+ fullFile, qt3ds::foundation::FileReadFlags());
+
+ UICDM::SDOMElement *theElem
+ = UICDM::CDOMSerializer::Read(*theDomFact, theStream);
+ if (theElem) {
+ std::shared_ptr<UICDM::IDOMReader> theReader =
+ UICDM::IDOMReader::CreateDOMReader(*theElem, theStringTable,
+ theDomFact);
+ if (theReader->MoveToFirstChild("assets")) {
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ if (UICDM::AreEqual(theReader->GetElementName(), L"presentation") ||
+ UICDM::AreEqual(theReader->GetElementName(), L"presentation-qml")) {
+ UICDM::TXMLStr src = nullptr;
+ UICDM::TXMLStr id = nullptr;
+ theReader->Att("src", src);
+ theReader->Att("id", id);
+ if (docFilename != src.c_str())
+ renderables.push_back(QString::fromLatin1(id.c_str()));
+ } else if (UICDM::AreEqual(theReader->GetElementName(),
+ L"renderplugin")) {
+ const wchar_t *id = nullptr;
+ theReader->UnregisteredAtt(L"id", id);
+ renderables.push_back(QString::fromWCharArray(id));
+ }
+ }
+ }
+ }
+ }
+ // second step, find the renderable plugins.
+ {
+ Q3DStudio::CFilePath pluginDir
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(docDir, "plugins");
+ if (pluginDir.Exists() && pluginDir.IsDirectory()) {
+ std::vector<Q3DStudio::CFilePath> dirFiles;
+ pluginDir.ListFilesAndDirectories(dirFiles);
+ for (size_t idx = 0, end = dirFiles.size(); idx < end; ++idx) {
+ if (dirFiles[idx].IsFile()) {
+ Q3DStudio::CFilePath relPath =
+ Q3DStudio::CFilePath::GetRelativePathFromBase(docDir, dirFiles[idx]);
+ renderables.push_back(relPath.toQString());
+ }
+ }
+ }
+ }
+ std::sort(renderables.begin() + 1, renderables.end());
+ return renderables;
+}
+
+static std::pair<bool, bool> getSlideCharacteristics(UICDM::CUICDMInstanceHandle instance,
+ const UICDM::ISlideCore &slideCore,
+ const UICDM::ISlideSystem &slideSystem)
+{
+ // Get the slide from the instance.
+ UICDM::CUICDMSlideHandle slide = slideCore.GetSlideByInstance(instance);
+ UICDM::CUICDMSlideHandle master = slideSystem.GetMasterSlide(slide);
+ int index = (int)slideSystem.GetSlideIndex(slide);
+ int count = (int)slideSystem.GetSlideCount(master);
+ bool hasNextSlide = index > 0 && index < count - 1;
+ bool hasPreviousSlide = index > 1;
+ return std::make_pair(hasNextSlide, hasPreviousSlide);
+}
+
+InspectorControlModel::InspectorControlModel(QObject *parent)
+ : QAbstractListModel(parent)
+ , m_UpdatableEditor(*g_StudioApp.GetCore()->GetDoc())
+{
+}
+
+void InspectorControlModel::setInspectable(CInspectableBase *inInspectable)
+{
+ const auto signalProvider
+ = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider();
+
+ if (m_notifier.get() == nullptr) {
+ m_notifier = signalProvider->ConnectInstancePropertyValue(
+ std::bind(&InspectorControlModel::notifyInstancePropertyValue,
+ this, std::placeholders::_1, std::placeholders::_2));
+ }
+ if (m_slideNotifier.get() == nullptr) {
+ m_slideNotifier = signalProvider->ConnectSlideRearranged(
+ std::bind(&InspectorControlModel::onSlideRearranged, this,
+ std::placeholders::_1, std::placeholders::_2,
+ std::placeholders::_3));
+ }
+
+ if (m_inspectableBase != inInspectable) {
+ m_inspectableBase = inInspectable;
+ rebuildTree();
+ }
+}
+
+void InspectorControlModel::notifyInstancePropertyValue(UICDM::CUICDMInstanceHandle inHandle,
+ UICDM::CUICDMPropertyHandle inProperty)
+{
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ bool changed = false;
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ UICDM::CUICDMInstanceHandle imageInstance;
+ if (property->m_dataType == UICDM::DataModelDataType::Long4
+ && property->m_property.Valid()) {
+ imageInstance = doc->GetDocumentReader().GetImageInstanceForProperty(
+ property->m_instance, property->m_property);
+ }
+ if (property->m_property == inProperty || imageInstance == inHandle) {
+ updatePropertyValue(property);
+ changed = true;
+ }
+ }
+ }
+ if (changed)
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+QVariant InspectorControlModel::getPropertyValue(long instance, int handle)
+{
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property == UICDM::CDataModelHandle(handle))
+ return property->m_value;
+ }
+ }
+ return {};
+}
+
+void InspectorControlModel::setMaterials(std::vector<Q3DStudio::CFilePath> &materials)
+{
+ m_materials.clear();
+ const Q3DStudio::CString base = g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory();
+
+ for (Q3DStudio::CFilePath path : materials) {
+
+ const QString relativePath = path.toQString();
+ const Q3DStudio::CFilePath absolutePath
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(base, path);
+
+ const QString name = g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
+ .GetCustomMaterialName(absolutePath).toQString();
+
+ m_materials.push_back({name, relativePath});
+ }
+}
+
+InspectorControlBase* InspectorControlModel::createMaterialItem(CUICDMInspectable *inspectable,
+ int groupIndex)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Material Type");
+
+ CClientDataModelBridge *theBridge = studio->GetClientDataModelBridge();
+ EStudioObjectType theType = theBridge->GetObjectType(item->m_instance);
+ item->m_dataType = UICDM::DataModelDataType::StringRef;
+ item->m_propertyType = UICDM::AdditionalMetaDataType::None;
+ item->m_tooltip = tr("Type of material being used or custom material");
+
+ item->m_animatable = false;
+
+ QStringList values;
+ values.push_back(tr("Standard Material"));
+ values.push_back(tr("Referenced Material"));
+
+ const QString sourcePath = theBridge->GetSourcePath(item->m_instance).toQString();
+
+ switch (theType) {
+
+ case OBJTYPE_MATERIAL:
+ item->m_value = tr("Standard Material");
+ break;
+
+ case OBJTYPE_REFERENCEDMATERIAL:
+ item->m_value = tr("Referenced Material");
+ break;
+ }
+
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx) {
+ values.push_back(m_materials[matIdx].m_name);
+ if (m_materials[matIdx].m_relativePath == sourcePath)
+ item->m_value = values.last();
+ }
+
+ item->m_values = values;
+
+ return item;
+}
+
+InspectorControlBase* InspectorControlModel::createItem(CUICDMInspectable *inspectable,
+ Q3DStudio::CUICDMInspectorRow *row,
+ int groupIndex)
+{
+ return createItem(inspectable, row->GetMetaDataPropertyInfo(), groupIndex);
+}
+
+InspectorControlBase* InspectorControlModel::createItem(CUICDMInspectable *inspectable,
+ const UICDM::SMetaDataPropertyInfo &metaProperty,
+ int groupIndex)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ if (metaProperty.m_IsHidden)
+ return nullptr;
+
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_property = metaProperty.m_Property;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ Q3DStudio::CString title;
+ title.Assign(metaProperty.m_FormalName.c_str());
+ if (title.IsEmpty())
+ title.Assign(metaProperty.m_Name.c_str());
+ item->m_title = title.toQString();
+
+ const auto propertySystem = studio->GetPropertySystem();
+ item->m_dataType = propertySystem->GetDataType(metaProperty.m_Property);
+ item->m_propertyType = static_cast<UICDM::AdditionalMetaDataType::Value>
+ (propertySystem->GetAdditionalMetaDataType(item->m_instance, metaProperty.m_Property));
+ item->m_tooltip = Q3DStudio::CString(metaProperty.m_Description.c_str()).toQString();
+
+ item->m_animatable = metaProperty.m_Animatable &&
+ studio->GetAnimationSystem()->IsPropertyAnimatable(item->m_instance,
+ metaProperty.m_Property);
+ if (item->m_animatable) {
+ item->m_animated = studio->GetAnimationSystem()->IsPropertyAnimated(item->m_instance,
+ metaProperty.m_Property);
+
+ // Update the Animate Toggle on undo/redo
+ auto signalProvider = studio->GetFullSystemSignalProvider();
+ item->m_connections.push_back(signalProvider->ConnectAnimationCreated(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+
+ item->m_connections.push_back(signalProvider->ConnectAnimationDeleted(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+ }
+
+ // synchronize the value itself
+ updatePropertyValue(item);
+ return item;
+}
+
+UICDM::SValue InspectorControlModel::currentPropertyValue(long instance, int handle)
+{
+ UICDM::SValue value;
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto propertySystem = studioSystem->GetPropertySystem();
+ propertySystem->GetInstancePropertyValue(instance, handle, value);
+
+ return value;
+}
+
+void InspectorControlModel::updateAnimateToggleState(InspectorControlBase* inItem)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ bool animated = studio->GetAnimationSystem()->IsPropertyAnimated(inItem->m_instance,
+ inItem->m_property);
+ if (animated != inItem->m_animated) {
+ inItem->m_animated = animated;
+ Q_EMIT inItem->animatedChanged();
+ }
+}
+
+bool InspectorControlModel::isTreeRebuildRequired(CInspectableBase* inspectBase) const
+{
+ if (inspectBase != m_inspectableBase)
+ return true;
+
+ long theCount = m_inspectableBase->GetGroupCount();
+ if (m_groupElements.size() != theCount)
+ return true;
+
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ const CInspectorGroup *theInspectorGroup = m_inspectableBase->GetGroup(theIndex);
+ if (m_groupElements.at(theIndex).groupTitle != theInspectorGroup->GetName())
+ return true;
+ }
+
+ return false;
+}
+
+bool InspectorControlModel::isGroupRebuildRequired(CInspectableBase* inspectable, int theIndex) const
+{
+ Q_ASSERT(theIndex < m_groupElements.size());
+ const CInspectorGroup *theInspectorGroup = inspectable->GetGroup(theIndex);
+ const auto existingGroup = m_groupElements.at(theIndex);
+ if (existingGroup.groupTitle != theInspectorGroup->GetName())
+ return true;
+
+ if (const auto cdmInspectable = dynamic_cast<CUICDMInspectable *>(inspectable)) {
+ int existingIndex = 0;
+ if (const auto group = dynamic_cast<const CUICDMInspectorGroup *>(theInspectorGroup)) {
+ const auto materialGroup
+ = dynamic_cast<const UICDMMaterialInspectorGroup *>(group);
+ if (materialGroup && materialGroup->isMaterialGroup()) {
+ auto i = existingGroup.controlElements.at(existingIndex++).value<InspectorControlBase*>();
+ if (i->m_instance != cdmInspectable->GetGroupInstance(theIndex))
+ return true;
+ }
+
+ if ((existingGroup.controlElements.size() - existingIndex) != group->GetRows().size())
+ return true;
+
+ for (const auto row : group->GetRows()) {
+ auto i = existingGroup.controlElements.at(existingIndex++).value<InspectorControlBase*>();
+ if (i->m_instance != cdmInspectable->GetGroupInstance(theIndex))
+ return true;
+
+ if (i->m_property != row->GetMetaDataPropertyInfo().m_Property)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+auto InspectorControlModel::computeTree(CInspectableBase* inspectBase)
+ -> QVector<GroupInspectorControl>
+{
+ QVector<GroupInspectorControl> result;
+
+ if (inspectBase) {
+ long theCount = inspectBase->GetGroupCount();
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ result.append(computeGroup(inspectBase, theIndex));
+ }
+ }
+
+ return result;
+}
+
+auto InspectorControlModel::computeGroup(CInspectableBase* inspectable,
+ int theIndex)
+ -> GroupInspectorControl
+{
+ CInspectorGroup* theInspectorGroup = inspectable->GetGroup(theIndex);
+ GroupInspectorControl result;
+ result.groupTitle = theInspectorGroup->GetName();
+
+ if (const auto cdmInspectable = dynamic_cast<CUICDMInspectable *>(inspectable)) {
+ if (const auto group = dynamic_cast<CUICDMInspectorGroup *>(theInspectorGroup)) {
+ const auto materialGroup
+ = dynamic_cast<UICDMMaterialInspectorGroup *>(group);
+ if (materialGroup && materialGroup->isMaterialGroup()) {
+ InspectorControlBase *item = createMaterialItem(cdmInspectable, theIndex);
+ if (item) {
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ }
+ for (const auto row : group->GetRows()) {
+ InspectorControlBase *item = createItem(cdmInspectable, row, theIndex);
+ if (!item)
+ continue;
+
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ }
+ } else if (dynamic_cast<SGuideInspectableImpl *>(inspectable)) {
+ //KDAB_FIXME: load row element (How ?)
+ }
+
+ return result;
+}
+
+void InspectorControlModel::rebuildTree()
+{
+ beginResetModel();
+ m_groupElements = computeTree(m_inspectableBase);
+ endResetModel();
+}
+
+int InspectorControlModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_groupElements.count();
+}
+
+void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) const
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto propertySystem = studioSystem->GetPropertySystem();
+ UICDM::SValue value;
+ const auto instance = element->m_instance;
+ propertySystem->GetInstancePropertyValue(instance, element->m_property, value);
+
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ const auto info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, element->m_property));
+ switch (element->m_dataType) {
+ case UICDM::DataModelDataType::String:
+ element->m_value = UICDM::get<QString>(value);
+ //intentional fall-through
+ case UICDM::DataModelDataType::StringOrInt:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::StringList) {
+ QStringList stringlist = UICDM::get<QStringList>(info->m_MetaDataData);
+ auto slideSystem = studioSystem->GetSlideSystem();
+
+ if (element->m_title == QStringLiteral("Play Mode")) {
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide && !hasPreviousSlide)
+ stringlist.removeAll("Play Through To...");
+ } else if (element->m_title == QStringLiteral("Play Through To")) {
+ // the code duplication is intentional as we may ask for slide characteristics
+ // only if the property refers to slides
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ QString listOpt;
+ int selectedSlideHandle = 0;
+ int selectedIndex = -1;
+ UICDM::SStringOrInt stringOrInt = UICDM::get<UICDM::SStringOrInt>(value);
+ if (stringOrInt.GetType() == UICDM::SStringOrIntTypes::String)
+ listOpt = QString::fromWCharArray(UICDM::get<UICDM::TDataStrPtr>
+ (stringOrInt.m_Value)->GetData());
+ else
+ selectedSlideHandle = UICDM::get<long>(stringOrInt.m_Value);
+
+ selectedIndex = stringlist.indexOf(listOpt);
+ // Add the slide names (exclude the master slide)
+ auto bridge = studioSystem->GetClientDataModelBridge();
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideCount = (long)slideSystem->GetSlideCount(masterSlide);
+ for (long slideIndex = 1; slideIndex < slideCount; ++slideIndex) {
+ auto currentSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ auto currentInstance = slideSystem->GetSlideInstance(currentSlide);
+
+ QString slideName = bridge->GetName(currentInstance).toQString();
+ //hack to add a separator before the item
+ if (slideIndex == 1 && itemCount > 0)
+ slideName += "|separator";
+ stringlist.append(slideName);
+
+ if (currentSlide.GetHandleValue() == selectedSlideHandle)
+ selectedIndex = slideIndex + itemCount - 1;
+ }
+
+ element->m_value = QString(selectedIndex > 0 ? stringlist[selectedIndex]
+ : stringlist.first()).replace("|separator", "");
+ }
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Import) {
+ QStringList stringlist = UICDM::get<QStringList>(info->m_MetaDataData);
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Renderable) {
+ element->m_values = renderableItems();
+ if (element->m_value.toString().isEmpty())
+ element->m_value = element->m_values.toStringList().at(0);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::MultiLine) {
+ element->m_value = UICDM::get<QString>(value);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Font) {
+ std::vector<Q3DStudio::CString> fontNames;
+ g_StudioApp.GetCore()->GetDoc()->GetProjectFonts(fontNames);
+ QStringList possibleValues;
+ for (const auto &fontName: fontNames)
+ possibleValues.append(fontName.toQString());
+ element->m_values = possibleValues;
+ element->m_value = UICDM::get<QString>(value);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Mesh) {
+ QString meshValue = UICDM::get<QString>(value);
+ Q3DStudio::CFilePath theSelectionItem(Q3DStudio::CString::fromQString(meshValue));
+ Q3DStudio::CFilePath theSelectionWithoutId(theSelectionItem.GetPathWithoutIdentifier());
+ if (theSelectionWithoutId.size())
+ element->m_value = theSelectionWithoutId.GetFileName().toQString();
+ else
+ element->m_value = theSelectionItem.GetIdentifier().toQString();
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Texture) {
+ QFileInfo fileInfo(UICDM::get<QString>(value));
+ element->m_value = fileInfo.fileName();
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::PathBuffer) {
+ element->m_value = UICDM::get<QString>(value);
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " element->m_propertyType : "
+ << element->m_propertyType;
+ }
+ break;
+ case UICDM::DataModelDataType::StringRef:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::None) {
+ element->m_value = UICDM::get<QString>(value);
+ }
+ break;
+ case UICDM::DataModelDataType::Bool:
+ element->m_value = UICDM::get<bool>(value);
+ break;
+ case UICDM::DataModelDataType::Long4:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::Image) {
+ UICDM::Option<UICDM::SLong4> guid = UICDM::get<UICDM::SLong4>(value);
+ UICDM::CUICDMInstanceHandle imageInstance = doc->GetDocumentReader()
+ .GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ Q3DStudio::CString path = doc->GetDocumentReader().GetSourcePath(imageInstance);
+ Q3DStudio::CFilePath relPath(path);
+ element->m_value = QVariant(relPath.GetFileName().toQString());
+ } else {
+ element->m_value = QVariant(QString(""));
+ }
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " " << element->m_title;
+ }
+ break;
+ case UICDM::DataModelDataType::Long:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::Range) {
+ element->m_value = UICDM::get<int>(value);
+ const UICDM::SMetaDataRange ranges = UICDM::get<UICDM::SMetaDataRange>(info->m_MetaDataData);
+ const QList<double> rangesValues{ranges.m_Min, ranges.m_Max};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ }
+ else if (element->m_propertyType == UICDM::AdditionalMetaDataType::ShadowMapResolution) {
+ element->m_value = UICDM::get<int>(value);
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType;
+ }
+ break;
+ case UICDM::DataModelDataType::Float3:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::Color) {
+ element->m_value = UICDM::get<QColor>(value);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Rotation) {
+ const QVector3D theFloat3 = UICDM::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::None) {
+ const QVector3D theFloat3 = UICDM::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ }
+ break;
+ case UICDM::DataModelDataType::Float:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::None) {
+ element->m_value = UICDM::get<float>(value);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::Range) {
+ element->m_value = UICDM::get<float>(value);
+ const UICDM::SMetaDataRange ranges = UICDM::get<UICDM::SMetaDataRange>(info->m_MetaDataData);
+ const QList<double> rangesValues{ranges.m_Min, ranges.m_Max};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ } else if (element->m_propertyType == UICDM::AdditionalMetaDataType::FontSize) {
+ element->m_value = UICDM::get<float>(value);
+ }
+ break;
+ case UICDM::DataModelDataType::ObjectRef:
+ if (element->m_propertyType == UICDM::AdditionalMetaDataType::ObjectRef) {
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ if (objRefHelper) {
+ UICDM::CUICDMInstanceHandle refInstance = objRefHelper->Resolve(value, instance);
+ element->m_value = objRefHelper->LookupObjectFormalName(refInstance).toQString();
+ }
+ }
+ break;
+ default:
+ qWarning() << "TODO: InspectorControlModel::updatePropertyValue: I've no idea how to handle this datatype"
+ << element->m_dataType;
+ break;
+ }
+ Q_EMIT element->valueChanged();
+ Q_EMIT element->valuesChanged();
+}
+
+void InspectorControlModel::refreshRenderables()
+{
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_propertyType == UICDM::AdditionalMetaDataType::Renderable)
+ updatePropertyValue(property);
+ }
+ }
+}
+
+void InspectorControlModel::refresh()
+{
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property.Valid())
+ updatePropertyValue(property);
+ }
+ }
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+void InspectorControlModel::setMaterialTypeValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle);
+ const QString typeValue = value.toString();
+ Q3DStudio::CString v;
+
+ if (typeValue == tr("Standard Material")) {
+ v = Q3DStudio::CString("Standard Material");
+ } else if (typeValue == tr("Referenced Material")) {
+ v = Q3DStudio::CString("Referenced Material");
+ } else {
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx) {
+ if (m_materials[matIdx].m_name == typeValue) {
+ v = Q3DStudio::CString::fromQString(m_materials[matIdx].m_relativePath);
+ break;
+ }
+ }
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(),
+ QObject::tr("Set Property"))->SetMaterialType(instance, v);
+}
+
+void InspectorControlModel::setRenderableValue(long instance, int handle, const QVariant &value)
+{
+ UICDM::SValue oldValue = currentPropertyValue(instance, handle);
+
+ QString v = value.toString();
+ if (v == QObject::tr("No renderable item"))
+ v = QString();
+
+ if (v == UICDM::get<QString>(oldValue))
+ return;
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValueAsRenderable(instance, handle,
+ Q3DStudio::CString::fromQString(v));
+}
+
+void InspectorControlModel::setPropertyValue(long instance, int handle, const QVariant &value, bool commit)
+{
+ UICDM::SValue oldValue = currentPropertyValue(instance, handle);
+ UICDM::SValue v = value;
+
+ if (v == oldValue)
+ return;
+
+ // some properties may initialize OpenGL resources (e.g. loading meshes will
+ // initialize vertex buffers), so the renderer's OpenGL context must be current
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.GetRenderer());
+ theRenderer.MakeContextCurrent();
+
+ m_UpdatableEditor.EnsureEditor(L"Set Property", __FILE__, __LINE__)
+ .SetInstancePropertyValue(instance, handle, v);
+
+ theRenderer.ReleaseContext();
+
+ m_UpdatableEditor.FireImmediateRefresh(instance);
+
+ if (commit) {
+ m_UpdatableEditor.CommitEditor();
+
+ if (isTreeRebuildRequired(m_inspectableBase)) {
+ rebuildTree();
+ } else {
+ // group strucutre is intact, let's walk to see which rows changed
+ long theCount = m_inspectableBase->GetGroupCount();
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ if (isGroupRebuildRequired(m_inspectableBase, theIndex)) {
+ m_groupElements[theIndex] = computeGroup(m_inspectableBase, theIndex);
+ Q_EMIT dataChanged(index(theIndex), index(theIndex));
+ }
+ }
+ }
+
+ } // of commit
+}
+
+void InspectorControlModel::setSlideSelection(long instance, int handle, int index,
+ const QStringList &list)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ const auto info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, handle));
+ QStringList stringlist = UICDM::get<QStringList>(info->m_MetaDataData);
+
+ auto slideSystem = studioSystem->GetSlideSystem();
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ UICDM::SStringOrInt newSelectedData;
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ if (index < itemCount) {
+ newSelectedData = UICDM::SStringOrInt(std::make_shared<UICDM::CDataStr>
+ (Q3DStudio::CString::fromQString(list[index]).c_str()));
+ } else {
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideIndex = index - itemCount + 1;
+ auto newSelectedSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ newSelectedData = UICDM::SStringOrInt((long)newSelectedSlide);
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, newSelectedData);
+}
+
+void InspectorControlModel::setPropertyAnimated(long instance, int handle, bool animated)
+{
+ CCmd* cmd = nullptr;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ if (animated)
+ cmd = new CCmdDataModelAnimate(doc, instance, handle);
+ else
+ cmd = new CCmdDataModelDeanimate(doc, instance, handle);
+
+ g_StudioApp.GetCore()->ExecuteCommand(cmd);
+}
+
+void InspectorControlModel::onSlideRearranged(const UICDM::CUICDMSlideHandle &inMaster,
+ int inOldIndex, int inNewIndex)
+{
+ Q_UNUSED(inMaster);
+ Q_UNUSED(inOldIndex);
+ Q_UNUSED(inNewIndex);
+ rebuildTree();
+}
+
+QVariant InspectorControlModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return {};
+
+ const auto row = index.row();
+
+ switch (role) {
+ case GroupValuesRole:
+ return m_groupElements.at(row).controlElements;
+ case GroupTitleRole:
+ return m_groupElements.at(row).groupTitle;
+ }
+ return {};
+}
+
+QHash<int, QByteArray> InspectorControlModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(GroupValuesRole, "values");
+ names.insert(GroupTitleRole, "title");
+ return names;
+}
+
+InspectorControlBase::~InspectorControlBase()
+{
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h
new file mode 100644
index 00000000..20718039
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INSPECTORCONTROLMODEL_H
+#define INSPECTORCONTROLMODEL_H
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qvector.h>
+
+#include "UICDMValue.h"
+#include "UICDMMetaDataValue.h"
+#include "UICDMMetaDataTypes.h"
+#include "UICFileTools.h"
+
+#include "IDocumentEditor.h"
+
+class CInspectableBase;
+class CUICDMInspectable;
+class SGuideInspectableImpl;
+
+namespace UICDM {
+class ISignalConnection;
+typedef std::shared_ptr<ISignalConnection> TSignalConnectionPtr;
+}
+
+namespace Q3DStudio
+{
+class CUICDMInspectorRow;
+}
+
+class InspectorControlBase : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(UICDM::DataModelDataType::Value dataType MEMBER m_dataType CONSTANT)
+ Q_PROPERTY(UICDM::AdditionalMetaDataType::Value propertyType MEMBER m_propertyType CONSTANT)
+ Q_PROPERTY(QVariant value MEMBER m_value NOTIFY valueChanged)
+ Q_PROPERTY(QVariant values MEMBER m_values NOTIFY valuesChanged)
+ Q_PROPERTY(QString title MEMBER m_title CONSTANT)
+ Q_PROPERTY(QString toolTip MEMBER m_tooltip CONSTANT)
+ Q_PROPERTY(int instance MEMBER m_instance CONSTANT)
+ Q_PROPERTY(int handle MEMBER m_property CONSTANT)
+
+ Q_PROPERTY(bool animatable MEMBER m_animatable CONSTANT)
+ Q_PROPERTY(bool animated MEMBER m_animated NOTIFY animatedChanged)
+
+public:
+ virtual ~InspectorControlBase();
+
+Q_SIGNALS:
+ void valueChanged();
+ void valuesChanged();
+ void animatedChanged();
+
+public:
+ UICDM::DataModelDataType::Value m_dataType;
+ UICDM::AdditionalMetaDataType::Value m_propertyType;
+ QVariant m_value;
+ QVariant m_values;
+ QString m_title;
+ QString m_tooltip;
+
+ UICDM::CUICDMInstanceHandle m_instance;
+ UICDM::CUICDMPropertyHandle m_property;
+
+ bool m_animatable = false;
+ bool m_animated = false;
+
+ std::vector<UICDM::TSignalConnectionPtr> m_connections;
+};
+
+class InspectorControlModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit InspectorControlModel(QObject *parent);
+ ~InspectorControlModel() = default;
+
+ enum Roles {
+ GroupValuesRole = Qt::UserRole + 1,
+ GroupTitleRole
+ };
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ void setInspectable(CInspectableBase *inInspectable);
+ void setMaterials(std::vector<Q3DStudio::CFilePath> &materials);
+ void refreshRenderables();
+ void refresh();
+
+ QVariant getPropertyValue(long instance, int handle);
+
+ Q_INVOKABLE void setMaterialTypeValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setRenderableValue(long instance, int handle, const QVariant &value);
+ Q_INVOKABLE void setPropertyValue(long instance, int handle, const QVariant &value, bool commit = true);
+ Q_INVOKABLE void setSlideSelection(long instance, int handle, int index,
+ const QStringList &list);
+ Q_INVOKABLE void setPropertyAnimated(long instance, int handle, bool animated);
+
+private:
+ void onSlideRearranged(const UICDM::CUICDMSlideHandle &inMaster, int inOldIndex,
+ int inNewIndex);
+
+
+ struct GroupInspectorControl {
+ QString groupTitle;
+ QVariantList controlElements;
+
+ ~GroupInspectorControl() {
+ //for (auto element : controlElements)
+ // element.value<QObject *>()->deleteLater();
+ }
+ };
+
+ mutable QVector<GroupInspectorControl> m_groupElements;
+ CInspectableBase *m_inspectableBase = nullptr;
+
+ struct MaterialEntry
+ {
+ QString m_name;
+ QString m_relativePath;
+ };
+
+ std::vector<MaterialEntry> m_materials;
+
+ Q3DStudio::CUpdateableDocumentEditor m_UpdatableEditor;
+
+ void updatePropertyValue(InspectorControlBase *element) const;
+ void rebuildTree();
+ void notifyInstancePropertyValue(UICDM::CUICDMInstanceHandle, UICDM::CUICDMPropertyHandle inProperty);
+ void updateAnimateToggleState(InspectorControlBase* inItem);
+
+ std::shared_ptr<UICDM::ISignalConnection> m_notifier;
+ std::shared_ptr<UICDM::ISignalConnection> m_slideNotifier;
+
+ InspectorControlBase *createMaterialItem(CUICDMInspectable *inspectable, int groupIndex);
+ InspectorControlBase *createItem(CUICDMInspectable *inspectable,
+ Q3DStudio::CUICDMInspectorRow *row, int groupIndex);
+ InspectorControlBase *createItem(CUICDMInspectable *inspectable,
+ const UICDM::SMetaDataPropertyInfo &metaProperty,
+ int groupIndex);
+
+ UICDM::SValue currentPropertyValue(long instance, int handle);
+
+ QVector<GroupInspectorControl> computeTree(CInspectableBase *inspectBase);
+ bool isTreeRebuildRequired(CInspectableBase *inspectBase) const;
+
+ GroupInspectorControl computeGroup(CInspectableBase* inspectBase, int theIndex);
+ bool isGroupRebuildRequired(CInspectableBase *inspectable, int theIndex) const;
+
+};
+
+#endif // INSPECTORCONTROLMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp
new file mode 100644
index 00000000..3079c02a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp
@@ -0,0 +1,429 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorControlView.h"
+#include "Literals.h"
+#include "CColor.h"
+#include "UICDMValue.h"
+#include "StudioUtils.h"
+#include "InspectorControlModel.h"
+#include "StudioPreferences.h"
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+#include <QtWidgets/qmenu.h>
+#include "Core.h"
+#include "Doc.h"
+#include "IDocumentEditor.h"
+#include "ImageChooserModel.h"
+#include "ImageChooserView.h"
+#include "MeshChooserView.h"
+#include "TextureChooserView.h"
+#include "InspectableBase.h"
+#include "StudioApp.h"
+#include "ObjectListModel.h"
+#include "ObjectBrowserView.h"
+#include "IDirectoryWatchingSystem.h"
+#include "StandardExtensions.h"
+#include "FileChooserView.h"
+#include "IObjectReferenceHelper.h"
+#include "UICDMStudioSystem.h"
+#include "StudioFullSystem.h"
+
+InspectorControlView::InspectorControlView(QWidget *parent)
+ : QQuickWidget(parent),
+ TabNavigable(),
+ m_inspectorControlModel(new InspectorControlModel(this)),
+ m_instance(0),
+ m_handle(0)
+{
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &InspectorControlView::initialize);
+ auto dispatch = g_StudioApp.GetCore()->GetDispatch();
+ dispatch->AddPresentationChangeListener(this);
+ dispatch->AddDataModelListener(this);
+
+ m_selectionChangedConnection = g_StudioApp.GetCore()->GetDispatch()->ConnectSelectionChange(
+ std::bind(&InspectorControlView::OnSelectionSet, this, std::placeholders::_1));
+}
+
+const wchar_t **AllSupportedExtensionsList()
+{
+ static const wchar_t *extensions[] = {
+ L"png", L"jpg", L"jpeg", L"dds", L"hdr",
+ L"mesh", L"import", L"path",
+ L"material",
+ nullptr
+ };
+ return extensions;
+}
+
+static bool isInList(const wchar_t **list, const Q3DStudio::CString &inStr)
+{
+ for (const wchar_t **item = list; item && *item; ++item) {
+ if (inStr.Compare(*item, Q3DStudio::CString::ENDOFSTRING, false))
+ return true;
+ }
+ return false;
+}
+
+void InspectorControlView::filterMaterials(std::vector<Q3DStudio::CFilePath> &materials)
+{
+ static const wchar_t *extensions[] = {
+ L"material",
+ nullptr
+ };
+ for (size_t i = 0; i < m_fileList.size(); ++i) {
+ if (isInList(extensions, m_fileList[i].GetExtension()))
+ materials.push_back(m_fileList[i]);
+ }
+}
+
+void InspectorControlView::OnNewPresentation()
+{
+ m_DirectoryConnection = g_StudioApp.GetDirectoryWatchingSystem().AddDirectory(
+ g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory().toQString(),
+ std::bind(&InspectorControlView::onFilesChanged, this, std::placeholders::_1));
+}
+
+void InspectorControlView::OnClosingPresentation()
+{
+ m_fileList.clear();
+}
+
+void InspectorControlView::OnLoadedSubPresentation()
+{
+ m_DirectoryConnection = g_StudioApp.GetDirectoryWatchingSystem().AddDirectory(
+ g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory().toQString(),
+ std::bind(&InspectorControlView::onFilesChanged, this, std::placeholders::_1));
+}
+
+void InspectorControlView::onFilesChanged(
+ const Q3DStudio::TFileModificationList &inFileModificationList)
+{
+ const wchar_t **extensions = AllSupportedExtensionsList();
+ for (size_t idx = 0, end = inFileModificationList.size(); idx < end; ++idx) {
+ const Q3DStudio::SFileModificationRecord &record(inFileModificationList[idx]);
+ if (record.m_FileInfo.IsFile()
+ && isInList(extensions, record.m_File.GetExtension())) {
+ Q3DStudio::CFilePath relativePath(
+ Q3DStudio::CFilePath::GetRelativePathFromBase(
+ g_StudioApp.GetCore()->GetDoc()->GetDocumentDirectory(),
+ record.m_File));
+
+ if (record.m_ModificationType == Q3DStudio::FileModificationType::Created)
+ UICDM::binary_sort_insert_unique(m_fileList, relativePath);
+ else if (record.m_ModificationType == Q3DStudio::FileModificationType::Destroyed)
+ UICDM::binary_sort_erase(m_fileList, relativePath);
+ }
+ if (record.m_FileInfo.IsFile()
+ && record.m_ModificationType == Q3DStudio::FileModificationType::Modified) {
+ if (record.m_File.toQString() == g_StudioApp.GetCore()->GetDoc()
+ ->GetDocumentUIAFile()) {
+ m_inspectorControlModel->refreshRenderables();
+ }
+ }
+ }
+ std::vector<Q3DStudio::CFilePath> materials;
+ filterMaterials(materials);
+ m_inspectorControlModel->setMaterials(materials);
+}
+
+InspectorControlView::~InspectorControlView()
+{
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+}
+
+QSize InspectorControlView::sizeHint() const
+{
+ return {120, 600};
+}
+
+void InspectorControlView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_inspectorView"_L1, this);
+ rootContext()->setContextProperty("_inspectorModel"_L1, m_inspectorControlModel);
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+ rootContext()->setContextProperty("_tabOrderHandler"_L1, tabOrderHandler());
+ qmlRegisterUncreatableType<UICDM::DataModelDataType>("Qt3DStudio", 1, 0, "DataModelDataType",
+ "DataModelDataType is an enum container");
+ qmlRegisterUncreatableType<UICDM::AdditionalMetaDataType>(
+ "Qt3DStudio", 1, 0, "AdditionalMetaDataType",
+ "AdditionalMetaDataType is an enum container");
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/InspectorControlView.qml"_L1));
+}
+
+QAbstractItemModel *InspectorControlView::inspectorControlModel() const
+{
+ return m_inspectorControlModel;
+}
+
+QString InspectorControlView::titleText() const
+{
+ if (m_inspectableBase) {
+ Q3DStudio::CString theName = m_inspectableBase->GetName();
+ if (theName == L"PathAnchorPoint")
+ return tr("Anchor Point");
+ else
+ return theName.toQString();
+ }
+ return tr("No Object Selected");
+}
+
+QColor InspectorControlView::titleColor(int instance, int handle) const
+{
+ if (instance != 0 && handle != 0) {
+ if (g_StudioApp.GetCore()->GetDoc()->GetDocumentReader().IsPropertyLinked(instance, handle))
+ return CStudioPreferences::masterColor();
+ else
+ return CStudioPreferences::textColor();
+ } else {
+ return CStudioPreferences::textColor();
+ }
+}
+
+QString InspectorControlView::titleIcon() const
+{
+ if (m_inspectableBase)
+ return CStudioObjectTypes::GetNormalIconName(m_inspectableBase->GetObjectType());
+ return {};
+}
+
+void InspectorControlView::OnSelectionSet(Q3DStudio::SSelectedValue inSelectable)
+{
+ updateInspectable(g_StudioApp.GetInspectableFromSelectable(inSelectable));
+}
+
+void InspectorControlView::updateInspectable(CInspectableBase *inInspectable)
+{
+ if (inInspectable != nullptr) {
+ if (inInspectable->IsValid() == false)
+ inInspectable = nullptr;
+ }
+ setInspectable(inInspectable);
+}
+
+void InspectorControlView::setInspectable(CInspectableBase *inInspectable)
+{
+ if (m_inspectableBase != inInspectable) {
+ m_inspectableBase = inInspectable;
+ m_inspectorControlModel->setInspectable(inInspectable);
+ Q_EMIT titleChanged();
+ auto sp = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystem()->GetSignalProvider();
+ m_PropertyChangeConnection = sp->ConnectInstancePropertyValue(
+ std::bind(&InspectorControlView::titleChanged, this));
+ }
+}
+
+void InspectorControlView::showContextMenu(int x, int y, int handle, int instance)
+{
+ m_instance = instance;
+ m_handle = handle;
+
+ QMenu theContextMenu;
+
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ bool isLinkedFlag = doc->GetDocumentReader().IsPropertyLinked(instance, handle);
+ bool canBeLinkedFlag = doc->GetDocumentReader().CanPropertyBeLinked(instance, handle);
+ if (isLinkedFlag) {
+ auto action = theContextMenu.addAction(QObject::tr("Unlink Property from Master Slide"));
+ action->setEnabled(canBeLinkedFlag);
+ connect(action, &QAction::triggered, this, &InspectorControlView::toggleMasterLink);
+ } else {
+ auto action = theContextMenu.addAction(QObject::tr("Link Property from Master Slide"));
+ action->setEnabled(canBeLinkedFlag);
+ connect(action, &QAction::triggered, this, &InspectorControlView::toggleMasterLink);
+ }
+ theContextMenu.exec(mapToGlobal({x, y}));
+
+ m_instance = 0;
+ m_handle = 0;
+}
+
+void InspectorControlView::toggleMasterLink()
+{
+ Q3DStudio::ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(),
+ L"Link Property", __FILE__, __LINE__);
+ bool wasLinked = editor->IsPropertyLinked(m_instance, m_handle);
+
+ if (wasLinked)
+ editor->UnlinkProperty(m_instance, m_handle);
+ else
+ editor->LinkProperty(m_instance, m_handle);
+}
+
+void InspectorControlView::setPropertyValueFromFilename(long instance, int handle,
+ const QString &name)
+{
+ if (m_inspectorControlModel) {
+ QString value;
+ if (name != tr("[None]"))
+ value = name;
+ m_inspectorControlModel->setPropertyValue(instance, handle, value);
+ }
+}
+
+QObject *InspectorControlView::showImageChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_imageChooserView) {
+ m_imageChooserView = new ImageChooserView(this);
+ connect(m_imageChooserView, &ImageChooserView::imageSelected, this,
+ [this] (int handle, int instance, const QString &imageName){
+ setPropertyValueFromFilename(instance, handle, imageName);
+ m_imageChooserView->hide();
+ });
+ }
+
+ m_imageChooserView->setHandle(handle);
+ m_imageChooserView->setInstance(instance);
+
+ showBrowser(m_imageChooserView, point);
+
+ return m_imageChooserView;
+}
+
+QObject *InspectorControlView::showFilesChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_fileChooserView) {
+ m_fileChooserView = new FileChooserView(this);
+ connect(m_fileChooserView, &FileChooserView::fileSelected, this,
+ [this] (int handle, int instance, const QString &fileName){
+ setPropertyValueFromFilename(instance, handle, fileName);
+ m_fileChooserView->hide();
+ });
+ }
+
+ m_fileChooserView->setHandle(handle);
+ m_fileChooserView->setInstance(instance);
+
+ showBrowser(m_fileChooserView, point);
+
+ return m_fileChooserView;
+}
+
+QObject *InspectorControlView::showMeshChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_meshChooserView) {
+ m_meshChooserView = new MeshChooserView(this);
+ connect(m_meshChooserView, &MeshChooserView::meshSelected, this,
+ [this] (int handle, int instance, const QString &name){
+ if (m_inspectorControlModel)
+ m_inspectorControlModel->setPropertyValue(instance, handle, name);
+ });
+ }
+
+ m_meshChooserView->setHandle(handle);
+ m_meshChooserView->setInstance(instance);
+
+ showBrowser(m_meshChooserView, point);
+
+ return m_meshChooserView;
+}
+
+QObject *InspectorControlView::showTextureChooser(int handle, int instance, const QPoint &point)
+{
+ if (!m_textureChooserView) {
+ m_textureChooserView = new TextureChooserView(this);
+ connect(m_textureChooserView, &TextureChooserView::textureSelected, this,
+ [this] (int handle, int instance, const QString &fileName){
+ setPropertyValueFromFilename(instance, handle, fileName);
+ m_textureChooserView->hide();
+ });
+ }
+
+ m_textureChooserView->setHandle(handle);
+ m_textureChooserView->setInstance(instance);
+
+ showBrowser(m_textureChooserView, point);
+
+ return m_textureChooserView;
+}
+
+QObject *InspectorControlView::showObjectReference(int handle, int instance, const QPoint &point)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ if (!m_objectReferenceModel) {
+ m_objectReferenceModel
+ = new ObjectListModel(g_StudioApp.GetCore(), doc->GetActiveRootInstance(), this);
+ }
+ if (!m_objectReferenceView)
+ m_objectReferenceView = new ObjectBrowserView(this);
+ m_objectReferenceView->setModel(m_objectReferenceModel);
+
+ disconnect(m_objectReferenceView);
+
+ showBrowser(m_objectReferenceView, point);
+
+ connect(m_objectReferenceView, &ObjectBrowserView::selectionChanged,
+ this, [this, doc, handle, instance] {
+ auto selectedItem = m_objectReferenceView->selectedHandle();
+ UICDM::SObjectRefType objRef = doc->GetDataModelObjectReferenceHelper()->GetAssetRefValue(
+ selectedItem, handle,
+ (CRelativePathTools::EPathType)(m_objectReferenceView->pathType()));
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, objRef);
+ });
+
+ return m_objectReferenceView;
+}
+
+void InspectorControlView::showBrowser(QQuickWidget *browser, const QPoint &point)
+{
+ QSize popupSize = CStudioPreferences::browserPopupSize();
+ browser->resize(popupSize);
+ browser->move(point - QPoint(popupSize.width(), popupSize.height()));
+
+ // Show asynchronously to avoid flashing blank window on first show
+ QTimer::singleShot(0, this, [browser] {
+ browser->show();
+ browser->activateWindow();
+ browser->setFocus();
+ });
+}
+
+void InspectorControlView::OnBeginDataModelNotifications()
+{
+
+}
+
+void InspectorControlView::OnEndDataModelNotifications()
+{
+ m_inspectorControlModel->refresh();
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance)
+{
+ m_inspectorControlModel->refresh();
+}
+
+void InspectorControlView::OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance, long inInstanceCount)
+{
+ m_inspectorControlModel->refresh();
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h
new file mode 100644
index 00000000..e5815e6c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INSPECTORCONTROLVIEW_H
+#define INSPECTORCONTROLVIEW_H
+
+#include <QtQuickWidgets/qquickwidget.h>
+#include <QtCore/qpointer.h>
+#include "DispatchListeners.h"
+#include "Dispatch.h"
+#include "UICFileTools.h"
+#include "TabOrderHandler.h"
+
+class InspectorControlModel;
+class QAbstractItemModel;
+class CInspectableBase;
+class ImageChooserView;
+class ImageChooserModel;
+class MeshChooserView;
+class ObjectBrowserView;
+class ObjectListModel;
+class FileChooserView;
+class TextureChooserView;
+
+class InspectorControlView : public QQuickWidget,
+ public CPresentationChangeListener,
+ public IDataModelListener,
+ public TabNavigable
+{
+ Q_OBJECT
+ Q_PROPERTY(QString titleText READ titleText NOTIFY titleChanged FINAL)
+ Q_PROPERTY(QString titleIcon READ titleIcon NOTIFY titleChanged FINAL)
+public:
+ explicit InspectorControlView(QWidget *parent = nullptr);
+ ~InspectorControlView();
+
+ void OnSelectionSet(Q3DStudio::SSelectedValue inValue);
+ QAbstractItemModel *inspectorControlModel() const;
+
+ QString titleText() const;
+ Q_INVOKABLE QColor titleColor(int instance = 0, int handle = 0) const;
+ QString titleIcon() const;
+
+ Q_INVOKABLE void showContextMenu(int x, int y, int handle, int instance);
+ Q_INVOKABLE QObject *showImageChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showFilesChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showMeshChooser(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showObjectReference(int handle, int instance, const QPoint &point);
+ Q_INVOKABLE QObject *showTextureChooser(int handle, int instance, const QPoint &point);
+
+ // IDataModelListener
+ void OnBeginDataModelNotifications() override;
+ void OnEndDataModelNotifications() override;
+ void OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance) override;
+ void OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance,
+ long inInstanceCount) override;
+
+
+Q_SIGNALS:
+ void titleChanged();
+ void controlsChanged();
+ void imageSelected(const QString &name);
+
+public Q_SLOTS:
+ void toggleMasterLink();
+
+protected:
+ QSize sizeHint() const override;
+
+private:
+ void setInspectable(CInspectableBase *inInspectable);
+ void updateInspectable(CInspectableBase *inInspectable);
+ void initialize();
+ void onFilesChanged(const Q3DStudio::TFileModificationList &inFileModificationList);
+ void OnNewPresentation() override;
+ void OnClosingPresentation() override;
+ void OnLoadedSubPresentation() override;
+ void filterMaterials(std::vector<Q3DStudio::CFilePath> &materials);
+ void setPropertyValueFromFilename(long instance, int handle, const QString &name);
+ void showBrowser(QQuickWidget *browser, const QPoint &point);
+
+ std::shared_ptr<UICDM::ISignalConnection> m_selectionChangedConnection;
+ std::shared_ptr<UICDM::ISignalConnection> m_DirectoryConnection;
+ std::shared_ptr<UICDM::ISignalConnection> m_PropertyChangeConnection;
+ QColor m_backgroundColor;
+ InspectorControlModel *m_inspectorControlModel = nullptr;
+ CInspectableBase *m_inspectableBase = nullptr;
+ QPointer<ImageChooserView> m_imageChooserView;
+ QPointer<MeshChooserView> m_meshChooserView;
+ QPointer<FileChooserView> m_fileChooserView;
+ QPointer<TextureChooserView> m_textureChooserView;
+ QPointer<ObjectBrowserView> m_objectReferenceView;
+ QPointer<ObjectListModel> m_objectReferenceModel;
+ std::vector<Q3DStudio::CFilePath> m_fileList;
+
+ int m_instance;
+ int m_handle;
+};
+
+#endif // INSPECTORCONTROLVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml
new file mode 100644
index 00000000..9e838d4c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml
@@ -0,0 +1,771 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.8
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.2
+
+import Qt3DStudio 1.0
+import "../controls"
+import "../Action"
+
+Rectangle {
+ id: root
+ color: _backgroundColor
+
+ Connections {
+ target: _inspectorModel
+ onModelAboutToBeReset: {
+ _tabOrderHandler.clear();
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.topMargin: 4
+ spacing: 8
+
+ RowLayout {
+ height: _controlBaseHeight + anchors.margins * 2
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.margins: 4
+
+ Image {
+ id: headerImage
+ source: _inspectorView.titleIcon !== "" ? _resDir + _inspectorView.titleIcon : ""
+ }
+
+ StyledLabel {
+ text: _inspectorView.titleText
+ color: _inspectorView.titleColor()
+ }
+ }
+
+ ListView {
+ id: groupElements
+
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ spacing: 4
+
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {
+ visible: size < 1.0
+ }
+
+ model: _inspectorModel
+ delegate: Rectangle {
+ id: delegateItem
+
+ property int indexOfThisDelegate: index
+
+ width: parent.width
+ height: items.height
+ color: "transparent";
+
+ readonly property var values: model.values
+
+ Column {
+ id: items
+
+ x: 10
+ width: parent.width - x
+ spacing: 4
+
+ StyledLabel {
+ text: model.title
+ }
+
+ Column {
+ spacing: 4
+
+ Repeater {
+ model: delegateItem.values
+
+ onItemAdded: {
+ if (index === 0)
+ _tabOrderHandler.clearGroup(indexOfThisDelegate);
+ if (item.loadedItem.tabItem1 !== undefined) {
+ _tabOrderHandler.addItem(indexOfThisDelegate,
+ item.loadedItem.tabItem1)
+ if (item.loadedItem.tabItem2 !== undefined) {
+ _tabOrderHandler.addItem(
+ indexOfThisDelegate,
+ item.loadedItem.tabItem2)
+ if (item.loadedItem.tabItem3 !== undefined) {
+ _tabOrderHandler.addItem(
+ indexOfThisDelegate,
+ item.loadedItem.tabItem3)
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ id: groupDelegateItem
+ spacing: 0
+
+ property alias loadedItem: loader.item
+
+ function showContextMenu(coords) {
+ _inspectorView.showContextMenu(
+ coords.x, coords.y,
+ model.modelData.handle,
+ model.modelData.instance);
+ // Refresh text; title color is wrong after this
+ propertyRow.color = _inspectorView.titleColor(
+ modelData.instance, modelData.handle);
+ }
+
+ Item {
+ Layout.alignment: Qt.AlignTop
+ width: animatedPropertyButton.sourceSize.width
+ height: _controlBaseHeight
+ visible: model.modelData.animatable
+ Image {
+ id: animatedPropertyButton
+
+ property bool animated: model.modelData.animated
+
+ anchors.fill: parent
+ fillMode: Image.Pad
+
+ source: {
+ _resDir + (animated
+ ? "Inspector-AnimateToggle-Active.png"
+ : "Inspector-AnimateToggle-Normal.png")
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton | Qt.LeftButton
+ onClicked: {
+ if (mouse.button === Qt.LeftButton) {
+ _inspectorModel.setPropertyAnimated(
+ model.modelData.instance,
+ model.modelData.handle,
+ !model.modelData.animated)
+ } else {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ groupDelegateItem.showContextMenu(coords);
+ }
+ }
+ }
+ }
+ }
+
+
+ Item {
+ // Spacer item
+ width: model.modelData.animatable
+ ? 4 : animatedPropertyButton.width + 4
+ height: loadedItem.height + 4 // Add little space between items
+ }
+
+ StyledLabel {
+ id: propertyRow
+
+ readonly property var modelData: model.modelData
+ text: model.modelData.title
+ color: _inspectorView.titleColor(modelData.instance,
+ modelData.handle)
+
+ Layout.alignment: Qt.AlignTop
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ hoverEnabled: true
+ onClicked: {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ groupDelegateItem.showContextMenu(coords);
+ }
+ }
+
+ StyledTooltip {
+ text: modelData.toolTip
+ visible: mouse.containsMouse
+ }
+ }
+
+ Loader {
+ id: loader
+ readonly property var modelData: propertyRow.modelData
+ sourceComponent: {
+ const dataType = modelData.dataType;
+ console.warn("KDAB_TODO: DEBUG for type", dataType,
+ "property", modelData.propertyType,
+ "text ", model.modelData.title);
+ switch (dataType) {
+ case DataModelDataType.Long:
+ if (modelData.propertyType ===
+ AdditionalMetaDataType.ShadowMapResolution) {
+ return shadowResolutionComponent;
+ }
+ if (modelData.propertyType === AdditionalMetaDataType.Range) {
+ return intSliderComponent;
+ }
+ console.warn("KDAB_TODO: implement handler for type \"Long\", property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Long4:
+ if (modelData.propertyType === AdditionalMetaDataType.Image) {
+ return imageChooser;
+ }
+ console.warn("KDAB_TODO: implement handler for type \"long4\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.ObjectRef:
+ if (modelData.propertyType === AdditionalMetaDataType.ObjectRef)
+ return objectReference;
+ console.warn("KDAB_TODO: implement handler for type: \"objectref\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.StringOrInt:
+ //TODO: Maybe do some further check if the right combo is used
+ if (modelData.propertyType === AdditionalMetaDataType.StringList)
+ return slideSelectionDropDown;
+ console.warn("KDAB_TODO: (String) implement handler for type \"stringOrInt\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.String:
+ if (modelData.propertyType === AdditionalMetaDataType.Import)
+ return fileChooser;
+ if (modelData.propertyType === AdditionalMetaDataType.StringList)
+ return comboDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Renderable)
+ return renderableDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Mesh)
+ return meshChooser;
+ if (modelData.propertyType === AdditionalMetaDataType.MultiLine)
+ return multiLine;
+ if (modelData.propertyType === AdditionalMetaDataType.Font)
+ return comboDropDown;
+ if (modelData.propertyType === AdditionalMetaDataType.Texture)
+ return textureChooser;
+ console.warn("KDAB_TODO: (String) implement handler for type \"string\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Bool:
+ return checkBox;
+ case DataModelDataType.Float:
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return valueComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.Range)
+ return sliderComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.FontSize)
+ return fontSizeComponent;
+ console.warn("KDAB_TODO: implement handler for type\"float\" property:",
+ modelData.propertyType);
+ return null;
+ case DataModelDataType.Float3:
+ if (modelData.propertyType === AdditionalMetaDataType.Color)
+ return colorBox;
+ if (modelData.propertyType === AdditionalMetaDataType.Rotation)
+ return xyzPropertyComponent;
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return xyzPropertyComponent;
+ console.warn("KDAB_TODO: implement handler for type:\"float3\" property:",
+ modelData.propertyType, "text ",
+ model.modelData.title);
+ return null;
+ case DataModelDataType.StringRef:
+ if (modelData.propertyType === AdditionalMetaDataType.None)
+ return materialDropDown;
+ console.warn("KDAB_TODO: implement handler for type:\"StringRef\" text ",
+ model.modelData.title);
+ return null;
+ default:
+ console.warn("KDAB_TODO: implement handler for type",
+ dataType, "property",
+ modelData.propertyType, "text ",
+ model.modelData.title);
+ }
+ return null;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Component {
+ id: multiLine
+
+ ScrollView {
+ id: scrollView
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant value: parent.modelData.value
+ property Item tabItem1: textArea
+
+ width: _valueWidth
+ height: _controlBaseHeight * 3
+ clip: true
+
+ ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
+ ScrollBar.vertical.policy: ScrollBar.AsNeeded
+
+ TextArea {
+ id: textArea
+
+ text: scrollView.value
+ horizontalAlignment: TextInput.AlignLeft
+ verticalAlignment: TextInput.AlignTop
+ font.pixelSize: _fontSize
+ color: _textColor
+ wrapMode: TextEdit.WordWrap
+ selectionColor: _selectionColor
+ selectedTextColor: _textColor
+
+ topPadding: 6
+ bottomPadding: 6
+ rightPadding: 6
+
+ background: Rectangle {
+ color: textArea.enabled ? _studioColor2 : "transparent"
+ border.width: textArea.activeFocus ? 1 : 0
+ border.color: textArea.activeFocus ? _selectionColor : _disabledColor
+ }
+
+ MouseArea {
+ id: mouseAreaX
+ anchors.fill: parent
+ property int clickedPos
+ preventStealing: true
+
+ onPressed: {
+ textArea.forceActiveFocus()
+ clickedPos = textArea.positionAt(mouse.x, mouse.y)
+ textArea.cursorPosition = clickedPos
+ }
+ onDoubleClicked: textArea.selectAll()
+ onPositionChanged: {
+ textArea.cursorPosition = textArea.positionAt(mouse.x, mouse.y)
+ textArea.select(clickedPos, textArea.cursorPosition)
+ }
+ }
+
+ onTextChanged: {
+ _inspectorModel.setPropertyValue(scrollView.instance, scrollView.handle,
+ text, false)
+ }
+
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(scrollView.instance, scrollView.handle,
+ text, true)
+ }
+ }
+ }
+ }
+
+ Component {
+ id: meshChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _inspectorView.showMeshChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: imageChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _inspectorView.showImageChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: fileChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value === "" ? qsTr("[None]") : parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _inspectorView.showFilesChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: textureChooser
+ HandlerFilesChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _inspectorView.showTextureChooser(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: xyzPropertyComponent
+
+ RowLayout {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ property alias tabItem1: xyzHandler.tabItem1
+ property alias tabItem2: xyzHandler.tabItem2
+ property alias tabItem3: xyzHandler.tabItem3
+ spacing: 0
+ Item {
+ width: _valueWidth - xyzHandler.width
+ }
+ HandlerPropertyBaseXYZ {
+ id: xyzHandler
+ valueX: Number(values[0]).toFixed(3)
+ valueY: Number(values[1]).toFixed(3)
+ valueZ: Number(values[2]).toFixed(3)
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle,
+ Qt.vector3d(valueX, valueY, valueZ))
+ }
+ }
+ }
+ }
+
+ Component {
+ id: valueComponent
+
+ RowLayout {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property real value: Number(parent.modelData.value).toFixed(3)
+ property Item tabItem1: floatField
+ spacing: 0
+ Item {
+ width: _valueWidth - floatField.width
+ }
+ FloatTextField {
+ id: floatField
+ text: parent.value
+ implicitHeight: _controlBaseHeight
+ implicitWidth: _valueWidth / 2
+
+ onWheelEventFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle, Number(text));
+ }
+
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(parent.instance, parent.handle, Number(text));
+ }
+ }
+ }
+ }
+
+ Component {
+ id: sliderComponent
+
+ HandlerPropertyBaseSlider {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ property real oldValue: 0.0
+
+ value: parent.modelData.value
+ sliderMin: values[0]
+ sliderMax: values[1]
+
+ onEditingStarted: {
+ oldValue = value
+ }
+ onEditingFinished: {
+ // make sure the undo step is created, therefore resetting it to the old value
+ var val = value
+ _inspectorModel.setPropertyValue(instance, handle, oldValue, false)
+ _inspectorModel.setPropertyValue(instance, handle, val, true)
+ }
+ onSliderMoved: {
+ _inspectorModel.setPropertyValue(instance, handle, value, false)
+ }
+ }
+ }
+
+ Component {
+ id: comboDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setPropertyValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: slideSelectionDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ var newIndex = find(value)
+ if (newIndex === -1)
+ newIndex = find(value + "|separator")
+ currentIndex = newIndex
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex).replace("|separator", "")
+ if (value !== newValue && currentIndex !== -1) {
+ _inspectorModel.setSlideSelection(instance, handle,
+ currentIndex, values)
+ }
+ }
+ onValueChanged: {
+ var newIndex = find(value)
+ if (newIndex === -1)
+ newIndex = find(value + "|separator")
+ currentIndex = newIndex
+ }
+ }
+ }
+
+ Component {
+ id: materialDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setMaterialTypeValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: renderableDropDown
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var values: parent.modelData.values
+ property var value: parent.modelData.value
+ model: values
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+
+ onCurrentIndexChanged: {
+ var newValue = textAt(currentIndex)
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setRenderableValue(instance, handle, newValue)
+ }
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: checkBox
+
+ Image {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property bool checked: parent.modelData.value
+
+ source: (_resDir + (checked ? "checkbox-checked.png" : "checkbox-unchecked.png"))
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: _inspectorModel.setPropertyValue(instance, handle, !checked)
+ }
+ }
+ }
+
+ Component {
+ id: colorBox
+
+ HandlerGenericBaseColor {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+
+ color: parent.modelData.value
+ onColorSelected: _inspectorModel.setPropertyValue(instance, handle, selectedColor)
+ }
+ }
+
+ Component {
+ id: objectReference
+
+ HandlerGenericChooser {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ value: parent.modelData.value
+ onShowBrowser: {
+ activeBrowser = _inspectorView.showObjectReference(handle, instance,
+ mapToGlobal(width, 0))
+ }
+ }
+ }
+
+ Component {
+ id: intSliderComponent
+
+ HandlerPropertyBaseSlider {
+ intSlider: true;
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property variant values: parent.modelData.values
+ intValue: parent.modelData.value
+ sliderMin: values[0]
+ sliderMax: values[1]
+
+ onEditingFinished: {
+ _inspectorModel.setPropertyValue(instance, handle, intValue)
+ }
+ }
+ }
+
+ Component {
+ id: fontSizeComponent
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property real value: parent.modelData.value
+
+ model: ["8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72", "96", "120"]
+
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+
+ onCurrentIndexChanged: {
+ var newvalue = parseInt(textAt(currentIndex))
+ _inspectorModel.setPropertyValue(instance, handle, newvalue)
+ }
+
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+
+ Component {
+ id: shadowResolutionComponent
+
+ StyledComboBox {
+ property int instance: parent.modelData.instance
+ property int handle: parent.modelData.handle
+ property var value: parent.modelData.value
+ property int newValue
+
+ model: ["8", "9", "10", "11"]
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+
+ Component.onCompleted: {
+ currentIndex = find(value)
+ }
+
+ onCurrentIndexChanged: {
+ newValue = parseInt(textAt(currentIndex))
+ if (value !== newValue && currentIndex !== -1)
+ _inspectorModel.setPropertyValue(instance, handle, newValue)
+ }
+
+ onValueChanged: {
+ currentIndex = find(value)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.cpp
new file mode 100644
index 00000000..c3ea86cb
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorGroup.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CInspectorGroup::CInspectorGroup()
+{
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CInspectorGroup::~CInspectorGroup()
+{
+}
+
+//==============================================================================
+/**
+ * Set the name to show up in the title bar
+ */
+void CInspectorGroup::SetName(const QString &inName)
+{
+ m_name = inName;
+}
+
+QString CInspectorGroup::GetName() const
+{
+ return m_name;
+}
+
diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.h b/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.h
new file mode 100644
index 00000000..98ac824d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/InspectorGroup.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_INSPECTOR_GROUP_H
+#define INCLUDED_INSPECTOR_GROUP_H 1
+
+#pragma once
+
+#include <QString>
+
+//==============================================================================
+/**
+ * @class CInspectorGroup
+ * @brief This is the base class for inspector groups.
+ *
+ * Derive from this class in order to create a new group for the inspector
+ * palette.
+ */
+class CInspectorGroup
+{
+public:
+ enum EGroupCommand {
+ EGroupCommandNone = 0,
+ EGroupCommandToggle,
+ };
+
+public:
+ CInspectorGroup();
+ virtual ~CInspectorGroup();
+
+ void SetName(const QString &inName);
+ QString GetName() const;
+
+protected:
+ QString m_name;
+};
+
+#endif // INCLUDED_INSPECTOR_GROUP_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/MeshChooser.qml b/src/Authoring/Studio/Palettes/Inspector/MeshChooser.qml
new file mode 100644
index 00000000..229c4c8c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/MeshChooser.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+ ListView {
+ id: listView
+
+ anchors {
+ fill: parent
+ leftMargin: 8
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _meshChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: _meshChooserView.setSelectedMeshName(filePath)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.cpp b/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.cpp
new file mode 100644
index 00000000..e1b61bbf
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MeshChooserModel.h"
+#include "Core.h"
+#include "Doc.h"
+#include "IDocumentBufferCache.h"
+#include "StudioApp.h"
+
+MeshChooserModel::MeshChooserModel(QObject *parent)
+ : ChooserModelBase(parent)
+{
+}
+
+MeshChooserModel::~MeshChooserModel()
+{
+
+}
+
+bool MeshChooserModel::isVisible(const QString &path) const
+{
+ return getIconType(path) == OBJTYPE_MODEL;
+}
+
+const QVector<ChooserModelBase::FixedItem> MeshChooserModel::getFixedItems() const
+{
+ static QVector<FixedItem> items;
+
+ if (items.isEmpty()) {
+ auto primitiveNames = g_StudioApp.GetCore()->GetDoc()->GetBufferCache().GetPrimitiveNames();
+ for (auto item = primitiveNames; *item; ++item) {
+ auto itemName = *item;
+ if (itemName[0] == L'#')
+ items.append({ OBJTYPE_MODEL, QString::fromWCharArray(itemName + 1) });
+ }
+ }
+
+ return items;
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.h b/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.h
new file mode 100644
index 00000000..5aea88a8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/MeshChooserModel.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESHCHOOSERMODEL_H
+#define MESHCHOOSERMODEL_H
+
+#include "ChooserModelBase.h"
+
+class MeshChooserModel : public ChooserModelBase
+{
+ Q_OBJECT
+
+public:
+ explicit MeshChooserModel(QObject *parent = nullptr);
+ virtual ~MeshChooserModel();
+
+private:
+ bool isVisible(const QString &path) const override;
+ const QVector<FixedItem> getFixedItems() const override;
+};
+
+#endif // MESHCHOOSERMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.cpp b/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.cpp
new file mode 100644
index 00000000..86f7c75b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "MeshChooserView.h"
+#include "MeshChooserModel.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "StudioPreferences.h"
+#include "IDocumentEditor.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "IDocumentBufferCache.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+MeshChooserView::MeshChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new MeshChooserModel(this))
+{
+ setWindowTitle(tr("Meshes"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &MeshChooserView::initialize);
+}
+
+void MeshChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_resDir"_L1,
+ resourceImageUrl());
+ rootContext()->setContextProperty("_meshChooserView"_L1, this);
+ rootContext()->setContextProperty("_meshChooserModel"_L1, m_model);
+
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/MeshChooser.qml"_L1));
+}
+
+QSize MeshChooserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+void MeshChooserView::setSelectedMeshName(const QString &name)
+{
+ hide();
+ bool resourceName = false;
+ QString meshName = QLatin1Char('#') + name;
+ const wchar_t **files = g_StudioApp.GetCore()->GetDoc()->GetBufferCache().GetPrimitiveNames();
+ for (const wchar_t **item = files; item && *item; ++item) {
+ QString primitive = QString::fromWCharArray(*item);
+ if (primitive == meshName) {
+ resourceName = true;
+ break;
+ }
+ }
+ if (!resourceName)
+ meshName = name;
+
+ Q_EMIT meshSelected(m_handle, m_instance, meshName);
+}
+
+void MeshChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+void MeshChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+void MeshChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &MeshChooserView::close);
+}
+
+void MeshChooserView::showEvent(QShowEvent *event)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ UICDM::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ const QString meshValue = UICDM::get<QString>(value);
+ const Q3DStudio::CFilePath selectionItem = Q3DStudio::CFilePath::fromQString(meshValue);
+ const Q3DStudio::CFilePath selectionWithoutId = selectionItem.GetPathWithoutIdentifier();
+
+ QString currentFile;
+
+ if (selectionWithoutId.size()) {
+ currentFile = selectionWithoutId.toQString();
+ } else {
+ currentFile = selectionItem.GetIdentifier().toQString();
+ }
+
+ m_model->setCurrentFile(currentFile);
+
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.h b/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.h
new file mode 100644
index 00000000..b47770d9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/MeshChooserView.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MESHCHOOSERVIEW_H
+#define MESHCHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+namespace Q3DStudio {
+class CFilePath;
+class CString;
+}
+
+class QAbstractItemModel;
+class MeshChooserModel;
+class MeshChooserView : public QQuickWidget
+{
+ Q_OBJECT
+public:
+ explicit MeshChooserView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+
+ Q_INVOKABLE void setSelectedMeshName(const QString &name);
+
+ void setHandle(int handle);
+ void setInstance(int instance);
+
+Q_SIGNALS:
+ void meshSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ MeshChooserModel *m_model = nullptr;
+};
+
+#endif // MESHCHOOSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectBrowser.qml b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowser.qml
new file mode 100644
index 00000000..d0ece17a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowser.qml
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import Qt3DStudio 1.0
+import "../controls"
+
+Rectangle {
+ id: root
+
+ property alias selectedText: selectionText.text
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+
+ ListView {
+ id: browserList
+
+ Layout.margins: 10
+ Layout.columnSpan: 2
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Layout.minimumHeight: 80
+ Layout.preferredHeight: count * 20
+ Layout.preferredWidth: root.width
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _objectBrowserView.model
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+
+ delegate: Item {
+ id: delegateItem
+
+ x: model.depth * 20
+ width: parent.width
+ height: model.parentExpanded ? typeIcon.height + 10 : 0
+
+ visible: height > 0
+
+ Behavior on height {
+ NumberAnimation {
+ duration: 100
+ easing.type: Easing.OutQuad
+ }
+ }
+
+ Row {
+ id: row
+
+ height: typeIcon.height
+ spacing: 5
+
+ Image {
+ source: {
+ if (!model.hasChildren)
+ return "";
+ model.expanded ? _resDir + "arrow_down.png"
+ : _resDir + "arrow.png";
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: model.expanded = !model.expanded
+ }
+ }
+
+ Rectangle {
+ height: typeIcon.height
+ width: typeIcon.width + name.width + 10
+
+ color: model.index === browserList.currentIndex ? _selectionColor
+ : "transparent"
+
+ Row {
+ spacing: 10
+ Image {
+ id: typeIcon
+
+ source: model.icon
+ }
+
+ StyledLabel {
+ id: name
+ anchors.verticalCenter: typeIcon.verticalCenter
+ color: model.textColor
+ text: model.name
+ }
+ }
+
+ MouseArea {
+ id: delegateArea
+
+ anchors.fill: parent
+ onClicked: browserList.currentIndex = model.index
+ onDoubleClicked: {
+ browserList.currentIndex = model.index;
+ _objectBrowserView.close();
+ }
+ }
+ }
+ }
+ }
+
+ onCurrentIndexChanged: _objectBrowserView.selection = currentIndex
+ }
+
+ StyledMenuSeparator {}
+
+ GridLayout {
+ columns: 2
+ Layout.margins: 10
+
+ StyledLabel {
+ text: qsTr("Type")
+ }
+
+ StyledComboBox {
+ id: pathCombo
+ model: [qsTr("Absolute Reference"), qsTr("Path Reference")]
+
+ onActivated: {
+ if (index === 0)
+ _objectBrowserView.pathType = ObjectBrowserView.Name;
+ else if (index === 1)
+ _objectBrowserView.pathType = ObjectBrowserView.Relative;
+ }
+ }
+
+ StyledLabel {
+ text: qsTr("Path")
+ }
+
+ StyledLabel {
+ id: selectionText
+ Layout.preferredWidth: _valueWidth
+ text: pathCombo.currentIndex === 0 ? _objectBrowserView.name(browserList.currentIndex)
+ : _objectBrowserView.path(browserList.currentIndex)
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp
new file mode 100644
index 00000000..86067722
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "ObjectBrowserView.h"
+
+#include "CColor.h"
+#include "Literals.h"
+#include "ObjectListModel.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ObjectBrowserView::ObjectBrowserView(QWidget *parent)
+ : QQuickWidget(parent)
+{
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ObjectBrowserView::initialize);
+}
+
+QAbstractItemModel *ObjectBrowserView::model() const
+{
+ return m_model;
+}
+
+void ObjectBrowserView::setModel(ObjectListModel *model)
+{
+ if (!m_model) {
+ m_model = new FlatObjectListModel(model, this);
+ }
+ m_model->setSourceModel(model);
+
+ Q_EMIT modelChanged();
+}
+
+
+QSize ObjectBrowserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+QString ObjectBrowserView::name(int index) const
+{
+ return m_model->index(index, 0).data(ObjectListModel::NameRole).toString();
+}
+
+QString ObjectBrowserView::path(int index) const
+{
+ return m_model->index(index, 0).data(ObjectListModel::PathReferenceRole).toString();
+}
+
+void ObjectBrowserView::setSelection(int index)
+{
+ if (m_selection != index) {
+ m_selection = index;
+ Q_EMIT selectionChanged();
+ }
+}
+
+void ObjectBrowserView::setPathType(ObjectBrowserView::PathType type)
+{
+ if (type != m_pathType) {
+ m_pathType = type;
+ Q_EMIT pathTypeChanged();
+ }
+}
+
+UICDM::CUICDMInstanceHandle ObjectBrowserView::selectedHandle() const
+{
+ auto handleId = m_model->index(m_selection, 0).data(ObjectListModel::HandleRole).toInt();
+ return UICDM::CUICDMInstanceHandle(handleId);
+}
+
+void ObjectBrowserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &ObjectBrowserView::close);
+}
+
+void ObjectBrowserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_objectBrowserView"_L1, this);
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+ qmlRegisterUncreatableType<ObjectBrowserView>("Qt3DStudio", 1, 0, "ObjectBrowserView"
+ , tr("Creation of ObjectBrowserView not allowed from QML"));
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/ObjectBrowser.qml"_L1));
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.h b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.h
new file mode 100644
index 00000000..8a4ed1eb
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef OBJECTBROWSERVIEW_H
+#define OBJECTBROWSERVIEW_H
+
+#include <QQuickWidget>
+
+#include "RelativePathTools.h"
+#include "UICDMHandles.h"
+
+#include <QColor>
+
+class ObjectListModel;
+class FlatObjectListModel;
+
+class QAbstractItemModel;
+
+class ObjectBrowserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged FINAL)
+ Q_PROPERTY(int selection READ selection WRITE setSelection NOTIFY selectionChanged FINAL)
+ Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
+
+public:
+ ObjectBrowserView(QWidget *parent = nullptr);
+
+
+ enum PathType {
+ Name = CRelativePathTools::EPATHTYPE_GUID,
+ Relative = CRelativePathTools::EPATHTYPE_RELATIVE,
+ };
+ Q_ENUM(PathType)
+
+ QAbstractItemModel *model() const;
+ void setModel(ObjectListModel *model);
+ QSize sizeHint() const override;
+
+ Q_INVOKABLE QString name(int index) const;
+ Q_INVOKABLE QString path(int index) const;
+
+ int selection() const { return m_selection; }
+ void setSelection(int index);
+
+ PathType pathType() const {return m_pathType;}
+ void setPathType(PathType type);
+
+ UICDM::CUICDMInstanceHandle selectedHandle() const;
+
+Q_SIGNALS:
+ void modelChanged();
+ void pathTypeChanged();
+ void selectionChanged();
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void initialize();
+
+ FlatObjectListModel *m_model = nullptr;
+ QHash<int, ObjectListModel *> m_subModels;
+ QColor m_baseColor = QColor::fromRgb(75, 75, 75);
+ QColor m_selectColor;
+ int m_selection = -1;
+ PathType m_pathType = Name;
+};
+
+#endif // OBJECTBROWSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp
new file mode 100644
index 00000000..ad1d752f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp
@@ -0,0 +1,336 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ObjectListModel.h"
+
+#include "Core.h"
+#include "Doc.h"
+#include "GraphUtils.h"
+#include "IObjectReferenceHelper.h"
+#include "StudioUtils.h"
+#include "SlideSystem.h"
+#include "StudioObjectTypes.h"
+#include "StudioPreferences.h"
+#include "UICDMStudioSystem.h"
+
+#include <QCoreApplication>
+#include <QColor>
+
+ObjectListModel::ObjectListModel(CCore *core,
+ const UICDM::CUICDMInstanceHandle &baseHandle, QObject *parent)
+ : QAbstractItemModel(parent)
+ , m_core(core)
+ , m_baseHandle(baseHandle)
+{
+ auto doc = m_core->GetDoc();
+ m_objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ m_slideHandle = m_objRefHelper->GetSlideList(m_baseHandle)[0];
+}
+
+QHash<int, QByteArray> ObjectListModel::roleNames() const
+{
+ auto names = QAbstractItemModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(HandleRole, "handle");
+ names.insert(IconRole, "icon");
+ names.insert(TextColorRole, "textColor");
+ names.insert(AbsolutePathRole, "absolutePath");
+ names.insert(PathReferenceRole, "referencePath");
+
+ return names;
+}
+
+int ObjectListModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return 1;
+
+ const auto handle = handleForIndex(parent);
+ const auto children = childrenList(m_slideHandle, handle);
+ return children.size();
+}
+
+int ObjectListModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ return 1;
+}
+
+QVariant ObjectListModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(), index.parent()))
+ return {};
+
+ switch (role) {
+ case NameRole: {
+ return nameForHandle(handleForIndex(index));
+ }
+ case PathReferenceRole: {
+ Q3DStudio::CString data(m_objRefHelper->GetObjectReferenceString(
+ m_baseHandle, CRelativePathTools::EPATHTYPE_RELATIVE, handleForIndex(index)));
+ return data.toQString();
+ }
+ case AbsolutePathRole: {
+ Q3DStudio::CString data(m_objRefHelper->GetObjectReferenceString(
+ m_baseHandle, CRelativePathTools::EPATHTYPE_GUID, handleForIndex(index)));
+ return data.toQString();
+ }
+ case HandleRole: {
+ return (int)handleForIndex(index);
+ }
+ case IconRole: {
+ auto info = m_objRefHelper->GetInfo(handleForIndex(index));
+ return resourceImageUrl() + CStudioObjectTypes::GetNormalIconName(info.m_Type);
+ }
+ case TextColorRole: {
+ auto info = m_objRefHelper->GetInfo(handleForIndex(index));
+ if (info.m_Master)
+ return QVariant::fromValue(CStudioPreferences::masterColor());
+ else
+ return QVariant::fromValue(CStudioPreferences::textColor());
+ }
+ default:
+ return {};
+ }
+
+ return {};
+}
+
+QModelIndex ObjectListModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return createIndex(row, column, (quintptr)(m_baseHandle));
+
+ const auto handle = handleForIndex(parent);
+ const auto children = childrenList(m_slideHandle, handle);
+ if (row >= children.size())
+ return {};
+
+ auto childHandle = children[row];
+ return createIndex(row, column, (quintptr)(childHandle));
+}
+
+QModelIndex ObjectListModel::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return {};
+
+ const auto handle = handleForIndex(index);
+ UICDM::CUICDMInstanceHandle parentHandle = m_core->GetDoc()->GetAssetGraph()->GetParent(handle);
+ if (!parentHandle.Valid())
+ return {};
+
+ int row = 0;
+ UICDM::CUICDMInstanceHandle grandParentHandle = m_core->GetDoc()->GetAssetGraph()
+ ->GetParent(handle);
+ const auto children = childrenList(m_slideHandle, grandParentHandle);
+ const auto it = std::find(children.begin(), children.end(), parentHandle);
+ if (it != children.end())
+ row = it - children.begin();
+
+ return createIndex(row, 0, (quintptr)(parentHandle));
+}
+
+UICDM::CUICDMInstanceHandle ObjectListModel::handleForIndex(const QModelIndex &index) const
+{
+ return static_cast<UICDM::CUICDMInstanceHandle>(index.internalId());
+}
+
+UICDM::TInstanceHandleList ObjectListModel::childrenList(const UICDM::CUICDMSlideHandle &slideHandle, const UICDM::CUICDMInstanceHandle &handle) const
+{
+ auto slideSystem = m_core->GetDoc()->GetStudioSystem()->GetSlideSystem();
+ auto currentMaster = slideSystem->GetMasterSlide(slideHandle);
+
+ UICDM::TInstanceHandleList children;
+ m_objRefHelper->GetChildInstanceList(handle, children, slideHandle, m_baseHandle);
+ children.erase(
+ std::remove_if(children.begin(), children.end(),
+ [&slideHandle, slideSystem, &currentMaster](const UICDM::CUICDMInstanceHandle &h) {
+ const auto childSlide = slideSystem->GetAssociatedSlide(h);
+ if (!childSlide.Valid())
+ return true;
+ const auto childMaster = slideSystem->GetMasterSlide(childSlide);
+ if (childMaster == currentMaster) {
+ return childSlide != childMaster && childSlide != slideHandle;
+ } else {
+ return childSlide != childMaster;
+ }
+ }), children.end());
+ return children;
+}
+
+QString ObjectListModel::nameForHandle(const UICDM::CUICDMInstanceHandle &handle) const
+{
+ const auto data = m_objRefHelper->GetInfo(handle);
+ return data.m_Name.toQString();
+}
+
+QModelIndex ObjectListModel::indexForHandle(const UICDM::CUICDMInstanceHandle &handle,
+ const QModelIndex &startIndex) const
+{
+ if (handle == m_baseHandle)
+ return index(0, 0, {});
+
+ for (int i = 0; i < rowCount(startIndex); i++) {
+ auto idx = index(i, 0, startIndex);
+ if (static_cast<UICDM::CUICDMInstanceHandle>(idx.internalId()) == handle)
+ return idx;
+ if (rowCount(idx) > 0)
+ return indexForHandle(handle, idx);
+ }
+ return {};
+}
+
+
+FlatObjectListModel::FlatObjectListModel(ObjectListModel *sourceModel, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_sourceModel(sourceModel)
+
+{
+ Q_ASSERT(sourceModel);
+ m_sourceInfo = collectSourceIndexes({}, 0);
+}
+
+QVector<FlatObjectListModel::SourceInfo> FlatObjectListModel::collectSourceIndexes(
+ const QModelIndex &sourceIndex, int depth) const
+{
+ QVector<SourceInfo> sourceInfo;
+
+ for (int i = 0; i < m_sourceModel->rowCount(sourceIndex); i++) {
+ auto idx = m_sourceModel->index(i, 0, sourceIndex);
+ SourceInfo info;
+ info.depth = depth;
+ info.index = idx;
+ sourceInfo.append(info);
+ if (m_sourceModel->rowCount(idx) > 0) {
+ sourceInfo += collectSourceIndexes(idx, depth + 1);
+ }
+ }
+
+ return sourceInfo;
+}
+
+QHash<int, QByteArray> FlatObjectListModel::roleNames() const
+{
+ auto roles = m_sourceModel->roleNames();
+ roles.insert(DepthRole, "depth");
+ roles.insert(ExpandedRole, "expanded");
+ roles.insert(ParentExpandedRole, "parentExpanded");
+ roles.insert(HasChildrenRole, "hasChildren");
+ return roles;
+}
+
+QModelIndex FlatObjectListModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ int row = proxyIndex.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+ return m_sourceInfo[row].index;
+}
+
+QVariant FlatObjectListModel::data(const QModelIndex &index, int role) const
+{
+ const auto row = index.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+
+ switch (role) {
+ case DepthRole: {
+ auto info = m_sourceInfo[row];
+ return info.depth;
+ }
+ case ExpandedRole: {
+ auto info = m_sourceInfo[row];
+ return info.expanded;
+ }
+ case ParentExpandedRole: {
+ auto info = m_sourceInfo[row];
+ if (info.depth == 0)
+ return true;
+
+ int depth = info.depth;
+ for (int i = row - 1; i >= 0; i--) {
+ const auto prevInfo = m_sourceInfo[i];
+ if (prevInfo.depth < depth) {
+ if (!prevInfo.expanded) {
+ return false;
+ } else {
+ depth = prevInfo.depth;
+ }
+ }
+ }
+ return true;
+ }
+ case HasChildrenRole: {
+ if (row == m_sourceInfo.count() - 1)
+ return false;
+ auto info = m_sourceInfo[row];
+ auto nextInfo = m_sourceInfo[row + 1];
+ return (nextInfo.depth > info.depth);
+ }
+ }
+
+ QModelIndex sourceIndex = mapToSource(index);
+ return m_sourceModel->data(sourceIndex, role);
+}
+
+bool FlatObjectListModel::setData(const QModelIndex &index, const QVariant &data, int role)
+{
+ const auto row = index.row();
+ if (row < 0 || row >= m_sourceInfo.count())
+ return {};
+
+ switch (role) {
+ case ExpandedRole: {
+ auto info = &m_sourceInfo[index.row()];
+ info->expanded = data.toBool();
+ Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {});
+ return true;
+ }
+ }
+
+ QModelIndex sourceIndex = mapToSource(index);
+ return m_sourceModel->setData(sourceIndex, data, role);
+}
+
+int FlatObjectListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_sourceInfo.count();
+}
+
+void FlatObjectListModel::setSourceModel(ObjectListModel *sourceModel)
+{
+ beginResetModel();
+ m_sourceModel = sourceModel;
+ m_sourceInfo = collectSourceIndexes({}, 0);
+ endResetModel();
+}
+
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h
new file mode 100644
index 00000000..fbff3537
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OBJECTLISTMODEL_H
+#define OBJECTLISTMODEL_H
+
+#include <QAbstractItemModel>
+#include <QAbstractListModel>
+
+#include "UICDMHandles.h"
+
+class IObjectReferenceHelper;
+class CCore;
+
+class ObjectListModel : public QAbstractItemModel
+{
+ Q_OBJECT
+public:
+ ObjectListModel(CCore *core, const UICDM::CUICDMInstanceHandle &baseHandle, QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ AbsolutePathRole = Qt::UserRole + 1,
+ PathReferenceRole,
+ IconRole,
+ TextColorRole,
+ HandleRole,
+ LastRole = HandleRole
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ UICDM::CUICDMInstanceHandle baseHandle() const {return m_baseHandle;}
+
+private:
+ UICDM::CUICDMInstanceHandle handleForIndex(const QModelIndex &index) const;
+
+ UICDM::TInstanceHandleList childrenList(const UICDM::CUICDMSlideHandle &slideHandle,
+ const UICDM::CUICDMInstanceHandle &handle) const;
+
+ QString nameForHandle(const UICDM::CUICDMInstanceHandle &handle) const;
+
+ QModelIndex indexForHandle(const UICDM::CUICDMInstanceHandle &handle, const QModelIndex &startIndex = {}) const;
+
+ CCore *m_core;
+ UICDM::CUICDMSlideHandle m_slideHandle;
+ UICDM::CUICDMInstanceHandle m_baseHandle;
+ IObjectReferenceHelper *m_objRefHelper;
+};
+
+class FlatObjectListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ FlatObjectListModel(ObjectListModel *sourceModel, QObject *parent = nullptr);
+
+ enum Roles {
+ DepthRole = ObjectListModel::LastRole + 1,
+ ExpandedRole,
+ ParentExpandedRole,
+ HasChildrenRole
+ };
+
+ QHash<int, QByteArray> roleNames() const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ void setSourceModel(ObjectListModel *sourceModel);
+ ObjectListModel *sourceModel() const {return m_sourceModel;}
+
+private:
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+
+ struct SourceInfo {
+ bool expanded = false;
+ int depth = 0;
+ QPersistentModelIndex index;
+ };
+
+ QVector<SourceInfo> collectSourceIndexes(const QModelIndex &sourceIndex, int depth) const;
+
+ QVector<SourceInfo> m_sourceInfo;
+ ObjectListModel *m_sourceModel;
+};
+
+
+#endif // OBJECTLISTMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectReference.qml b/src/Authoring/Studio/Palettes/Inspector/ObjectReference.qml
new file mode 100644
index 00000000..3e456067
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectReference.qml
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ ColumnLayout {
+ anchors.fill: parent
+ //KDAB_FIXME implement object chooser
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.cpp
new file mode 100644
index 00000000..40dc91e1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ObjectReferenceModel.h"
+
+ObjectReferenceModel::ObjectReferenceModel(QObject *parent)
+ : QAbstractListModel(parent)
+{
+ //KDAB_FIXME: Understand how it must be filled
+}
+
+ObjectReferenceModel::~ObjectReferenceModel()
+{
+
+}
+
+
+int ObjectReferenceModel::rowCount(const QModelIndex &parent) const
+{
+ //KDAB_FIXME
+ return 0;
+}
+
+QVariant ObjectReferenceModel::data(const QModelIndex &index, int role) const
+{
+ //KDAB_FIXME
+ return {};
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.h b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.h
new file mode 100644
index 00000000..29932442
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceModel.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OBJECTREFERENCEMODEL_H
+#define OBJECTREFERENCEMODEL_H
+
+#include <QAbstractListModel>
+
+class ObjectReferenceModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit ObjectReferenceModel(QObject *parent = nullptr);
+ ~ObjectReferenceModel();
+
+ int rowCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+};
+
+#endif // OBJECTREFERENCEMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.cpp
new file mode 100644
index 00000000..eab0b0aa
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ObjectReferenceView.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "StudioPreferences.h"
+#include "IDocumentEditor.h"
+#include "UICDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ObjectReferenceView::ObjectReferenceView(QWidget *parent) : QQuickWidget(parent)
+{
+ setWindowTitle(tr("Object Reference"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ObjectReferenceView::initialize);
+}
+
+void ObjectReferenceView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_resDir"_L1,
+ resourceImageUrl());
+ rootContext()->setContextProperty("_objectReferenceView"_L1, this);
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/ObjectReference.qml"_L1));
+}
+
+QSize ObjectReferenceView::sizeHint() const
+{
+ return {500, 500};
+}
+
+QAbstractItemModel *ObjectReferenceView::model() const
+{
+ return m_model;
+}
+
+void ObjectReferenceView::setModel(QAbstractItemModel *model)
+{
+ if (m_model != model) {
+ m_model = model;
+ Q_EMIT modelChanged();
+ }
+}
+
+void ObjectReferenceView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+void ObjectReferenceView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+void ObjectReferenceView::setSelectedReference(const QString &name)
+{
+ UICDM::SValue v = QVariant(name);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(m_instance, m_handle, v);
+}
+
+void ObjectReferenceView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ close();
+}
+
diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.h b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.h
new file mode 100644
index 00000000..5634fd97
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/ObjectReferenceView.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OBJECTREFERENCEVIEW_H
+#define OBJECTREFERENCEVIEW_H
+
+#include <QQuickWidget>
+class QAbstractItemModel;
+
+class ObjectReferenceView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged FINAL)
+public:
+ explicit ObjectReferenceView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+ QAbstractItemModel *model() const;
+ void setModel(QAbstractItemModel *model);
+
+ void setHandle(int handle);
+ void setInstance(int instance);
+
+ Q_INVOKABLE void setSelectedReference(const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+Q_SIGNALS:
+ void modelChanged();
+
+private:
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ QColor m_baseColor = QColor::fromRgb(75, 75, 75);
+ QAbstractItemModel *m_model = nullptr;
+};
+
+#endif // OBJECTREFERENCEVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.cpp b/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.cpp
new file mode 100644
index 00000000..415b1877
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.cpp
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "TabOrderHandler.h"
+
+TabOrderHandler::TabOrderHandler(QObject *parent)
+ : QObject(parent)
+{
+
+}
+
+TabOrderHandler::~TabOrderHandler()
+{
+
+}
+
+void TabOrderHandler::addItem(int group, QQuickItem *item)
+{
+ m_itemMap[group].append(item);
+}
+
+void TabOrderHandler::clear()
+{
+ m_itemMap.clear();
+}
+
+void TabOrderHandler::clearGroup(int group)
+{
+ m_itemMap[group].clear();
+}
+
+void TabOrderHandler::tabNavigate(bool tabForward)
+{
+ // Find the currently focused control
+ for (int i = 0; i < m_itemMap.size(); i++) {
+ const QList<QQuickItem *> items = m_itemMap[i];
+ for (int j = 0; j < items.size(); j++) {
+ if (items[j]->hasActiveFocus()) {
+ if (tabForward)
+ nextItem(i, j)->forceActiveFocus();
+ else
+ previousItem(i, j)->forceActiveFocus();
+ return;
+ }
+ }
+ }
+ // Activate the first item if could not find currently focused item
+ for (int i = 0; i < m_itemMap.size(); i++) {
+ if (m_itemMap[i].size() > 0)
+ m_itemMap[i][0]->forceActiveFocus();
+ }
+}
+
+QQuickItem *TabOrderHandler::nextItem(int group, int index)
+{
+ if (m_itemMap[group].size() > index + 1) {
+ // Try next item in group
+ index++;
+ } else {
+ // Get item in next available group
+ int nextGroup = group + 1;
+ while (nextGroup != group) {
+ if (m_itemMap.size() >= nextGroup)
+ nextGroup = 0;
+ if (m_itemMap[nextGroup].size() == 0)
+ nextGroup++;
+ else
+ group = nextGroup;
+ }
+ index = 0;
+ }
+ return m_itemMap[group][index];
+}
+
+QQuickItem *TabOrderHandler::previousItem(int group, int index)
+{
+ if (index - 1 >= 0) {
+ // Try previous item in group
+ index--;
+ } else {
+ // Get last item in previous available group
+ int nextGroup = group - 1;
+ while (nextGroup != group) {
+ if (nextGroup < 0)
+ nextGroup = m_itemMap.size() - 1;
+ if (m_itemMap[nextGroup].size() == 0)
+ nextGroup--;
+ else
+ group = nextGroup;
+ }
+ index = m_itemMap[group].size() - 1;
+ }
+ return m_itemMap[group][index];
+}
+
diff --git a/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.h b/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.h
new file mode 100644
index 00000000..10c1d400
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/TabOrderHandler.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef TABORDERHANDLER_H
+#define TABORDERHANDLER_H
+
+#include <QtCore/qobject.h>
+#include <QtQuick/qquickitem.h>
+
+class TabOrderHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit TabOrderHandler(QObject *parent = nullptr);
+ ~TabOrderHandler();
+
+ Q_INVOKABLE void addItem(int group, QQuickItem *item);
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void clearGroup(int group);
+
+ void tabNavigate(bool tabForward);
+
+private:
+ QQuickItem *nextItem(int group, int index);
+ QQuickItem *previousItem(int group, int index);
+
+ QHash<int, QList<QQuickItem *> > m_itemMap;
+};
+
+class TabNavigable {
+public:
+ TabNavigable() : m_tabOrderHandler(new TabOrderHandler) {}
+ virtual ~TabNavigable() { delete m_tabOrderHandler; }
+ TabOrderHandler *tabOrderHandler() const { return m_tabOrderHandler; }
+
+private:
+ TabOrderHandler *m_tabOrderHandler;
+};
+
+#endif // TABORDERHANDLER_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/TextureChooser.qml b/src/Authoring/Studio/Palettes/Inspector/TextureChooser.qml
new file mode 100644
index 00000000..4f90640e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/TextureChooser.qml
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+ border.color: _studioColor3
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ spacing: 10
+ ListView {
+ id: listView
+
+ anchors {
+ fill: parent
+ leftMargin: 8
+ }
+
+ boundsBehavior: Flickable.StopAtBounds
+ spacing: 4
+ clip: true
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _textureChooserModel
+
+ delegate: ChooserDelegate {
+ onClicked: {
+ _textureChooserView.textureSelected(
+ _textureChooserView.handle,
+ _textureChooserView.instance,
+ filePath);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.cpp b/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.cpp
new file mode 100644
index 00000000..78084b06
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "TextureChooserView.h"
+#include "ImageChooserModel.h"
+#include "StudioPreferences.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "IDocumentEditor.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMValue.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+TextureChooserView::TextureChooserView(QWidget *parent)
+ : QQuickWidget(parent)
+ , m_model(new ImageChooserModel(this))
+{
+ setWindowTitle(tr("Texture"));
+ setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &TextureChooserView::initialize);
+}
+
+void TextureChooserView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_textureChooserView"_L1, this);
+ rootContext()->setContextProperty("_textureChooserModel"_L1, m_model);
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Inspector/TextureChooser.qml"_L1));
+}
+
+QSize TextureChooserView::sizeHint() const
+{
+ return {500, 500};
+}
+
+void TextureChooserView::setHandle(int handle)
+{
+ m_handle = handle;
+}
+
+int TextureChooserView::handle() const
+{
+ return m_handle;
+}
+
+void TextureChooserView::setInstance(int instance)
+{
+ m_instance = instance;
+}
+
+int TextureChooserView::instance() const
+{
+ return m_instance;
+}
+
+void TextureChooserView::focusOutEvent(QFocusEvent *event)
+{
+ QQuickWidget::focusOutEvent(event);
+ QTimer::singleShot(0, this, &TextureChooserView::close);
+}
+
+void TextureChooserView::showEvent(QShowEvent *event)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+
+ UICDM::SValue value;
+ propertySystem->GetInstancePropertyValue(m_instance, m_handle, value);
+
+ m_model->setCurrentFile(UICDM::get<QString>(value));
+
+ QQuickWidget::showEvent(event);
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.h b/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.h
new file mode 100644
index 00000000..5bd51367
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/TextureChooserView.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEXTURECHOOSERVIEW_H
+#define TEXTURECHOOSERVIEW_H
+
+#include <QQuickWidget>
+
+class ImageChooserModel;
+
+class TextureChooserView : public QQuickWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int instance READ instance)
+ Q_PROPERTY(int handle READ handle)
+
+public:
+ explicit TextureChooserView(QWidget *parent = nullptr);
+
+ QSize sizeHint() const override;
+
+ void setHandle(int handle);
+ int handle() const;
+
+ void setInstance(int instance);
+ int instance() const;
+
+Q_SIGNALS:
+ void textureSelected(int handle, int instance, const QString &name);
+
+protected:
+ void focusOutEvent(QFocusEvent *event) override;
+
+private:
+ void showEvent(QShowEvent *event) override;
+ void initialize();
+ int m_handle = -1;
+ int m_instance = -1;
+ ImageChooserModel *m_model = nullptr;
+};
+
+#endif // TEXTURECHOOSERVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.cpp b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.cpp
new file mode 100644
index 00000000..1d009b4c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.cpp
@@ -0,0 +1,325 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMInspectable.h"
+#include "UICDMInspectorGroup.h"
+#include "UICDMInspectorRow.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMDataCore.h"
+#include "UICDMPropertyDefinition.h"
+#include "Core.h"
+#include "StudioFullSystem.h"
+#include "StudioCoreSystem.h"
+#include "UICDMMetaData.h"
+#include "UICDMSlides.h"
+#include "IDocumentReader.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+using namespace UICDM;
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CUICDMInspectable::CUICDMInspectable(CStudioApp &inApp, CCore *inCore,
+ UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inDualPersonalityInstance /*= 0*/)
+ : CInspectableBase(inCore)
+ , m_Instance(inInstance)
+ , m_DualPersonalityInstance((inDualPersonalityInstance != 0) ? inDualPersonalityInstance
+ : inInstance)
+ , m_App(inApp)
+{
+ QT3DS_ASSERT(inCore->GetDoc()->GetDocumentReader().IsInstance(m_Instance));
+}
+
+//==============================================================================
+/**
+ * Query the name of the inspectable item
+ */
+Q3DStudio::CString CUICDMInspectable::GetName()
+{
+ CClientDataModelBridge *theBridge =
+ m_Core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+
+ if (m_Instance == m_DualPersonalityInstance)
+ return theBridge->GetName(m_Instance);
+
+ Q3DStudio::CString theName = theBridge->GetName(m_Instance);
+ theName += " (";
+ theName += theBridge->GetName(m_DualPersonalityInstance);
+ theName += ")";
+
+ return theName;
+}
+
+//==============================================================================
+/**
+ * Query the number of groups to display
+ */
+long CUICDMInspectable::GetGroupCount()
+{
+ // If you have a dual personality inspectable then you may overwrite
+ QT3DS_ASSERT(
+ m_Instance == m_DualPersonalityInstance
+ || m_Core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge()->IsComponentInstance(
+ m_Instance));
+ IMetaData &theMetaData = *m_Core->GetDoc()->GetStudioSystem()->GetActionMetaData();
+ long count = (long)theMetaData.GetGroupCountForInstance(m_Instance);
+ return count;
+}
+
+//==============================================================================
+/**
+ * Return the property group for display
+ */
+CInspectorGroup *CUICDMInspectable::GetGroup(long inIndex)
+{
+ CUICDMInspectorGroup *theGroup =
+ new CUICDMInspectorGroup(m_App, GetGroupName(inIndex).toQString(), *this, inIndex);
+
+ TMetaDataPropertyHandleList theProperties = GetGroupProperties(inIndex);
+
+ size_t thePropertyCount = theProperties.size();
+ for (size_t thePropertyIndex = 0; thePropertyIndex < thePropertyCount; ++thePropertyIndex)
+ theGroup->CreateRow(m_Core->GetDoc(), theProperties[thePropertyIndex]);
+
+ return theGroup;
+}
+
+//==============================================================================
+/**
+ * Return the property handles for display, given the group index
+ */
+TMetaDataPropertyHandleList CUICDMInspectable::GetGroupProperties(long inIndex)
+{
+ TMetaDataPropertyHandleList retval;
+ IMetaData &theMetaData = *m_Core->GetDoc()->GetStudioSystem()->GetActionMetaData();
+ theMetaData.GetMetaDataProperties(GetGroupInstance(inIndex), retval);
+ UICDM::IPropertySystem &thePropertySystem(
+ *m_Core->GetDoc()->GetStudioSystem()->GetPropertySystem());
+ // get name of the current group fofr filtering
+ Option<UICDM::TCharStr> theGroupFilterName =
+ theMetaData.GetGroupFilterNameForInstance(GetGroupInstance(inIndex), inIndex);
+ long theGroupCount = GetGroupCount();
+
+ // end is explicitly required
+ for (size_t idx = 0; idx < retval.size(); ++idx) {
+ if (theMetaData.GetMetaDataPropertyInfo(retval[idx])->m_IsHidden) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ } else if (theGroupCount > 1 && theGroupFilterName.hasValue()
+ && theMetaData.GetMetaDataPropertyInfo(retval[idx])->m_GroupName
+ != theGroupFilterName) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ } else {
+ qt3ds::foundation::NVConstDataRef<SPropertyFilterInfo> theFilters(
+ theMetaData.GetMetaDataPropertyFilters(retval[idx]));
+ if (theFilters.size()) {
+ Option<bool> keepProperty;
+ // The tests are done in an ambiguous way. Really, show if equal should take
+ // multiple conditions
+ // as should hide if equal. They do not, so we need to rigorously define exactly
+ // how those two interact.
+ for (QT3DSU32 propIdx = 0, propEnd = theFilters.size(); propIdx < propEnd; ++propIdx) {
+ const SPropertyFilterInfo &theFilter(theFilters[propIdx]);
+
+ QT3DS_ASSERT(theFilter.m_FilterType == PropertyFilterTypes::ShowIfEqual
+ || theFilter.m_FilterType == PropertyFilterTypes::HideIfEqual);
+
+ SValue theValue;
+ thePropertySystem.GetInstancePropertyValue(
+ GetGroupInstance(inIndex), theFilter.m_FilterProperty, theValue);
+ bool resultIfTrue =
+ theFilter.m_FilterType == PropertyFilterTypes::ShowIfEqual ? true : false;
+ if (Equals(theValue.toOldSkool(), theFilter.m_Value.toOldSkool())) {
+ keepProperty = resultIfTrue;
+ break;
+ } else {
+ keepProperty = !resultIfTrue;
+ }
+ }
+ if (keepProperty.hasValue() && *keepProperty == false) {
+ retval.erase(retval.begin() + idx);
+ --idx;
+ }
+ }
+ }
+ }
+ return retval;
+}
+
+//==============================================================================
+/**
+ * Return the Resource String ID for the Group Name, given the group index
+ */
+Q3DStudio::CString CUICDMInspectable::GetGroupName(long inGroupIndex)
+{
+ std::vector<TCharStr> theGroupNames;
+ IMetaData &theMetaData = *m_Core->GetDoc()->GetStudioSystem()->GetActionMetaData();
+ theMetaData.GetGroupNamesForInstance(GetGroupInstance(inGroupIndex), theGroupNames);
+
+ size_t theIndex = inGroupIndex;
+
+ if (theGroupNames.size() > theIndex) {
+ Q3DStudio::CString theName = theGroupNames[inGroupIndex].wide_str();
+ return theName;
+ } else
+ return ::LoadResourceString(IDS_PROPERTIES_BASIC);
+}
+
+//==============================================================================
+/**
+ * Return the Inspectable Instance Handle for the Group, given the group index
+ */
+CUICDMInstanceHandle CUICDMInspectable::GetGroupInstance(long inGroupIndex)
+{
+ Q_UNUSED(inGroupIndex);
+ return m_DualPersonalityInstance;
+}
+
+EStudioObjectType CUICDMInspectable::GetObjectType()
+{
+ IMetaData &theMetaData = *m_Core->GetDoc()->GetStudioSystem()->GetActionMetaData();
+ Option<UICDM::TCharStr> theObjTypeName = theMetaData.GetTypeForInstance(m_Instance);
+ if (theObjTypeName.hasValue()) {
+ ComposerObjectTypes::Enum theType =
+ ComposerObjectTypes::Convert(theObjTypeName->wide_str());
+ switch (theType) {
+ case ComposerObjectTypes::Slide: {
+ CDoc *theDoc = m_Core->GetDoc();
+ CClientDataModelBridge *theBridge =
+ theDoc->GetStudioSystem()->GetClientDataModelBridge();
+ UICDM::CUICDMInstanceHandle theInstance =
+ theBridge->GetOwningComponentInstance(theDoc->GetActiveSlide());
+ Option<TCharStr> theObjTypeName = theMetaData.GetTypeForInstance(theInstance);
+ if (theObjTypeName.hasValue()) {
+ ComposerObjectTypes::Enum theType =
+ ComposerObjectTypes::Convert(theObjTypeName->wide_str());
+ if (theType == ComposerObjectTypes::Scene)
+ return OBJTYPE_SCENE;
+ else
+ return OBJTYPE_COMPONENT;
+ }
+ return OBJTYPE_UNKNOWN;
+ }
+ case ComposerObjectTypes::Scene:
+ return OBJTYPE_SCENE;
+ case ComposerObjectTypes::Layer:
+ return OBJTYPE_LAYER;
+ case ComposerObjectTypes::Behavior:
+ return OBJTYPE_BEHAVIOR;
+ case ComposerObjectTypes::Material:
+ return OBJTYPE_MATERIAL;
+ case ComposerObjectTypes::Camera:
+ return OBJTYPE_CAMERA;
+ case ComposerObjectTypes::Light:
+ return OBJTYPE_LIGHT;
+ case ComposerObjectTypes::Model:
+ return OBJTYPE_MODEL;
+ case ComposerObjectTypes::Group:
+ return OBJTYPE_GROUP;
+ case ComposerObjectTypes::Image:
+ return OBJTYPE_IMAGE;
+ case ComposerObjectTypes::Text:
+ return OBJTYPE_TEXT;
+ case ComposerObjectTypes::Component:
+ return OBJTYPE_COMPONENT;
+ case ComposerObjectTypes::Effect:
+ return OBJTYPE_EFFECT;
+ case ComposerObjectTypes::CustomMaterial:
+ return OBJTYPE_CUSTOMMATERIAL;
+ case ComposerObjectTypes::ReferencedMaterial:
+ return OBJTYPE_REFERENCEDMATERIAL;
+ case ComposerObjectTypes::Path:
+ return OBJTYPE_PATH;
+ case ComposerObjectTypes::SubPath:
+ return OBJTYPE_SUBPATH;
+ case ComposerObjectTypes::PathAnchorPoint:
+ return OBJTYPE_PATHANCHORPOINT;
+ case ComposerObjectTypes::Lightmaps:
+ return OBJTYPE_LIGHTMAPS;
+ }
+ }
+ return OBJTYPE_UNKNOWN;
+}
+
+bool CUICDMInspectable::IsValid() const
+{
+ return m_Core->GetDoc()->GetStudioSystem()->IsInstance(m_Instance)
+ && m_Core->GetDoc()->GetStudioSystem()->IsInstance(m_DualPersonalityInstance);
+}
+
+bool CUICDMInspectable::IsMaster()
+{
+ ISlideSystem *theSlideSystem = m_Core->GetDoc()->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMSlideHandle theSlideHandle = theSlideSystem->GetAssociatedSlide(m_Instance);
+ if (theSlideHandle.Valid())
+ return theSlideSystem->IsMasterSlide(theSlideHandle);
+ // Slide handle may not be valid if we are selecting the Scene or if we are inside Component and
+ // we select the Component root.
+ return false;
+}
+
+// std::wstring CUICDMInspectable::GetTypeString( ) const
+//{
+// std::wstring theReturn(L"");
+// try
+// {
+// IPropertySystem* thePropertySystem = m_Core->GetDoc( )->GetStudioSystem(
+//)->GetPropertySystem( );
+// CUICDMPropertyHandle theProperty =
+//thePropertySystem->GetAggregateInstancePropertyByName( m_Instance, L"type" );
+// SValue theTypeValue;
+// if ( theProperty && thePropertySystem->GetInstancePropertyValue( m_Instance,
+//theProperty, theTypeValue ) )
+// {
+// theReturn.assign( UICDM::get<TDataStrPtr>( theTypeValue )->GetData() );
+// }
+// }
+// catch( ... )
+// {
+// theReturn.assign(L"");
+// }
+//
+// return theReturn;
+//}
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.h b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.h
new file mode 100644
index 00000000..f497d4f9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectable.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_UICDM_INSPECTABLE_H
+#define INCLUDED_UICDM_INSPECTABLE_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "InspectableBase.h"
+#include "UICDMHandles.h"
+#include "StudioApp.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+/**
+* For inspecting data model instances
+*/
+class CUICDMInspectable : public CInspectableBase
+{
+protected: // Fields
+ UICDM::CUICDMInstanceHandle m_Instance;
+ UICDM::CUICDMInstanceHandle m_DualPersonalityInstance;
+ CStudioApp &m_App;
+
+public: // Constructor
+ CUICDMInspectable(CStudioApp &inApp, CCore *inCore, UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inDualPersonalityInstance = 0);
+
+public: // CInspectableBase
+ Q3DStudio::CString GetName() override;
+ long GetGroupCount() override;
+ CInspectorGroup *GetGroup(long) override;
+ EStudioObjectType GetObjectType() override;
+ // virtual std::wstring GetTypeString( ) const;
+ bool IsValid() const override;
+ bool IsMaster() override;
+ virtual UICDM::TMetaDataPropertyHandleList GetGroupProperties(long inGroupIndex);
+ virtual UICDM::CUICDMInstanceHandle GetGroupInstance(long inGroupIndex);
+
+protected:
+ virtual Q3DStudio::CString GetGroupName(long inGroupIndex);
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.cpp b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.cpp
new file mode 100644
index 00000000..86f62847
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMInspectorGroup.h"
+#include "UICDMInspectorRow.h"
+#include "UICDMInspectable.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "UICDMMetaData.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CUICDMInspectorGroup::CUICDMInspectorGroup(CStudioApp &inApp, const QString &inName,
+ CUICDMInspectable &inInspectable, long inIndex)
+ : CEasyInspectorGroup(inName)
+ , m_App(inApp)
+ , m_Inspectable(inInspectable)
+ , m_Index(inIndex)
+{
+}
+
+//==============================================================================
+/**
+ * clean up
+ */
+CUICDMInspectorGroup::~CUICDMInspectorGroup()
+{
+ std::vector<Q3DStudio::CUICDMInspectorRow *>::iterator theIterator =
+ m_UICDMInspectorRows.begin();
+ for (; theIterator != m_UICDMInspectorRows.end(); ++theIterator)
+ delete (*theIterator);
+}
+
+//==============================================================================
+/**
+ * Method to create a new InspectorRowBase.
+ */
+void CUICDMInspectorGroup::CreateRow(CDoc *inDoc, UICDM::CUICDMMetaDataPropertyHandle inProperty)
+{
+ Q3DStudio::CUICDMInspectorRow *theUICDMRow =
+ new Q3DStudio::CUICDMInspectorRow(inDoc, inProperty);
+ m_UICDMInspectorRows.push_back(
+ theUICDMRow); // this CUICDMInspectorRow is now owned by this class
+
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.h b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.h
new file mode 100644
index 00000000..3d811364
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorGroup.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_UICDM_INSPECTORGROUP_H
+#define INCLUDED_UICDM_INSPECTORGROUP_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "EasyInspectorGroup.h"
+#include "UICDMHandles.h"
+#include "StudioApp.h"
+
+class CDoc;
+namespace Q3DStudio {
+class CUICDMInspectorRow;
+};
+
+class CUICDMInspectable;
+
+//==============================================================================
+/**
+ *
+ */
+class CUICDMInspectorGroup: public CEasyInspectorGroup
+{
+protected: // Members
+ CStudioApp &m_App;
+ std::vector<Q3DStudio::CUICDMInspectorRow *> m_UICDMInspectorRows;
+ CUICDMInspectable &m_Inspectable;
+ long m_Index;
+
+public: // Construction
+ CUICDMInspectorGroup(CStudioApp &inApp, const QString &inName,
+ CUICDMInspectable &inInspectable, long inIndex);
+ ~CUICDMInspectorGroup();
+
+ const std::vector<Q3DStudio::CUICDMInspectorRow *> &GetRows() const
+ {
+ return m_UICDMInspectorRows;
+ }
+
+public: // Use
+ void CreateRow(CDoc *inDoc, UICDM::CUICDMMetaDataPropertyHandle inProperty);
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.cpp b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.cpp
new file mode 100644
index 00000000..d92ef10a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMInspectorRow.h"
+#include "UICDMMetaData.h"
+#include "Doc.h"
+#include "StudioApp.h"
+#include "UICDMSlides.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMAnimation.h"
+#include "UICDMSignals.h"
+#include "CmdDataModelDeanimate.h"
+#include "UICDMDataCore.h"
+#include "Core.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentEditor.h"
+
+//==============================================================================
+// Namespace
+//==============================================================================
+using namespace UICDM;
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CUICDMInspectorRow::CUICDMInspectorRow(CDoc *inDoc,
+ CUICDMMetaDataPropertyHandle inProperty)
+ : m_MetaProperty(inProperty)
+{
+ IMetaData *theMetaData = inDoc->GetStudioSystem()->GetActionMetaData();
+ SMetaDataPropertyInfo theInfo(theMetaData->GetMetaDataPropertyInfo(inProperty));
+ m_MetaDataPropertyInfo = theInfo;
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CUICDMInspectorRow::~CUICDMInspectorRow()
+{
+}
+
+} // namespace Q3DStudio
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.h b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.h
new file mode 100644
index 00000000..ff2155bc
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMInspectorRow.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMHandles.h"
+#include "IEasyInspectorRowListener.h"
+#include "IDataDrivenChangeListener.h"
+#include "DispatchListeners.h"
+#include "UICDMMetaDataTypes.h"
+#include "CmdBatch.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CDoc;
+class CEasyInspectorRow;
+
+// UICDM
+namespace UICDM {
+class ISignalConnection;
+}
+
+class CGenericEdit;
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+/**
+ * This is a binding between a DataModelInspectable and an EasyInspectorRow
+ */
+class CUICDMInspectorRow
+{
+ //==============================================================================
+ // Members
+ //==============================================================================
+protected:
+ UICDM::CUICDMMetaDataPropertyHandle m_MetaProperty;
+ UICDM::SMetaDataPropertyInfo m_MetaDataPropertyInfo;
+
+ //==============================================================================
+ // Methods
+ //==============================================================================
+public: // Construction
+ CUICDMInspectorRow(CDoc *inDoc, UICDM::CUICDMMetaDataPropertyHandle inProperty);
+ virtual ~CUICDMInspectorRow();
+
+private: // Disabled parameterless construction
+ CUICDMInspectorRow();
+
+public: // Use
+ UICDM::CUICDMMetaDataPropertyHandle GetMetaDataProperty() const
+ {
+ return m_MetaProperty;
+ }
+ const UICDM::SMetaDataPropertyInfo &GetMetaDataPropertyInfo() const
+ {
+ return m_MetaDataPropertyInfo;
+ }
+};
+
+} // namespace Q3DStudio
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.cpp b/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.cpp
new file mode 100644
index 00000000..70b204e5
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "UICDMMaterialInspectable.h"
+#include "UICDMInspectorGroup.h"
+#include "UICDMInspectorRow.h"
+#include "Core.h"
+#include "IDocumentEditor.h"
+#include "GenericComboDropDown.h"
+#include "UICDMHandles.h"
+#include "Doc.h"
+#include "GenericFunctor.h"
+#include "StudioApp.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentReader.h"
+#include "Dispatch.h"
+#include "IDirectoryWatchingSystem.h"
+#include "UICDMSignals.h"
+#include "UICString.h"
+
+using namespace UICDM;
+
+struct SMaterialTypeDropDown : public CGenericComboDropDown
+{
+ struct SMaterialEntry
+ {
+ Q3DStudio::CString m_Name;
+ Q3DStudio::CString m_RelativePath;
+ bool operator<(const SMaterialEntry &inOther) const { return m_Name < inOther.m_Name; }
+ };
+ CUICDMInstanceHandle m_Instance;
+ Q3DStudio::CAutoMemPtr<CGenericEditCommitListener> m_CommitListener;
+ vector<SMaterialEntry> m_Materials;
+ CDoc &m_Doc;
+ TSignalConnectionPtr m_FileListPtr;
+
+ SMaterialTypeDropDown(CDoc &inDoc, CUICDMInstanceHandle inInstance)
+ : m_Instance(inInstance)
+ , m_Doc(inDoc)
+ {
+ using Q3DStudio::CString;
+ using Q3DStudio::CFilePath;
+ m_CommitListener =
+ CREATE_LISTENER(CGenericEditCommitListener, SMaterialTypeDropDown, OnDataCommit);
+ AddCommitListener(m_CommitListener);
+ m_FileListPtr = m_Doc.GetDirectoryWatchingSystem()->AddDirectory(
+ m_Doc.GetDocumentDirectory().toQString(),
+ std::bind(&SMaterialTypeDropDown::OnFilesChanged, this, std::placeholders::_1));
+ }
+
+ vector<SMaterialEntry>::iterator GetMaterialEntry(const Q3DStudio::CString &inRelativePath)
+ {
+ for (size_t idx = 0, end = m_Materials.size(); idx < end; ++idx)
+ if (m_Materials[idx].m_RelativePath == inRelativePath)
+ return m_Materials.begin() + idx;
+ return m_Materials.end();
+ }
+
+ vector<SMaterialEntry>::iterator
+ GetOrCreateMaterialEntry(const Q3DStudio::CString &inRelativePath)
+ {
+ vector<SMaterialEntry>::iterator retval = GetMaterialEntry(inRelativePath);
+ if (retval == m_Materials.end()) {
+ m_Materials.push_back(SMaterialEntry());
+ m_Materials.back().m_RelativePath = inRelativePath;
+ return m_Materials.begin() + m_Materials.size() - 1;
+ } else
+ return retval;
+ }
+
+ void OnFilesChanged(const Q3DStudio::TFileModificationList &inFileModificationList)
+ {
+ for (size_t idx = 0, end = inFileModificationList.size(); idx < end; ++idx) {
+ const Q3DStudio::SFileModificationRecord &theRecord = inFileModificationList[idx];
+ Q3DStudio::CFilePath relativePath(Q3DStudio::CFilePath::GetRelativePathFromBase(
+ m_Doc.GetDocumentDirectory(), theRecord.m_File));
+ Q3DStudio::CString extension = relativePath.GetExtension();
+ if (extension.CompareNoCase("material")) {
+ switch (theRecord.m_ModificationType) {
+ case Q3DStudio::FileModificationType::Created:
+ case Q3DStudio::FileModificationType::Modified: {
+ SMaterialEntry &theEntry =
+ *GetOrCreateMaterialEntry(Q3DStudio::CString(relativePath));
+ theEntry.m_Name =
+ m_Doc.GetDocumentReader().GetCustomMaterialName(theRecord.m_File);
+ } break;
+ case Q3DStudio::FileModificationType::Destroyed: {
+ vector<SMaterialEntry>::iterator theEntry = GetMaterialEntry(relativePath);
+ if (theEntry != m_Materials.end())
+ m_Materials.erase(theEntry);
+ } break;
+
+ default: // don't care.
+ break;
+ }
+ }
+ }
+
+ std::sort(m_Materials.begin(), m_Materials.end());
+
+ RefreshAllItems();
+ }
+
+ void RefreshAllItems()
+ {
+ RemoveAllItems();
+ AddItem("Standard Material");
+ AddItem("Referenced Material");
+ CClientDataModelBridge *theBridge = m_Doc.GetStudioSystem()->GetClientDataModelBridge();
+ long selectIdx = 0;
+ EStudioObjectType theType = theBridge->GetObjectType(m_Instance);
+
+ if (theType == OBJTYPE_REFERENCEDMATERIAL)
+ selectIdx = 1;
+
+ Q3DStudio::CString sourcePath = theBridge->GetSourcePath(m_Instance);
+
+ for (size_t matIdx = 0, end = m_Materials.size(); matIdx < end; ++matIdx) {
+ AddItem(m_Materials[matIdx].m_Name);
+ if (m_Materials[matIdx].m_RelativePath.Compare(sourcePath))
+ selectIdx = (long)matIdx + 2;
+ }
+
+ SelectItem(selectIdx, false);
+ }
+
+ // Note that the this object is probably deleted when this happens or will be during its
+ // execution.
+ static void DoChangeObjectType(CDoc *inDoc, const Q3DStudio::CString &inNewType,
+ CUICDMInstanceHandle instance)
+ {
+ using namespace Q3DStudio;
+ SCOPED_DOCUMENT_EDITOR(*inDoc, QObject::tr("Set Property"))->SetMaterialType(instance, inNewType);
+ }
+
+ void OnDataCommit()
+ {
+ using Q3DStudio::CString;
+ size_t item = this->GetSelectedItem();
+ if (item >= 0) {
+ CString selectedType = this->GetItemText(this->GetSelectedItem());
+ if (item > 1) {
+ size_t matIdx = item - 2;
+ if (matIdx < m_Materials.size())
+ selectedType = m_Materials[matIdx].m_RelativePath;
+ }
+ // Fire a command to do this later because we will get screwed if we don't as we will be
+ // deleted
+ // during this process.
+ g_StudioApp.GetCore()->GetDispatch()->FireOnAsynchronousCommand(
+ std::bind(&DoChangeObjectType, &m_Doc, selectedType, m_Instance));
+ }
+ }
+};
+
+UICDMMaterialInspectorGroup::UICDMMaterialInspectorGroup(
+ CStudioApp &inApp,
+ const Q3DStudio::CString &inName,
+ CUICDMInspectable &inInspectable,
+ long inIndex)
+ : CUICDMInspectorGroup(inApp, inName.toQString(), inInspectable, inIndex)
+{
+}
+
+struct SUICDMMaterialInspectorGroup : public UICDMMaterialInspectorGroup
+{
+ SUICDMMaterialInspectorGroup(CStudioApp &inApp, const Q3DStudio::CString &inName,
+ CUICDMInspectable &inInspectable, long inIndex)
+ : UICDMMaterialInspectorGroup(inApp, inName, inInspectable, inIndex)
+ {
+ Q3DStudio::CString theMaterialGroupName = L"Material";
+ m_isMaterialGroup = (inName == theMaterialGroupName);
+ }
+
+ bool isMaterialGroup() const override
+ {
+ return m_isMaterialGroup;
+ }
+
+private:
+ bool m_isMaterialGroup;
+};
+
+CInspectorGroup *CUICDMMaterialInspectable::GetGroup(long inIndex)
+{
+ Q3DStudio::CString theGroupName = GetGroupName(inIndex);
+ Q3DStudio::CString theMaterialGroupName = L"Material";
+
+ CUICDMInspectorGroup *theGroup =
+ new SUICDMMaterialInspectorGroup(m_App, theGroupName, *this, inIndex);
+
+ TMetaDataPropertyHandleList theProperties = GetGroupProperties(inIndex);
+ size_t thePropertyCount = theProperties.size();
+
+ for (size_t thePropertyIndex = 0; thePropertyIndex < thePropertyCount; ++thePropertyIndex)
+ theGroup->CreateRow(m_Core->GetDoc(), theProperties[thePropertyIndex]);
+
+ return theGroup;
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.h b/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.h
new file mode 100644
index 00000000..20bb4365
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMMaterialInspectable.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_UICDM_MATERIAL_INSPECTABLE_H
+#define INCLUDED_UICDM_MATERIAL_INSPECTABLE_H 1
+
+#include "UICDMInspectable.h"
+#include "UICDMInspectorGroup.h"
+
+class UICDMMaterialInspectorGroup : public CUICDMInspectorGroup
+{
+public:
+ UICDMMaterialInspectorGroup(CStudioApp &inApp, const Q3DStudio::CString &inName,
+ CUICDMInspectable &inInspectable, long inIndex);
+
+ virtual bool isMaterialGroup() const = 0;
+};
+
+class CUICDMMaterialInspectable : public CUICDMInspectable
+{
+public:
+ CUICDMMaterialInspectable(CStudioApp &inApp, CCore *inCore,
+ UICDM::CUICDMInstanceHandle inInstance)
+ : CUICDMInspectable(inApp, inCore, inInstance)
+ {
+ }
+
+ CInspectorGroup *GetGroup(long) override;
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.cpp b/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.cpp
new file mode 100644
index 00000000..85f9b60c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMSceneInspectable.h"
+#include "Core.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+
+CUICDMSceneInspectable::CUICDMSceneInspectable(
+ CStudioApp &inApp, CCore *inCore, UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inCurrentActiveSlideInstance)
+ : CUICDMInspectable(inApp, inCore, inInstance)
+ , m_CurrentActiveSlideInstance(inCurrentActiveSlideInstance)
+{
+}
+
+bool CUICDMSceneInspectable::IsValid() const
+{
+ return CUICDMInspectable::IsValid()
+ && m_Core->GetDoc()->GetStudioSystem()->IsInstance(m_CurrentActiveSlideInstance);
+}
+
+long CUICDMSceneInspectable::GetGroupCount()
+{
+ return 2; // hard-coded to basic and shared
+}
+
+//==============================================================================
+/**
+ * Return the Resource String ID for the Group Name, given the group index
+ */
+Q3DStudio::CString CUICDMSceneInspectable::GetGroupName(long inGroupIndex)
+{
+ return (inGroupIndex == 0) ? ::LoadResourceString(IDS_PROPERTIES_BASIC)
+ : ::LoadResourceString(IDS_PROPERTIES_SHARED);
+}
+
+UICDM::CUICDMInstanceHandle CUICDMSceneInspectable::GetGroupInstance(long inGroupIndex)
+{
+ return (inGroupIndex == 0) ? m_CurrentActiveSlideInstance : m_Instance;
+}
diff --git a/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.h b/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.h
new file mode 100644
index 00000000..f30693b9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Inspector/UICDMSceneInspectable.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_UICDM_SCENE_INSPECTABLE_H
+#define INCLUDED_UICDM_SCENE_INSPECTABLE_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMInspectable.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+/**
+* For inspecting scene data model instances
+*/
+class CUICDMSceneInspectable : public CUICDMInspectable
+{
+public:
+ CUICDMSceneInspectable(CStudioApp &inApp, CCore *inCore, UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inCurrentActiveSlideInstance);
+
+ bool IsValid() const override;
+ // CUICDMInspectable
+ long GetGroupCount() override;
+
+protected:
+ inline Q3DStudio::CString GetGroupName(long inGroupIndex) override;
+ inline UICDM::CUICDMInstanceHandle GetGroupInstance(long inGroupIndex) override;
+
+ UICDM::CUICDMInstanceHandle m_CurrentActiveSlideInstance;
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Master/MasterControl.cpp b/src/Authoring/Studio/Palettes/Master/MasterControl.cpp
new file mode 100644
index 00000000..922b6d02
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Master/MasterControl.cpp
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MasterControl.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "InspectorControl.h"
+#include "ActionControl.h"
+#include "GenericTabControl.h"
+
+//===============================================================================
+/**
+ * Constructor
+ * @param inStudioApp
+ */
+CMasterControl::CMasterControl(IMasterControlProvider *inProvider)
+ : m_Provider(inProvider)
+ , m_TabMenu(nullptr)
+ , m_RenderDevice(nullptr)
+{
+ // Create the tab control
+ m_TabControl = new CGenericTabControl();
+ AddChild(m_TabControl);
+ m_TabControl->SigTabSelected.connect(std::bind(&CMasterControl::OnTabSelected, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ // Add the drop-down menu
+ m_MenuButton = new CProceduralButton<CButtonControl>();
+ m_MenuButton->SetUpImage("Storage-Dropdown-Normal.png");
+ m_MenuButton->SetDisabledImage("Tab-Menu-Disabled.png");
+ m_MenuButton->SetAbsoluteSize(m_MenuButton->GetImageSize());
+ m_MenuButton->SetFillColorUp(CStudioPreferences::GetDarkBaseColor());
+ m_MenuButton->SetFillColorDown(CStudioPreferences::GetDarkBaseColor());
+ m_MenuButton->SetFillColorOver(CStudioPreferences::GetDarkBaseColor());
+ m_MenuButton->SetFillColorDisabled(CStudioPreferences::GetDarkBaseColor());
+ m_MenuButton->SetFillStyleAll(CProceduralButton<CButtonControl>::EFILLSTYLE_FLOOD);
+ CProceduralButton<CButtonControl>::SBorderOptions theBorders(false, false, false, false);
+ m_MenuButton->SetBorderVisibilityAll(theBorders);
+ m_MenuButton->SigButtonDown.connect(std::bind(&CMasterControl::OnMenuButtonDown, this,
+ std::placeholders::_1));
+ m_TabControl->SetTabBarSibling(m_MenuButton);
+}
+
+//===============================================================================
+/**
+ * Destructor
+ */
+CMasterControl::~CMasterControl()
+{
+ RemoveChild(m_TabControl);
+}
+
+//===============================================================================
+/**
+ * Display the context menu for this control
+ */
+void CMasterControl::DisplayContextMenu(CPt inPosition)
+{
+ ASSERT(m_Provider != nullptr);
+ m_Provider->OnContextMenu(this, inPosition, m_TabMenu);
+}
+
+//=============================================================================
+/**
+ * Callback when the menu button is selected
+ */
+void CMasterControl::AddControl(const Q3DStudio::CString &inName, long inType, CControl *inControl)
+{
+ inControl->SetName(inName);
+ m_ControlList.insert(std::make_pair(inControl, inType));
+ m_TabControl->AddTab(inName, inControl);
+}
+
+//=============================================================================
+/**
+ * Callback when the menu button is selected
+ */
+void CMasterControl::RemoveControl(CControl *inControl)
+{
+ TControlMap::iterator theIterator = m_ControlList.begin();
+ TControlMap::iterator theEndIterator = m_ControlList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (inControl == theIterator->first) {
+ m_ControlList.erase(theIterator);
+ break;
+ }
+ }
+
+ if (m_ControlList.size())
+ m_TabControl->SelectTab((long)0);
+
+ m_TabControl->RemoveTab(inControl);
+}
+
+//=============================================================================
+/**
+ * Callback when the menu button is selected
+ */
+void CMasterControl::SelectControl(long inIndex)
+{
+ m_TabControl->SelectTab(inIndex);
+}
+
+//=============================================================================
+/**
+ * Callback when the menu button is selected
+ */
+void CMasterControl::SelectControl(CControl *inControl)
+{
+ m_TabControl->SelectTab(inControl);
+}
+
+//=============================================================================
+/**
+ * Return the number of controls
+ */
+long CMasterControl::GetControlCount()
+{
+ return m_TabControl->GetTabCount();
+}
+
+//=============================================================================
+/**
+ * Return the control for the specified type
+ */
+CControl *CMasterControl::FindControl(long inType)
+{
+ CControl *theControl = nullptr;
+ TControlMap::iterator theIterator = m_ControlList.begin();
+ TControlMap::iterator theEndIterator = m_ControlList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (inType == theIterator->second) {
+ theControl = theIterator->first;
+ break;
+ }
+ }
+ return theControl;
+}
+
+//=============================================================================
+/**
+ * Return the active control
+ */
+CControl *CMasterControl::GetActiveControl()
+{
+ return m_TabControl->GetSelectedTab();
+}
+
+//=============================================================================
+/**
+ * Return the index of the active tab
+ */
+long CMasterControl::GetActiveIndex()
+{
+ return m_TabControl->GetSelectedTabIndex();
+}
+
+//=============================================================================
+/**
+ * Return the active type
+ */
+long CMasterControl::GetActiveType()
+{
+ return m_ControlList[GetActiveControl()];
+}
+
+//=============================================================================
+/**
+ * Callback when the menu button is selected
+ */
+void CMasterControl::OnMenuButtonDown(CControl *)
+{
+ CPt thePosition(GetSize().x, GetPosition().y + m_MenuButton->GetSize().y);
+ DisplayContextMenu(thePosition);
+}
+
+//=============================================================================
+/**
+ * Callback when a tab changes
+ */
+void CMasterControl::OnTabSelected(CControl *, CControl *inNewControl)
+{
+ // Notify the provider that the selection changed
+ m_Provider->OnControlSelected(this, inNewControl, m_ControlList[inNewControl]);
+
+ // Set the enabled state of the menu item
+ m_MenuButton->SetEnabled((m_TabControl->GetTabCount() != 0));
+}
+
+//==============================================================================
+// CControl
+//==============================================================================
+
+Q3DStudio::CString CMasterControl::GetName()
+{
+ CControl *theActiveControl = GetActiveControl();
+ if (theActiveControl)
+ return theActiveControl->GetName();
+ return L"< Empty >";
+}
+
+//=============================================================================
+/**
+ * Fills the whole control with the base (gray) color, then other controls will
+ * draw on top of that.
+ * @param inRenderer renderer to draw to
+ */
+void CMasterControl::Draw(CRenderer *inRenderer)
+{
+ inRenderer->FillSolidRect(GetSize(), CStudioPreferences::GetDarkBaseColor());
+}
+
+//=============================================================================
+/**
+ * Set the size of the tree control
+ */
+void CMasterControl::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+ m_TabControl->SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ * Check to see if the right mouse down occurred on the tab control
+ */
+bool CMasterControl::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (m_TabControl->IsInTabBar(inPoint)) {
+ DisplayContextMenu(inPoint);
+ return true;
+ }
+
+ return CControl::OnMouseRDown(inPoint, inFlags); // not handled
+}
+
+//=============================================================================
+/**
+ * Return the window handle to the master view
+ */
+UICRenderDevice CMasterControl::GetPlatformDevice()
+{
+ return m_RenderDevice;
+}
+
+//=============================================================================
+/**
+ * Overriden from CControl. Since this control's parent is the CWnd, ensure
+ * that it gets the keyboard focus if this function is called.
+ * @see CControl::SetFocus
+ */
+void CMasterControl::GrabFocus(CControl *inControl)
+{
+ if (m_RenderDevice) {
+ ::SetFocus(m_RenderDevice);
+ }
+
+ CControl::GrabFocus(inControl);
+}
diff --git a/src/Authoring/Studio/Palettes/Master/MasterControl.h b/src/Authoring/Studio/Palettes/Master/MasterControl.h
new file mode 100644
index 00000000..40a347a4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Master/MasterControl.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_MASTER_CONTROL_H
+#define INCLUDED_MASTER_CONTROL_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "ProceduralButton.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+class CRenderer;
+class CAssetControl;
+class CGenericTabControl;
+class CMasterControl;
+
+//==============================================================================
+/**
+ * @class IMasterControlProvider
+ */
+class IMasterControlProvider
+{
+public:
+ virtual void OnControlRemoved(CMasterControl *inControl) = 0;
+ virtual void OnContextMenu(CMasterControl *inControl, const CPt &inPosition,
+ CContextMenu *inMyMenu) = 0;
+ virtual void OnControlSelected(CMasterControl *inMaster, CControl *inControl, long inType) = 0;
+};
+
+//==============================================================================
+/**
+ * @class CMasterControl
+ * @brief Class wrapping up all controls that appear in the Inspector Palette.
+ */
+class CMasterControl : public CControl
+{
+protected:
+ typedef std::map<CControl *, long> TControlMap;
+
+public:
+ CMasterControl(IMasterControlProvider *inProvider);
+ ~CMasterControl();
+
+ void SetRenderDevice(UICRenderDevice inDevice) { m_RenderDevice = inDevice; }
+
+ void AddControl(const Q3DStudio::CString &inName, long inType, CControl *inControl);
+ void RemoveControl(CControl *inControl);
+ void SelectControl(long inIndex);
+ void SelectControl(CControl *inControl);
+ long GetControlCount();
+ CControl *FindControl(long inType);
+
+ long GetActiveType();
+ long GetActiveIndex();
+ CControl *GetActiveControl();
+
+ // CControl
+ virtual Q3DStudio::CString GetName();
+ void Draw(CRenderer *inRenderer) override;
+ void SetSize(CPt inSize) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ UICRenderDevice GetPlatformDevice() override;
+ void GrabFocus(CControl *inControl) override;
+
+protected:
+ void DisplayContextMenu(CPt inPosition);
+ void OnTabSelected(CControl *inOldControl, CControl *inNewControl);
+ void OnMenuButtonDown(CControl *);
+
+protected:
+ IMasterControlProvider *m_Provider; ///<
+ CContextMenu *m_TabMenu; ///<
+ Q3DStudio::CAutoMemPtr<CGenericTabControl> m_TabControl; ///<
+ Q3DStudio::CAutoMemPtr<CProceduralButton<CButtonControl>>
+ m_MenuButton; ///< menu button for the storage palette
+ TControlMap m_ControlList; ///< the list of controls to display
+ UICRenderDevice m_RenderDevice; ///<
+};
+
+#endif //#ifndef INCLUDED_MASTER_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Master/MasterView.cpp b/src/Authoring/Studio/Palettes/Master/MasterView.cpp
new file mode 100644
index 00000000..8a4a9bd5
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Master/MasterView.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MasterView.h"
+#include "WndControl.h"
+#include "StudioApp.h"
+#include "MasterControl.h"
+#include "IDragable.h"
+
+//==============================================================================
+// Message Maps, etc.
+//==============================================================================
+IMPLEMENT_DYNCREATE(CMasterView, CView)
+
+BEGIN_MESSAGE_MAP(CMasterView, CView)
+//{{AFX_MSG_MAP(CMasterView)
+ON_WM_SIZE()
+ON_WM_ERASEBKGND()
+ON_WM_MOUSEACTIVATE()
+//}}AFX_MSG_MAP
+ON_MESSAGE(WM_STUDIO_INITIALIZE_PALETTES, OnInitializePalettes)
+END_MESSAGE_MAP()
+
+int CMasterView::OnMouseActivate(CWnd *, UINT, UINT)
+{
+ return MA_NOACTIVATE;
+}
+
+//=============================================================================
+/**
+ * Constructor: Protected because the view is always created dynamically.
+ * You must call Initialize() before trying to use this class.
+ */
+CMasterView::CMasterView()
+ : m_WndControl(nullptr)
+ , m_Provider(nullptr)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CMasterView::~CMasterView()
+{
+ m_WndControl->DestroyWindow();
+ m_WndControl = nullptr;
+
+ m_MasterControl = nullptr;
+}
+
+//==============================================================================
+/**
+ * Handles the WM_INITIALUPDATE message. Responsible for preparing the view
+ * before it is displayed for the first time.
+ */
+LRESULT CMasterView::OnInitializePalettes(WPARAM inwParam, LPARAM)
+{
+ if (!m_WndControl) {
+ CStudioApp *theStudioApp = reinterpret_cast<CStudioApp *>(inwParam);
+
+ m_MasterControl = new CMasterControl(m_Provider);
+ m_WndControl = new CWndControl(m_MasterControl);
+
+ m_WndControl->RegiserForDnd(this);
+
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_LISTBOX);
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_FILE);
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_ASSET_UICFILE);
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_ASSET_LIB);
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_ASSET_TL);
+ m_WndControl->AddMainFlavor(EUIC_FLAVOR_BASIC_OBJECTS);
+
+ CRect theClientRect;
+ GetClientRect(&theClientRect);
+
+ m_WndControl->CreateEx(
+ WS_EX_NOPARENTNOTIFY, AfxRegisterWndClass(CS_DBLCLKS, LoadCursor(nullptr, IDC_ARROW),
+ (HBRUSH)GetStockObject(BLACK_BRUSH)),
+ L"MasterViewWndCtrl", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ theClientRect, this, 100);
+ m_MasterControl->SetRenderDevice(m_WndControl->GetSafeHwnd());
+ }
+
+ return 0;
+}
+
+//=============================================================================
+/**
+ * Required by base class but does nothing since all drawing is handled by the
+ * child control.
+ */
+void CMasterView::OnDraw(CDC *inDC)
+{
+ Q_UNUSED(inDC);
+}
+
+//=============================================================================
+/**
+ * Resizes the wnd control to fill the whole view.
+ */
+void CMasterView::OnSize(UINT inType, int inX, int inY)
+{
+ CView::OnSize(inType, inX, inY);
+ if (::IsWindow(m_WndControl->GetSafeHwnd()))
+ m_WndControl->MoveWindow(0, 0, inX, inY);
+}
+
+//==============================================================================
+/**
+ * Tells the Inspector to erase before redrawing. Overridden because we erasing
+ * before each draw produces a flashing effect.
+ * @param inDC the DC to erase on.
+ * @return FALSE.
+ */
+BOOL CMasterView::OnEraseBkgnd(CDC *inDC)
+{
+ Q_UNUSED(inDC);
+ return FALSE;
+}
diff --git a/src/Authoring/Studio/Palettes/Master/MasterView.h b/src/Authoring/Studio/Palettes/Master/MasterView.h
new file mode 100644
index 00000000..008d748f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Master/MasterView.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_MASTER_VIEW_H
+#define INCLUDED_MASTER_VIEW_H 1
+
+#pragma once
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+class CWndControl;
+class CMasterControl;
+class IMasterControlProvider;
+
+//=============================================================================
+/**
+ * Windows view encapsulating the inspector palette.
+ */
+class CMasterView : public CView
+{
+protected:
+ CMasterView();
+ DECLARE_DYNCREATE(CMasterView)
+ virtual ~CMasterView();
+
+ afx_msg void OnSize(UINT inType, int inX, int inY);
+ afx_msg BOOL OnEraseBkgnd(CDC *inDC);
+ afx_msg int OnMouseActivate(CWnd *pDesktopWnd, UINT nHitTest, UINT message);
+ DECLARE_MESSAGE_MAP()
+
+public:
+ virtual void OnDraw(CDC *inDC);
+ virtual LRESULT OnInitializePalettes(WPARAM inwParam, LPARAM inlParam);
+
+ void SetProvider(IMasterControlProvider *inProvider) { m_Provider = inProvider; }
+ CMasterControl *GetMasterControl() const { return m_MasterControl; }
+
+protected:
+ IMasterControlProvider *m_Provider; ///<
+ CWndControl *m_WndControl; ///<
+ Q3DStudio::CAutoMemPtr<CMasterControl> m_MasterControl; ///<
+};
+
+#endif // INCLUDED_MASTER_VIEW_H
diff --git a/src/Authoring/Studio/Palettes/Progress/ProgressCallback.h b/src/Authoring/Studio/Palettes/Progress/ProgressCallback.h
new file mode 100644
index 00000000..eeff02b8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Progress/ProgressCallback.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PROGRESS_CALLBACK_H
+#define INCLUDED_PROGRESS_CALLBACK_H 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+/**
+ * Callback interface to progress update window
+ */
+class IProgressCallback
+{
+public:
+ virtual void SetProgress(long inPercent) = 0;
+
+ virtual void SetActionText(const Q3DStudio::CString &inText) = 0;
+};
+
+#endif // INCLUDED_PROGRESS_CALLBACK_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Progress/ProgressControl.cpp b/src/Authoring/Studio/Palettes/Progress/ProgressControl.cpp
new file mode 100644
index 00000000..2a69bf44
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Progress/ProgressControl.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ProgressControl.h"
+#include "Renderer.h"
+#include "ResourceCache.h"
+#include "ResImage.h"
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CProgressControl::CProgressControl()
+ : m_Percent(0)
+{
+ // Load the image
+ m_Image = CResourceCache::GetInstance()->GetBitmap("progress-screen.png");
+
+ // Load the default string for the window for now
+ m_ActionText = ::LoadResourceString(IDS_WAIT_LOADING);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CProgressControl::~CProgressControl()
+{
+}
+
+//==============================================================================
+/**
+ * The size of this control is equal to the size of the image that we display.
+ * @return the size (in pixels) of this control.
+ */
+CPt CProgressControl::GetSize() const
+{
+ return m_Image->GetSize();
+}
+
+//==============================================================================
+/**
+ * Draws this control.
+ * @param inRenderer renderer to draw to.
+ */
+void CProgressControl::Draw(CRenderer *inRenderer)
+{
+ // Draw the image over the whole window
+ inRenderer->DrawBitmap(CPt(0, 0), m_Image);
+
+ // Show "Loading..."
+ inRenderer->DrawText(105, 20, m_ActionText, GetSize(), CColor(255, 255, 255));
+
+ // Show the file name
+ if (!m_FileName.IsEmpty())
+ inRenderer->DrawText(105, 35, m_FileName, GetSize(), CColor(255, 255, 255));
+
+ // Show the percentage
+ inRenderer->DrawText(105, 50, m_PercentString, GetSize(), CColor(255, 255, 255));
+}
+
+//==============================================================================
+/**
+ * Sets the text displayed above the file name. For instance: "Loading..."
+ * @param inText text to be shown above the file name
+ */
+void CProgressControl::SetActionText(const Q3DStudio::CString &inText)
+{
+ m_ActionText = inText;
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Changes the percentage complete displayed by this control.
+ * @param inPercent new percentage complete.
+ */
+void CProgressControl::SetProgress(long inPercent)
+{
+ m_Percent = inPercent;
+ char theBuffer[256] = { 0 };
+ _ltoa(m_Percent, theBuffer, 10);
+ m_PercentString = theBuffer;
+ m_PercentString += " %";
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Sets the name of the file that is being opened. This is displayed on the
+ * control.
+ * @param inFileName File name to display in the middle of this control
+ */
+void CProgressControl::SetFileName(const Q3DStudio::CString &inFileName)
+{
+ m_FileName = inFileName;
+ Invalidate();
+}
diff --git a/src/Authoring/Studio/Palettes/Progress/ProgressControl.h b/src/Authoring/Studio/Palettes/Progress/ProgressControl.h
new file mode 100644
index 00000000..1d596c0c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Progress/ProgressControl.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PROGRESS_CONTROL_H
+#define INCLUDED_PROGRESS_CONTROL_H 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CResImage;
+
+//==============================================================================
+/**
+ * Top-level control of a loading progress window.
+ */
+class CProgressControl : public CControl
+{
+public:
+ CProgressControl();
+ virtual ~CProgressControl();
+
+ virtual CPt GetSize() const;
+ virtual void Draw(CRenderer *inRenderer);
+ virtual void SetActionText(const Q3DStudio::CString &inText);
+ virtual void SetProgress(long inPercent);
+ void SetFileName(const Q3DStudio::CString &inFileName);
+
+protected:
+ long m_Percent;
+ Q3DStudio::CString m_ActionText;
+ Q3DStudio::CString m_PercentString;
+ Q3DStudio::CString m_FileName;
+ CResImage *m_Image;
+};
+
+#endif // INCLUDED_PROGRESS_CONTROL_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp
new file mode 100644
index 00000000..edcafbf1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ProjectContextMenu.h"
+#include "ProjectView.h"
+
+ProjectContextMenu::ProjectContextMenu(ProjectView *parent, int index)
+ : QMenu(parent)
+ , m_view(parent)
+ , m_index(index)
+{
+ QAction *action = new QAction(tr("Show in Explorer"));
+ connect(action, &QAction::triggered, this, &ProjectContextMenu::handleShowInExplorer);
+ addAction(action);
+
+ addSeparator();
+
+ action = new QAction(tr("Copy Path"));
+ connect(action, &QAction::triggered, this, &ProjectContextMenu::handleCopyPath);
+ addAction(action);
+
+ action = new QAction(tr("Copy Full Path"));
+ connect(action, &QAction::triggered, this, &ProjectContextMenu::handleCopyFullPath);
+ addAction(action);
+
+ if (m_view->isGroup(m_index)) {
+ addSeparator();
+ action = new QAction(tr("Refresh Import..."));
+ connect(action, &QAction::triggered, this, &ProjectContextMenu::handleRefreshImport);
+ addAction(action);
+ }
+}
+
+ProjectContextMenu::~ProjectContextMenu()
+{
+}
+
+void ProjectContextMenu::handleShowInExplorer()
+{
+ m_view->showInExplorer(m_index);
+}
+
+void ProjectContextMenu::handleCopyPath()
+{
+ m_view->copyPath(m_index);
+}
+
+void ProjectContextMenu::handleCopyFullPath()
+{
+ m_view->copyFullPath(m_index);
+}
+
+void ProjectContextMenu::handleRefreshImport()
+{
+ m_view->refreshImport(m_index);
+}
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h
new file mode 100644
index 00000000..51dc8c5d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectContextMenu.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROJECT_CONTEXT_MENU_H
+#define PROJECT_CONTEXT_MENU_H
+
+#include <QtWidgets/qmenu.h>
+
+class ProjectView;
+
+class ProjectContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ explicit ProjectContextMenu(ProjectView *parent, int index);
+ virtual ~ProjectContextMenu();
+
+private Q_SLOTS:
+ void handleShowInExplorer();
+ void handleCopyPath();
+ void handleCopyFullPath();
+ void handleRefreshImport();
+
+private:
+ ProjectView *m_view;
+ int m_index;
+};
+#endif // PROJECT_CONTEXT_MENU_H
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
new file mode 100644
index 00000000..07ddd3d4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
@@ -0,0 +1,587 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qtAuthoring-config.h"
+#include <QtCore/qset.h>
+
+#include "stdafx.h"
+#include "ProjectFileSystemModel.h"
+#include "StudioUtils.h"
+#include "StudioApp.h"
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "Doc.h"
+#include "UICFileTools.h"
+#include "ImportUtils.h"
+#include "Dialogs.h"
+#include "UICDMStudioSystem.h"
+#include "UICImportTranslation.h"
+#include "IDocumentEditor.h"
+#include "PathImportTranslator.h"
+#include "IDragable.h"
+
+ProjectFileSystemModel::ProjectFileSystemModel(QObject *parent) : QAbstractListModel(parent)
+ , m_model(new QFileSystemModel(this))
+{
+ connect(m_model, &QAbstractItemModel::rowsInserted, this, &ProjectFileSystemModel::modelRowsInserted);
+ connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ProjectFileSystemModel::modelRowsRemoved);
+ connect(m_model, &QAbstractItemModel::layoutChanged, this, &ProjectFileSystemModel::modelLayoutChanged);
+}
+
+QHash<int, QByteArray> ProjectFileSystemModel::roleNames() const
+{
+ auto modelRoleNames = m_model->roleNames();
+ modelRoleNames.insert(IsExpandableRole, "_isExpandable");
+ modelRoleNames.insert(IsDraggableRole, "_isDraggable");
+ modelRoleNames.insert(IsReferencedRole, "_isReferenced");
+ modelRoleNames.insert(DepthRole, "_depth");
+ modelRoleNames.insert(ExpandedRole, "_expanded");
+ return modelRoleNames;
+}
+
+int ProjectFileSystemModel::rowCount(const QModelIndex &) const
+{
+ return m_items.count();
+}
+
+QVariant ProjectFileSystemModel::data(const QModelIndex &index, int role) const
+{
+ const auto &item = m_items.at(index.row());
+
+ switch (role) {
+ case Qt::DecorationRole: {
+ QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return resourceImageUrl() + getIconName(path);
+ }
+
+ case IsExpandableRole: {
+ if (item.index == m_rootIndex) {
+ return false;
+ } else {
+ return hasVisibleChildren(item.index);
+ }
+ }
+
+ case IsDraggableRole:
+ return QFileInfo(item.index.data(QFileSystemModel::FilePathRole).toString()).isFile();
+
+ case IsReferencedRole: {
+ const QString path = item.index.data(QFileSystemModel::FilePathRole).toString();
+ return m_references.contains(path);
+ }
+
+ case DepthRole:
+ return item.depth;
+
+ case ExpandedRole:
+ return item.expanded;
+
+ default:
+ return m_model->data(item.index, role);
+ }
+}
+
+QMimeData *ProjectFileSystemModel::mimeData(const QModelIndexList &indexes) const
+{
+ const auto path = filePath(indexes.first().row()); // can only drag one item
+ CUICFile dragFile(Q3DStudio::CString::fromQString(path));
+ return CDropSourceFactory::Create(EUIC_FLAVOR_ASSET_UICFILE,
+ reinterpret_cast<void *>(&dragFile),
+ sizeof(dragFile));
+}
+
+QString ProjectFileSystemModel::filePath(int row) const
+{
+ if (row < 0 || row >= m_items.size())
+ return QString();
+ const auto &item = m_items.at(row);
+ return item.index.data(QFileSystemModel::FilePathRole).toString();
+}
+
+void ProjectFileSystemModel::updateReferences(bool emitDataChanged)
+{
+ m_references.clear();
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ const auto sourcePathList = bridge->GetSourcePathList();
+ const auto fontFileList = bridge->GetFontFileList();
+ const auto effectTextureList = bridge->GetDynamicObjectTextureList();
+
+ auto addFileReferences = [this, doc](const Q3DStudio::CString &str) {
+ auto path = doc->GetResolvedPathToDoc(str).toQString();
+ path = QDir::cleanPath(path);
+ m_references.append(path);
+ QString rootPath = QDir::cleanPath(doc->GetDocumentDirectory().toQString());
+ QString parentPath = QFileInfo(path).path();
+ do {
+ if (!m_references.contains(parentPath))
+ m_references.append(parentPath);
+ path = parentPath;
+ parentPath = QFileInfo(path).path();
+ } while (rootPath != path && parentPath != path);
+ };
+
+ std::for_each(sourcePathList.begin(), sourcePathList.end(), addFileReferences);
+ std::for_each(fontFileList.begin(), fontFileList.end(), addFileReferences);
+ std::for_each(effectTextureList.begin(), effectTextureList.end(), addFileReferences);
+
+ if (emitDataChanged)
+ Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), {IsReferencedRole});
+}
+
+void ProjectFileSystemModel::setRootPath(const QString &path)
+{
+ setRootIndex(m_model->setRootPath(path));
+}
+
+void ProjectFileSystemModel::setRootIndex(const QModelIndex &rootIndex)
+{
+ if (rootIndex == m_rootIndex)
+ return;
+
+ clearModelData();
+ updateReferences(false);
+
+ m_rootIndex = rootIndex;
+
+ beginInsertRows({}, 0, 0);
+ m_items.append({ m_rootIndex, 0, true, nullptr, 0 });
+ endInsertRows();
+
+ showModelTopLevelItems();
+}
+
+void ProjectFileSystemModel::clearModelData()
+{
+ beginResetModel();
+ m_items.clear();
+ endResetModel();
+}
+
+void ProjectFileSystemModel::showModelTopLevelItems()
+{
+ int rowCount = m_model->rowCount(m_rootIndex);
+
+ if (rowCount == 0) {
+ if (m_model->hasChildren(m_rootIndex) && m_model->canFetchMore(m_rootIndex))
+ m_model->fetchMore(m_rootIndex);
+ } else {
+ showModelChildItems(m_rootIndex, 0, rowCount - 1);
+ }
+}
+
+void ProjectFileSystemModel::showModelChildItems(const QModelIndex &parentIndex, int start, int end)
+{
+ const int parentRow = modelIndexRow(parentIndex);
+ if (parentRow == -1)
+ return;
+
+ Q_ASSERT(isVisible(parentIndex));
+
+ QVector<QModelIndex> rowsToInsert;
+ for (int i = start; i <= end; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex))
+ rowsToInsert.append(childIndex);
+ }
+
+ const int insertCount = rowsToInsert.count();
+ if (insertCount == 0)
+ return;
+
+ auto parent = &m_items[parentRow];
+
+ const int depth = parent->depth + 1;
+ const int startRow = parentRow + parent->childCount + 1;
+
+ beginInsertRows({}, startRow, startRow + insertCount - 1);
+
+ for (auto it = rowsToInsert.rbegin(); it != rowsToInsert.rend(); ++it)
+ m_items.insert(startRow, { *it, depth, false, parent, 0 });
+
+ for (; parent != nullptr; parent = parent->parent)
+ parent->childCount += insertCount;
+
+ endInsertRows();
+
+ // also fetch children so we're notified when files are added or removed in immediate subdirs
+ for (const auto &childIndex : rowsToInsert) {
+ if (m_model->hasChildren(childIndex) && m_model->canFetchMore(childIndex))
+ m_model->fetchMore(childIndex);
+ }
+}
+
+void ProjectFileSystemModel::expand(int row)
+{
+ Q_ASSERT(row >= 0 && row < m_items.size());
+
+ auto &item = m_items[row];
+ Q_ASSERT(item.expanded == false);
+
+ const auto &modelIndex = item.index;
+
+ const int rowCount = m_model->rowCount(modelIndex);
+ if (rowCount == 0) {
+ if (m_model->hasChildren(modelIndex) && m_model->canFetchMore(modelIndex))
+ m_model->fetchMore(modelIndex);
+ } else {
+ showModelChildItems(modelIndex, 0, rowCount - 1);
+ }
+
+ item.expanded = true;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+bool ProjectFileSystemModel::hasValidUrlsForDropping(const QList<QUrl> &urls) const
+{
+ for (const auto &url : urls) {
+ if (url.isLocalFile()) {
+ const QString path = url.toLocalFile();
+ const QFileInfo fileInfo(path);
+ if (fileInfo.isFile()) {
+ const QString extension = fileInfo.suffix();
+ return extension.compare(QLatin1String(CDialogs::GetDAEFileExtension()),
+ Qt::CaseInsensitive) == 0
+#ifdef QT_3DSTUDIO_FBX
+ || extension.compare(QLatin1String(CDialogs::GetFbxFileExtension()),
+ Qt::CaseInsensitive) == 0
+#endif
+ || getIconType(path) != OBJTYPE_UNKNOWN;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ProjectFileSystemModel::dropUrls(const QList<QUrl> &urls, int row)
+{
+ Q_ASSERT(row >= 0 && row < m_items.size());
+
+ const auto &item = m_items.at(row);
+ const QString targetPath = item.index.data(QFileSystemModel::FilePathRole).toString();
+ const QDir targetDir(targetPath);
+
+ if (targetDir.exists()) {
+ for (const auto& url : urls)
+ dropUrl(targetPath, url);
+
+ if (!item.expanded)
+ expand(row);
+ }
+}
+
+void ProjectFileSystemModel::dropUrl(const QDir &targetDir, const QUrl &url) const
+{
+ using namespace Q3DStudio;
+ using namespace UICIMP;
+ // Drag and Drop - From Explorer window to Project Palette
+ // For all valid Project File Types:
+ // - This performs a file copy from the source Explorer location to the selected Project Palette
+ // Folder
+ // - The destination copy must NOT be read-only even if the source is read-only
+ // For DAE, it will import the file.
+
+ if (!url.isLocalFile())
+ return;
+
+ const QString sourceFile = url.toLocalFile();
+
+ const QFileInfo fileInfo(sourceFile);
+ if (!fileInfo.isFile())
+ return;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ const QString extension = fileInfo.suffix();
+ const QString fileStem = fileInfo.baseName();
+ const QString outputFileName = QStringLiteral("%1.%2").arg(fileStem).arg(CDialogs::GetImportFileExtension());
+
+ if (extension.compare(QLatin1String(CDialogs::GetDAEFileExtension()), Qt::CaseInsensitive) == 0) {
+ SColladaTranslator translator(sourceFile);
+ const QDir outputDir = SFileTools::FindUniqueDestDirectory(targetDir, fileStem);
+ const QString fullOutputFile = outputDir.filePath(outputFileName);
+ const SImportResult importResult =
+ CPerformImport::TranslateToImportFile(translator, CFilePath::fromQString(fullOutputFile));
+ bool forceError = QFileInfo(fullOutputFile).isFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ sourceFile, importResult.m_Error, doc->GetImportFailedHandler(),
+ translator.m_TranslationLog, forceError);
+#ifdef QT_3DSTUDIO_FBX
+ } else if (extension.compare(QLatin1String(CDialogs::GetFbxFileExtension()), Qt::CaseInsensitive) == 0) {
+ SFbxTranslator translator(sourceFile);
+ const QDir outputDir = SFileTools::FindUniqueDestDirectory(targetDir, fileStem);
+ const QString fullOutputFile = outputDir.filePath(outputFileName);
+ const SImportResult importResult =
+ CPerformImport::TranslateToImportFile(translator, CFilePath::fromQString(fullOutputFile));
+ bool forceError = QFileInfo(fullOutputFile).isFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ sourceFile, importResult.m_Error, doc->GetImportFailedHandler(),
+ translator.m_TranslationLog, forceError);
+#endif
+ } else if (extension.compare(QLatin1String("svg"), Qt::CaseInsensitive) == 0) {
+ IDocumentReader &reader(doc->GetDocumentReader());
+ SPathImportTranslator translator(sourceFile, *reader.GetLuaContext(), reader.GetFoundation());
+ const QDir outputDir = SFileTools::FindUniqueDestDirectory(targetDir, fileStem);
+ const QString fullOutputFile = outputDir.filePath(outputFileName);
+ const SImportResult importResult =
+ CPerformImport::TranslateToImportFile(translator, CFilePath::fromQString(fullOutputFile));
+ bool forceError = QFileInfo(fullOutputFile).isFile() == false;
+ IDocumentEditor::DisplayImportErrors(
+ sourceFile, importResult.m_Error,
+ doc->GetImportFailedHandler(),
+ translator.m_TranslationLog, forceError);
+ } else {
+ // Copy the file to target directory
+ // FindAndCopyDestFile will make sure the file name is unique and make sure it is
+ // not read only.
+ bool copyResult = SFileTools::FindAndCopyDestFile(targetDir, sourceFile);
+ ASSERT(copyResult);
+
+ // For effect and custom material files, automatically copy related resources
+ if (CDialogs::IsEffectFileExtension(extension.toLatin1().data())
+ || CDialogs::IsMaterialFileExtension(extension.toLatin1().data())) {
+ std::vector<Q3DStudio::CString> effectFileSourcePaths;
+ doc->GetDocumentReader().ParseSourcePathsOutOfEffectFile(
+ Q3DStudio::CFilePath::GetAbsolutePath(CFilePath::fromQString(sourceFile)),
+ effectFileSourcePaths);
+
+ const QDir fileDir = QFileInfo(sourceFile).dir();
+ const QDir documentDir(doc->GetDocumentDirectory().toQString());
+
+ for (const auto &effectFile : effectFileSourcePaths) {
+ const QString sourcePath = fileDir.filePath(effectFile.toQString());
+ const QString resultPath = documentDir.filePath(effectFile.toQString());
+
+ const QFileInfo resultFileInfo(resultPath);
+ if (!resultFileInfo.exists()) {
+ resultFileInfo.dir().mkpath(".");
+ QFile::copy(sourcePath, resultPath);
+ }
+ }
+ }
+ }
+}
+
+void ProjectFileSystemModel::collapse(int row)
+{
+ Q_ASSERT(row >= 0 && row < m_items.size());
+
+ auto &item = m_items[row];
+ Q_ASSERT(item.expanded == true);
+
+ const int childCount = item.childCount;
+
+ if (childCount > 0) {
+ beginRemoveRows({}, row + 1, row + childCount);
+
+ m_items.erase(std::begin(m_items) + row + 1, std::begin(m_items) + row + 1 + childCount);
+
+ for (auto parent = &item; parent != nullptr; parent = parent->parent)
+ parent->childCount -= childCount;
+
+ endRemoveRows();
+ }
+
+ item.expanded = false;
+ Q_EMIT dataChanged(index(row), index(row));
+}
+
+int ProjectFileSystemModel::modelIndexRow(const QModelIndex &modelIndex) const
+{
+ auto it = std::find_if(
+ std::begin(m_items),
+ std::end(m_items),
+ [&modelIndex](const TreeItem &item)
+ {
+ return item.index == modelIndex;
+ });
+
+ return it != std::end(m_items) ? std::distance(std::begin(m_items), it) : -1;
+}
+
+bool ProjectFileSystemModel::isExpanded(const QModelIndex &modelIndex) const
+{
+ if (modelIndex == m_rootIndex)
+ return true;
+ const int row = modelIndexRow(modelIndex);
+ return row != -1 && m_items.at(row).expanded;
+}
+
+EStudioObjectType ProjectFileSystemModel::getIconType(const QString &path) const
+{
+ Q3DStudio::CFilePath filePath(Q3DStudio::CString::fromQString(path));
+ return Q3DStudio::ImportUtils::GetObjectFileTypeForFile(filePath).m_IconType;
+}
+
+QString ProjectFileSystemModel::getIconName(const QString &path) const
+{
+ QString iconName;
+
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile()) {
+ EStudioObjectType type = getIconType(path);
+ if (type != OBJTYPE_UNKNOWN)
+ iconName = CStudioObjectTypes::GetNormalIconName(type);
+ else
+ iconName = QStringLiteral("Objects-Layer-Normal.png");
+ } else {
+ iconName = QStringLiteral("Objects-Folder-Normal.png");
+ }
+
+ return iconName;
+}
+
+bool ProjectFileSystemModel::hasVisibleChildren(const QModelIndex &modelIndex) const
+{
+ const QDir dir(modelIndex.data(QFileSystemModel::FilePathRole).toString());
+ if (!dir.exists() || dir.isEmpty())
+ return false;
+
+ const auto fileInfoList = dir.entryInfoList(QDir::Dirs|QDir::Files|QDir::NoDotAndDotDot);
+ for (const auto &fileInfo : fileInfoList) {
+ if (fileInfo.isDir() || getIconType(fileInfo.filePath()) != OBJTYPE_UNKNOWN)
+ return true;
+ }
+
+ return false;
+}
+
+bool ProjectFileSystemModel::isVisible(const QModelIndex &modelIndex) const
+{
+ bool result = false;
+
+ if (modelIndex == m_rootIndex) {
+ result = true;
+ } else {
+ QString path = modelIndex.data(QFileSystemModel::FilePathRole).toString();
+ QFileInfo fileInfo(path);
+ if (fileInfo.isFile()) {
+ result = getIconType(path) != OBJTYPE_UNKNOWN;
+ } else {
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+void ProjectFileSystemModel::modelRowsInserted(const QModelIndex &parent, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parent)) {
+ showModelChildItems(parent, start, end);
+ } else {
+ if (hasVisibleChildren(parent)) {
+ // show expand arrow
+ const int row = modelIndexRow(parent);
+ Q_EMIT dataChanged(index(row), index(row));
+ }
+ }
+}
+
+void ProjectFileSystemModel::modelRowsRemoved(const QModelIndex &parent, int start, int end)
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ if (isExpanded(parent)) {
+ for (int i = start; i <= end; ++i) {
+ const int row = modelIndexRow(m_model->index(i, 0, parent));
+
+ if (row != -1) {
+ const auto &item = m_items.at(row);
+
+ beginRemoveRows({}, row, row + item.childCount);
+
+ for (auto parent = item.parent; parent != nullptr; parent = parent->parent)
+ parent->childCount -= 1 + item.childCount;
+
+ m_items.erase(std::begin(m_items) + row, std::begin(m_items) + row + item.childCount + 1);
+
+ endRemoveRows();
+ }
+ }
+ }
+
+ if (!hasVisibleChildren(parent)) {
+ // hide expand arrow
+ const int row = modelIndexRow(parent);
+ m_items[row].expanded = false;
+ Q_EMIT dataChanged(index(row), index(row));
+ }
+}
+
+void ProjectFileSystemModel::modelLayoutChanged()
+{
+ if (!m_rootIndex.isValid())
+ return;
+
+ QSet<QPersistentModelIndex> expandedItems;
+ for (const auto &item : m_items) {
+ if (item.expanded)
+ expandedItems.insert(item.index);
+ }
+
+ const std::function<int(const QModelIndex &, TreeItem *)> insertChildren = [this, &expandedItems, &insertChildren](const QModelIndex &parentIndex, TreeItem *parent)
+ {
+ Q_ASSERT(isVisible(parentIndex));
+
+ const int rowCount = m_model->rowCount(parentIndex);
+ const int depth = parent->depth + 1;
+
+ int childCount = 0;
+
+ for (int i = 0; i < rowCount; ++i) {
+ const auto &childIndex = m_model->index(i, 0, parentIndex);
+ if (isVisible(childIndex)) {
+ const bool expanded = expandedItems.contains(childIndex);
+ m_items.append({ childIndex, depth, expanded, parent, 0 });
+ auto &item = m_items.last();
+ if (expanded) {
+ item.childCount = insertChildren(childIndex, &item);
+ childCount += item.childCount;
+ }
+ ++childCount;
+ }
+ }
+
+ return childCount;
+ };
+
+ const int itemCount = m_items.count();
+
+ m_items.erase(std::begin(m_items) + 1, std::end(m_items));
+ m_items.reserve(itemCount);
+ insertChildren(m_rootIndex, &m_items.first());
+
+ Q_ASSERT(m_items.count() == itemCount);
+
+ Q_EMIT dataChanged(index(0), index(itemCount - 1));
+}
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h
new file mode 100644
index 00000000..c9ef1b27
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef TREEVIEWADAPTOR_H
+#define TREEVIEWADAPTOR_H
+
+#include "StudioObjectTypes.h"
+
+#include <QFileSystemModel>
+#include <QAbstractListModel>
+#include <QList>
+#include <QUrl>
+
+class QFileSystemModel;
+
+class ProjectFileSystemModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ explicit ProjectFileSystemModel(QObject *parent = nullptr);
+
+ enum {
+ IsExpandableRole = QFileSystemModel::FilePermissions + 1,
+ IsDraggableRole,
+ IsReferencedRole,
+ DepthRole,
+ ExpandedRole,
+ };
+
+ void setRootPath(const QString &path);
+
+ QHash<int, QByteArray> roleNames() const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QMimeData *mimeData(const QModelIndexList &indexes) const override;
+
+ QString filePath(int row) const;
+
+ void updateReferences(bool emitDataChanged);
+
+ Q_INVOKABLE void expand(int row);
+ Q_INVOKABLE void collapse(int row);
+
+ Q_INVOKABLE void dropUrls(const QList<QUrl> &urls, int row);
+ Q_INVOKABLE bool hasValidUrlsForDropping(const QList<QUrl> &urls) const;
+
+Q_SIGNALS:
+ void modelChanged(QAbstractItemModel *model);
+
+private:
+ void setRootIndex(const QModelIndex &rootIndex);
+ void clearModelData();
+ void showModelTopLevelItems();
+ void showModelChildItems(const QModelIndex &parentItem, int start, int end);
+ int modelIndexRow(const QModelIndex &modelIndex) const;
+ bool isExpanded(const QModelIndex &modelIndex) const;
+ QString getIconName(const QString &path) const;
+ EStudioObjectType getIconType(const QString &path) const;
+ bool isVisible(const QModelIndex& modelIndex) const;
+ bool hasVisibleChildren(const QModelIndex &modelIndex) const;
+ void dropUrl(const QDir &targetDir, const QUrl &url) const;
+
+ void modelRowsInserted(const QModelIndex &parent, int start, int end);
+ void modelRowsRemoved(const QModelIndex &parent, int start, int end);
+ void modelRowsMoved(const QModelIndex &parent, int start, int end);
+ void modelLayoutChanged();
+
+ struct TreeItem {
+ QPersistentModelIndex index;
+ int depth;
+ bool expanded;
+ TreeItem *parent;
+ int childCount;
+ };
+
+ QFileSystemModel *m_model = nullptr;
+ QPersistentModelIndex m_rootIndex;
+ QList<TreeItem> m_items;
+ QStringList m_references;
+};
+
+#endif // TREEVIEWADAPTOR_H
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.cpp b/src/Authoring/Studio/Palettes/Project/ProjectView.cpp
new file mode 100644
index 00000000..0bdc69a6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectView.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "ProjectView.h"
+#include "ProjectFileSystemModel.h"
+#include "Core.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "Literals.h"
+#include "StudioUtils.h"
+#include "ImportUtils.h"
+#include "StudioApp.h"
+#include "StudioClipboard.h"
+#include "StudioPreferences.h"
+#include "UICImport.h"
+#include "Dialogs.h"
+#include "IDocumentEditor.h"
+#include "ProjectContextMenu.h"
+
+#include <QtCore/qprocess.h>
+#include <QtCore/qtimer.h>
+#include <QtGui/qdrag.h>
+#include <QtGui/qdesktopservices.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+ProjectView::ProjectView(QWidget *parent) : QQuickWidget(parent)
+ , m_ProjectModel(new ProjectFileSystemModel(this))
+{
+ const QString theApplicationPath =
+ CUICFile::GetApplicationDirectory().GetAbsolutePath().toQString();
+
+ m_BehaviorDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Behavior Library")));
+ m_EffectDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Effect Library")));
+ m_FontDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Font Library")));
+ m_ImageDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Maps Library")));
+ m_MaterialDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Material Library")));
+ m_ModelDir = CUICFile(
+ Q3DStudio::CString::fromQString(theApplicationPath
+ + QLatin1String("/Content/Models Library")));
+
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ QTimer::singleShot(0, this, &ProjectView::initialize);
+
+ auto dispatch = g_StudioApp.GetCore()->GetDispatch();
+ dispatch->AddPresentationChangeListener(this);
+ dispatch->AddDataModelListener(this);
+}
+
+ProjectView::~ProjectView()
+{
+}
+
+QAbstractItemModel *ProjectView::projectModel() const
+{
+ return m_ProjectModel;
+}
+
+QSize ProjectView::sizeHint() const
+{
+ return {500,500};
+}
+
+void ProjectView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+ rootContext()->setContextProperty("_projectView"_L1, this);
+
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Project/ProjectView.qml"_L1));
+}
+
+void ProjectView::effectAction()
+{
+ m_EffectDir.Execute();
+}
+
+void ProjectView::fontAction()
+{
+ m_FontDir.Execute();
+}
+
+void ProjectView::imageAction()
+{
+ m_ImageDir.Execute();
+}
+
+void ProjectView::materialAction()
+{
+ m_MaterialDir.Execute();
+}
+
+void ProjectView::modelAction()
+{
+ m_ModelDir.Execute();
+}
+
+void ProjectView::behaviorAction()
+{
+ m_BehaviorDir.Execute();
+}
+
+void ProjectView::OnNewPresentation()
+{
+ rebuild();
+}
+
+void ProjectView::OnBeginDataModelNotifications()
+{
+}
+
+void ProjectView::OnEndDataModelNotifications()
+{
+ m_ProjectModel->updateReferences(true);
+}
+
+void ProjectView::OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance)
+{
+ Q_UNUSED(inInstance);
+}
+
+void ProjectView::OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance, long inInstanceCount)
+{
+ Q_UNUSED(inInstance);
+ Q_UNUSED(inInstanceCount);
+}
+
+void ProjectView::startDrag(int row)
+{
+ const auto index = m_ProjectModel->index(row);
+
+ QDrag drag(this);
+ drag.setMimeData(m_ProjectModel->mimeData({index}));
+ drag.exec(Qt::CopyAction);
+}
+
+void ProjectView::showInExplorer(int row) const
+{
+ if (row == -1)
+ return;
+ const auto path = m_ProjectModel->filePath(row);
+#if defined(Q_OS_WIN)
+ QProcess::startDetached("explorer", {"/select", path});
+#elif defined(Q_OS_MACOS)
+ QProcess::startDetached("/usr/bin/osascript", {"-e",
+ QStringLiteral("tell application \"Finder\" to reveal POSIX file \"%1\"").arg(path)});
+ QProcess::startDetached("/usr/bin/osascript", {"-e",
+ QStringLiteral("tell application \"Finder\" to activate")});
+#else
+ // we cannot select a file here, because no file browser really supports it...
+ QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath()));
+#endif
+}
+
+void ProjectView::copyPath(int row) const
+{
+ if (row == -1)
+ return;
+ const auto path = m_ProjectModel->filePath(row);
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto relativePath = doc->GetRelativePathToDoc(
+ Q3DStudio::CFilePath(Q3DStudio::CString::fromQString(path)));
+ CStudioClipboard::CopyTextToClipboard(relativePath.toQString());
+}
+
+void ProjectView::copyFullPath(int row) const
+{
+ if (row == -1)
+ return;
+ const auto path = m_ProjectModel->filePath(row);
+ CStudioClipboard::CopyTextToClipboard(path);
+}
+
+bool ProjectView::isGroup(int row) const
+{
+ if (row == -1)
+ return false;
+ Q3DStudio::CFilePath path(Q3DStudio::CString::fromQString(m_ProjectModel->filePath(row)));
+ return Q3DStudio::ImportUtils::GetObjectFileTypeForFile(path).m_ObjectType == OBJTYPE_GROUP;
+}
+
+void ProjectView::showContextMenu(int x, int y, int index)
+{
+ ProjectContextMenu contextMenu(this, index);
+ contextMenu.exec(mapToGlobal({x, y}));
+}
+
+void ProjectView::refreshImport(int row) const
+{
+ if (row == -1)
+ return;
+ using namespace Q3DStudio;
+ const auto path = m_ProjectModel->filePath(row);
+ UICIMP::ImportPtrOrError importPtr = UICIMP::Import::Load(path.toStdWString().c_str());
+ if (importPtr.m_Value) {
+ const auto destDir = QString::fromWCharArray(importPtr.m_Value->GetDestDir());
+ const auto srcFile = QString::fromWCharArray(importPtr.m_Value->GetSrcFile());
+ const QString fullSrcPath(QDir(destDir).filePath(srcFile));
+ const QFileInfo newFile(g_StudioApp.GetDialogs()->ConfirmRefreshModelFile(fullSrcPath));
+ if (newFile.exists() && newFile.isFile()){
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Refresh Import..."))
+ ->RefreshImport(fullSrcPath, CString::fromQString(newFile.filePath()));
+ }
+ }
+}
+
+void ProjectView::rebuild()
+{
+ const auto theDoc = g_StudioApp.GetCore()->GetDoc();
+ const Q3DStudio::CFilePath thePath(theDoc->GetDocumentPath().GetAbsolutePath());
+ const Q3DStudio::CFilePath theRootDirPath = thePath.GetDirectory();
+
+ m_ProjectModel->setRootPath(theRootDirPath.toQString());
+}
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.h b/src/Authoring/Studio/Palettes/Project/ProjectView.h
new file mode 100644
index 00000000..f46aa976
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectView.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef PROJECTVIEW_H
+#define PROJECTVIEW_H
+
+#include "DispatchListeners.h"
+#include "UICFile.h"
+
+#include <QQuickWidget>
+#include <QModelIndex>
+
+class ProjectFileSystemModel;
+
+class ProjectView : public QQuickWidget,
+ public CPresentationChangeListener,
+ public IDataModelListener
+
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QAbstractItemModel *projectModel READ projectModel NOTIFY projectChanged FINAL)
+
+public:
+ explicit ProjectView(QWidget *parent = nullptr);
+ ~ProjectView();
+
+ QSize sizeHint() const override;
+
+ QAbstractItemModel *projectModel() const;
+
+ Q_INVOKABLE void effectAction();
+ Q_INVOKABLE void fontAction();
+ Q_INVOKABLE void imageAction();
+ Q_INVOKABLE void materialAction();
+ Q_INVOKABLE void modelAction();
+ Q_INVOKABLE void behaviorAction();
+
+ Q_INVOKABLE void startDrag(int row);
+
+ Q_INVOKABLE void showInExplorer(int row) const;
+ Q_INVOKABLE void copyPath(int row) const;
+ Q_INVOKABLE void copyFullPath(int row) const;
+ Q_INVOKABLE void refreshImport(int row) const;
+
+ Q_INVOKABLE bool isGroup(int row) const;
+ Q_INVOKABLE void showContextMenu(int x, int y, int index);
+
+ // CPresentationChangeListener
+ void OnNewPresentation() override;
+ // IDataModelListener
+ void OnBeginDataModelNotifications() override;
+ void OnEndDataModelNotifications() override;
+ // These are used during drag operations or during operations which
+ // require immediate user feedback. So they are unimplemented, effectively,
+ // we ignore them.
+ void OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance) override;
+ void OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance,
+ long inInstanceCount) override;
+
+Q_SIGNALS:
+ void projectChanged();
+
+private:
+ void initialize();
+ void rebuild();
+
+ ProjectFileSystemModel *m_ProjectModel = nullptr;
+ QColor m_BaseColor = QColor::fromRgb(75, 75, 75);
+ CUICFile m_BehaviorDir{""};
+ CUICFile m_EffectDir{""};
+ CUICFile m_FontDir{""};
+ CUICFile m_ImageDir{""};
+ CUICFile m_MaterialDir{""};
+ CUICFile m_ModelDir{""};
+};
+
+#endif // PROJECTVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectView.qml b/src/Authoring/Studio/Palettes/Project/ProjectView.qml
new file mode 100644
index 00000000..88ff608f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Project/ProjectView.qml
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+import "../controls"
+
+Rectangle {
+ id: root
+
+ color: _backgroundColor
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 4
+
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ MouseArea {
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton
+ onClicked: {
+ _projectView.showContextMenu(mouse.x, mouse.y, projectTree.currentIndex);
+ }
+ }
+
+ ListView {
+ id: projectTree
+
+ anchors.fill: parent
+
+ ScrollBar.vertical: ScrollBar {}
+
+ model: _projectView.projectModel
+
+ delegate: Rectangle {
+ id: delegateItem
+
+ width: parent.width
+ height: 20
+ color: index == projectTree.currentIndex ? _selectionColor : "transparent"
+
+ Row {
+ x: _depth*28
+ anchors.verticalCenter: delegateItem.verticalCenter
+
+ Image {
+ source: _resDir + (_expanded ? "arrow_down.png" : "arrow.png")
+ opacity: _isExpandable ? 1 : 0
+
+ MouseArea {
+ visible: _isExpandable
+ anchors.fill: parent
+ onClicked: {
+ if (_expanded)
+ projectTree.model.collapse(index)
+ else
+ projectTree.model.expand(index)
+ delegateMouseArea.clickPending = false
+ }
+ }
+ }
+
+ Image {
+ source: fileIcon
+ }
+
+ StyledLabel {
+ text: fileName
+ color: _isReferenced ? _textColor : _disabledColor
+ leftPadding: 2
+
+ Item {
+ id: dragItem
+
+ visible: _isDraggable
+ anchors.fill: parent
+
+ Drag.active: dragArea.drag.active
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+ Drag.dragType: Drag.Automatic
+ Drag.supportedActions: Qt.CopyAction
+
+ MouseArea {
+ id: dragArea
+ anchors.fill: parent
+ drag.target: dragItem
+ }
+
+ Drag.onDragStarted: _projectView.startDrag(index)
+ }
+ }
+ }
+
+ DropArea {
+ id: dropArea
+
+ anchors.fill: parent
+
+ onEntered: {
+ if (drag.hasUrls && projectTree.model.hasValidUrlsForDropping(drag.urls)) {
+ drag.accept(Qt.CopyAction)
+ } else {
+ drag.accepted = false;
+ }
+ }
+
+ onDropped: {
+ if (drop.hasUrls) {
+ projectTree.model.dropUrls(drop.urls, index)
+ }
+ }
+ }
+
+ MouseArea {
+ id: delegateMouseArea
+ property bool clickPending: false
+ anchors.fill: parent
+ acceptedButtons: Qt.RightButton|Qt.LeftButton
+ propagateComposedEvents: true
+ onPressed: {
+ projectTree.currentIndex = model.index;
+
+ // Presses must be ignored by this handler in order for dragging to work
+ mouse.accepted = false;
+
+ // Since ignoring presses means we don't get doubleClicked events,
+ // detect doubleclick using custom timer.
+ if (clickPending) {
+ if (_isExpandable) {
+ if (_expanded)
+ projectTree.model.collapse(index);
+ else
+ projectTree.model.expand(index);
+ }
+ clickPending = false;
+ } else {
+ clickPending = true;
+ doubleClickTimer.restart();
+ }
+ }
+ Timer {
+ id: doubleClickTimer
+ repeat: false
+ triggeredOnStart: false
+ interval: 500
+ onTriggered: parent.clickPending = false;
+ }
+ }
+ }
+ }
+ }
+
+ StyledMenuSeparator {}
+
+ RowLayout {
+ width: parent.width
+ Layout.margins: 4
+ Layout.rightMargin: 12
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Effect-Normal.png";
+ onClicked: _projectView.effectAction()
+ toolTipText: qsTr("Open Effect Library directory")
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Text-Normal.png";
+ onClicked: _projectView.fontAction()
+ toolTipText: qsTr("Open Font Library directory")
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Image-Normal.png";
+ onClicked: _projectView.imageAction()
+ toolTipText: qsTr("Open Maps Library directory")
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Material-Normal.png";
+ onClicked: _projectView.materialAction()
+ toolTipText: qsTr("Open Material Library directory")
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Model-Normal.png";
+ onClicked: _projectView.modelAction()
+ toolTipText: qsTr("Open Models Library directory")
+ }
+
+ StyledToolButton {
+ enabledImage: "Objects-Behavior-Normal.png";
+ onClicked: _projectView.behaviorAction()
+ toolTipText: qsTr("Open Behavior Library directory")
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.cpp b/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.cpp
new file mode 100644
index 00000000..9af46cb1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "SlideContextMenu.h"
+#include "SlideView.h"
+
+SlideContextMenu::SlideContextMenu(SlideView *parent, int row, int rowCount, bool master)
+ : QMenu(parent)
+ , m_view(parent)
+ , m_row(row)
+ , m_rowCount(rowCount)
+{
+ QAction *action = new QAction(tr("New Slide"));
+ action->setEnabled(!master);
+ connect(action, &QAction::triggered, this, &SlideContextMenu::handleAddNewSlide);
+ addAction(action);
+
+ action = new QAction(tr("Delete Slide"));
+ action->setEnabled(!master && m_row != -1);
+ connect(action, &QAction::triggered, this, &SlideContextMenu::handleRemoveSlide);
+ addAction(action);
+
+ action = new QAction(tr("Duplicate Slide"));
+ action->setEnabled(!master && m_row != -1);
+ connect(action, &QAction::triggered, this, &SlideContextMenu::handleDuplicateSlide);
+ addAction(action);
+}
+
+SlideContextMenu::~SlideContextMenu()
+{
+}
+
+void SlideContextMenu::handleAddNewSlide()
+{
+ m_view->addNewSlide(m_row == -1 ? m_rowCount : m_row + 1);
+}
+
+void SlideContextMenu::handleRemoveSlide()
+{
+ m_view->removeSlide(m_row);
+}
+
+void SlideContextMenu::handleDuplicateSlide()
+{
+ m_view->duplicateSlide(m_row);
+}
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.h b/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.h
new file mode 100644
index 00000000..bb4bb864
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideContextMenu.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SLIDE_CONTEXT_MENU_H
+#define SLIDE_CONTEXT_MENU_H
+
+#include <QtWidgets/qmenu.h>
+
+class SlideView;
+
+class SlideContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ explicit SlideContextMenu(SlideView *parent, int row, int rowCount, bool master);
+ virtual ~SlideContextMenu();
+
+private Q_SLOTS:
+ void handleAddNewSlide();
+ void handleRemoveSlide();
+ void handleDuplicateSlide();
+
+private:
+ SlideView *m_view;
+ int m_row;
+ int m_rowCount;
+};
+#endif // SLIDE_CONTEXT_MENU_H
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp
new file mode 100644
index 00000000..d2ef842c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "SlideModel.h"
+
+#include "CmdActivateSlide.h"
+#include "Core.h"
+#include "Doc.h"
+#include "StudioApp.h"
+
+#include "IDocumentEditor.h"
+
+#include "ClientDataModelBridge.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+
+SlideModel::SlideModel(int slideCount, QObject *parent) : QAbstractListModel(parent)
+ , m_slides(slideCount)
+{
+
+}
+
+QVariant SlideModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return {};
+
+ const auto row = index.row();
+
+ switch (role) {
+ case NameRole:
+ return slideName(m_slides[row]);
+ case SelectedRole:
+ return row == m_selectedRow;
+ }
+
+ return {};
+}
+
+bool SlideModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return false;
+
+ auto &slideHandle = m_slides[index.row()];
+
+ switch (role) {
+ case NameRole: {
+ setSlideName(slideHandle, value.toString());
+ Q_EMIT dataChanged(index, index, {role});
+ break;
+ }
+ case HandleRole: {
+ slideHandle = value.value<UICDM::CUICDMSlideHandle>();
+ Q_EMIT dataChanged(index, index, {HandleRole, NameRole});
+ break;
+ }
+ case SelectedRole: {
+ m_selectedRow = value.toBool() ? index.row() : -1;
+
+ if (m_selectedRow != -1) {
+ CCmdActivateSlide *theCmd = new CCmdActivateSlide(GetDoc(), m_slides[m_selectedRow]);
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ }
+
+ Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {role});
+ return true;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+int SlideModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_slides.count();
+}
+
+QHash<int, QByteArray> SlideModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(NameRole, "name");
+ names.insert(SelectedRole, "selected");
+
+ return names;
+}
+
+bool SlideModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ if (row > m_slides.count())
+ return false;
+
+ beginInsertRows(parent, row, row + count - 1);
+ for (int i = 0; i < count; i++)
+ m_slides.insert(row, {});
+ endInsertRows();
+
+ setData(index(row + count - 1), true, SelectedRole);
+ return true;
+}
+
+bool SlideModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row + count > m_slides.count())
+ return false;
+
+ bool selectionRemoved = false;
+ beginRemoveRows(parent, row, row + count - 1);
+ for (int i = 0; i < count; i++) {
+ if (m_selectedRow == row)
+ selectionRemoved = true;
+ m_slides.removeAt(row);
+ }
+ endRemoveRows();
+
+ auto newSelectedRow = -1;
+ if (selectionRemoved) {
+ if (row > 0)
+ newSelectedRow = row - 1;
+ else
+ newSelectedRow = row + count - 1;
+ } else if (m_selectedRow >= m_slides.count()) {
+ newSelectedRow = m_slides.count() - 1;
+ }
+ if (newSelectedRow != -1)
+ setData(index(newSelectedRow), true, SelectedRole);
+
+ return true;
+}
+
+void SlideModel::duplicateRow(int row)
+{
+ const auto handle = m_slides[row];
+
+ beginInsertRows({}, row, row);
+ m_slides.insert(row + 1, Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(),
+ QObject::tr("Duplicate Slide"))
+ ->DuplicateSlide(handle));
+ endInsertRows();
+ setData(index(row + 1), true, SelectedRole);
+
+ Q_EMIT dataChanged(index(row, 0), index(row + 1, 0), {});
+}
+
+void SlideModel::move(int fromRow, int toRow)
+{
+ if (fromRow == toRow)
+ return;
+
+ auto handle = m_slides[fromRow];
+ // toRow + 1 as DocumentEditor uses 1 based indexes for slides
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Rearrange Slide"))
+ ->RearrangeSlide(handle, toRow + 1);
+
+ if (fromRow > toRow)
+ beginMoveRows({}, fromRow, fromRow, {}, toRow);
+ else
+ beginMoveRows({}, fromRow, fromRow, {}, toRow + 1);
+ m_slides.move(fromRow, toRow);
+ endMoveRows();
+}
+
+void SlideModel::clear()
+{
+ beginResetModel();
+ m_slides.clear();
+ endResetModel();
+}
+
+void SlideModel::addNewSlide(int row)
+{
+ const auto handle = (row < m_slides.size()) ? m_slides[row] : m_slides.last();
+
+ const auto instanceHandle = GetBridge()->GetOwningComponentInstance(handle);
+ UICDM::CUICDMSlideHandle theMasterSlide = GetBridge()->GetComponentSlide(instanceHandle, 0);
+
+ beginInsertRows({}, row, row);
+ m_slides.insert(row, Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(),
+ QObject::tr("Create Slide"))
+ ->AddSlide(theMasterSlide, row + 1));
+ endInsertRows();
+
+ setData(index(row), true, SelectedRole);
+}
+
+void SlideModel::removeSlide(int row)
+{
+ const auto handle = m_slides[row];
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Delete Slide"))->DeleteSlide(handle);
+ removeRows(row, 1);
+}
+
+bool SlideModel::hasSlideWithName(const QString &name) const
+{
+ for (const auto &slide: m_slides) {
+ if (slideName(slide) == name)
+ return true;
+ }
+ return false;
+}
+
+QString SlideModel::slideName(const UICDM::CUICDMSlideHandle &handle) const
+{
+ auto doc = GetDoc();
+ if (!doc->IsValid())
+ return {};
+ const auto instanceHandle = doc->GetStudioSystem()->GetSlideSystem()->GetSlideInstance(handle);
+ return GetBridge()->GetName(instanceHandle).toQString();
+}
+
+void SlideModel::setSlideName(const UICDM::CUICDMSlideHandle &handle, const QString &name)
+{
+ const auto oldName = slideName(handle);
+ if (oldName != name && !name.trimmed().isEmpty()) {
+ using namespace UICDM;
+ CDoc *theDoc = GetDoc();
+ CClientDataModelBridge *theBridge = GetBridge();
+ if (!theBridge)
+ return;
+ const auto instanceHandle = GetDoc()->GetStudioSystem()->
+ GetSlideSystem()->GetSlideInstance(handle);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*theDoc, QObject::tr("Set Slide Name"))
+ ->SetSlideName(instanceHandle, theBridge->GetNameProperty(),
+ Q3DStudio::CString::fromQString(oldName),
+ Q3DStudio::CString::fromQString(name));
+ }
+}
+
+CDoc *SlideModel::GetDoc() const
+{
+ return g_StudioApp.GetCore()->GetDoc();
+}
+
+CClientDataModelBridge *SlideModel::GetBridge() const
+{
+ auto doc = GetDoc();
+ if (!doc->IsValid())
+ return nullptr;
+ return doc->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.h b/src/Authoring/Studio/Palettes/Slide/SlideModel.h
new file mode 100644
index 00000000..659fc548
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SLIDEMODEL_H
+#define SLIDEMODEL_H
+
+#include <QAbstractListModel>
+
+#include "UICDMHandles.h"
+
+class CClientDataModelBridge;
+class CDoc;
+
+class SlideModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum Roles {
+ NameRole = Qt::DisplayRole,
+ HandleRole = Qt::UserRole + 1,
+ SelectedRole
+ };
+
+ SlideModel(int slideCount, QObject *parent = nullptr);
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::DisplayRole) override;
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ bool insertRows(int row, int count,
+ const QModelIndex &parent = QModelIndex()) override;
+ bool removeRows(int row, int count,
+ const QModelIndex &parent = QModelIndex()) override;
+ void duplicateRow(int row);
+ void move(int fromRow, int toRow);
+
+ void clear();
+ void addNewSlide(int row);
+ void removeSlide(int row);
+
+private:
+ bool hasSlideWithName(const QString &name) const;
+ QString slideName(const UICDM::CUICDMSlideHandle &handle) const;
+ void setSlideName(const UICDM::CUICDMSlideHandle &handle, const QString &name);
+ inline CDoc *GetDoc() const;
+ inline CClientDataModelBridge *GetBridge() const;
+
+ QVector<UICDM::CUICDMSlideHandle> m_slides;
+ int m_selectedRow = -1;
+};
+
+
+#endif // SLIDEMODEL_H
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.cpp b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp
new file mode 100644
index 00000000..08b38a2c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "SlideView.h"
+#include "CColor.h"
+#include "Core.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "Literals.h"
+#include "StudioPreferences.h"
+#include "SlideModel.h"
+#include "StudioApp.h"
+#include "StudioUtils.h"
+#include "SlideContextMenu.h"
+
+#include "ClientDataModelBridge.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qtimer.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+SlideView::SlideView(QWidget *parent) : QQuickWidget(parent)
+ , m_MasterSlideModel(new SlideModel(1, this))
+ , m_SlidesModel(new SlideModel(0, this))
+ , m_ActiveRoot(0)
+{
+ g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this);
+ setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_CurrentModel = m_SlidesModel;
+ QTimer::singleShot(0, this, &SlideView::initialize);
+}
+
+SlideView::~SlideView()
+{
+ clearSlideList();
+ g_StudioApp.GetCore()->GetDispatch()->RemovePresentationChangeListener(this);
+}
+
+bool SlideView::showMasterSlide() const
+{
+ return m_CurrentModel == m_MasterSlideModel;
+}
+
+void SlideView::setShowMasterSlide(bool show)
+{
+ const bool currentIsMaster = m_CurrentModel == m_MasterSlideModel;
+ if (show == currentIsMaster)
+ return;
+
+ if (show)
+ m_CurrentModel = m_MasterSlideModel;
+ else
+ m_CurrentModel = m_SlidesModel;
+
+ // We need to get the first slide in the correct master mode
+ CDoc *theDoc = GetDoc();
+ UICDM::CUICDMInstanceHandle theRoot = theDoc->GetActiveRootInstance();
+ CClientDataModelBridge *theBridge = GetBridge();
+ UICDM::CUICDMSlideHandle theNewActiveSlide =
+ theBridge->GetOrCreateGraphRoot(theRoot); // this will return the master slide
+ UICDM::ISlideSystem *theSlideSystem = theDoc->GetStudioSystem()->GetSlideSystem();
+ if (m_CurrentModel != m_MasterSlideModel) {
+ const auto theFind = m_MasterSlideReturnPointers.find(theNewActiveSlide);
+ size_t theSlideIndex = 1;
+ size_t theNumSlides = theSlideSystem->GetSlideCount(theNewActiveSlide);
+ if (theFind != m_MasterSlideReturnPointers.end() && theFind->second < theNumSlides)
+ theSlideIndex = theFind->second;
+
+ theNewActiveSlide = theSlideSystem->GetSlideByIndex(
+ theNewActiveSlide, theSlideIndex); // activate the first slide
+ } else {
+ int theIndex = theSlideSystem->GetActiveSlideIndex(theNewActiveSlide);
+ m_MasterSlideReturnPointers[theNewActiveSlide] = theIndex;
+ }
+
+ // We have forced a mode change, and so we need to set the current active TC
+ // to be in the correct mode so our slide palette will show the correct information
+ if (theNewActiveSlide.Valid()) {
+ theDoc->NotifyActiveSlideChanged(theNewActiveSlide);
+ }
+
+ Q_EMIT showMasterSlideChanged();
+ Q_EMIT currentModelChanged();
+}
+
+QSize SlideView::sizeHint() const
+{
+ return {150, 200};
+}
+
+void SlideView::deselectAll()
+{
+ g_StudioApp.GetCore()->GetDoc()->DeselectAllItems();
+}
+
+void SlideView::addNewSlide(int row)
+{
+ m_SlidesModel->addNewSlide(row);
+}
+
+void SlideView::removeSlide(int row)
+{
+ m_SlidesModel->removeSlide(row);
+}
+
+void SlideView::duplicateSlide(int row)
+{
+ m_SlidesModel->duplicateRow(row);
+}
+
+void SlideView::moveSlide(int from, int to)
+{
+ m_SlidesModel->move(from, to);
+}
+
+void SlideView::showContextMenu(int x, int y, int row)
+{
+ SlideContextMenu contextMenu(this, row, m_SlidesModel->rowCount(),
+ m_CurrentModel == m_MasterSlideModel);
+ contextMenu.exec(mapToGlobal({x, y}));
+}
+
+void SlideView::OnNewPresentation()
+{
+ // Register callbacks
+ UICDM::IStudioFullSystemSignalProvider *theSignalProvider =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider();
+ m_MasterSlideReturnPointers.clear();
+
+ m_Connections.push_back(theSignalProvider->ConnectActiveSlide(
+ std::bind(&SlideView::OnActiveSlide, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)));
+
+ // KDAB_TODO We most probably don't need to listen to the below signals,
+ // as the functionality is done in the model already. Remove after it is confirmed
+ // it works as desired when the rendering works.
+ m_Connections.push_back(theSignalProvider->ConnectSlideCreated(
+ std::bind(&SlideView::OnNewSlide, this, std::placeholders::_1)));
+ m_Connections.push_back(theSignalProvider->ConnectSlideDeleted(
+ std::bind(&SlideView::OnDeleteSlide, this, std::placeholders::_1)));
+ m_Connections.push_back(theSignalProvider->ConnectSlideRearranged(
+ std::bind(&SlideView::OnSlideRearranged, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3)));
+}
+
+void SlideView::OnClosingPresentation()
+{
+ m_Connections.clear();
+ clearSlideList();
+}
+
+void SlideView::OnActiveSlide(const UICDM::CUICDMSlideHandle &inMaster, int inIndex,
+ const UICDM::CUICDMSlideHandle &inSlide)
+{
+ // When the active slide changes, we need to update our button and mode
+ if (inMaster.Valid()) {
+ // if inIndex is 0, it means that we are activating master slide
+ setShowMasterSlide(inIndex == 0);
+ setActiveSlide(inSlide);
+ }
+}
+
+void SlideView::OnNewSlide(const UICDM::CUICDMSlideHandle &inSlide)
+{
+
+}
+
+void SlideView::OnDeleteSlide(const UICDM::CUICDMSlideHandle &inSlide)
+{
+
+}
+
+void SlideView::OnSlideRearranged(const UICDM::CUICDMSlideHandle &inMaster, int inOldIndex, int inNewIndex)
+{
+}
+
+void SlideView::initialize()
+{
+ CStudioPreferences::setQmlContextProperties(rootContext());
+ rootContext()->setContextProperty("_slideView"_L1, this);
+ rootContext()->setContextProperty("_resDir"_L1, resourceImageUrl());
+
+ engine()->addImportPath(qmlImportPath());
+ setSource(QUrl("qrc:/Studio/Palettes/Slide/SlideView.qml"_L1));
+}
+
+void SlideView::clearSlideList()
+{
+ m_ActiveRoot = 0;
+ m_SlidesModel->clear();
+}
+
+void SlideView::setActiveSlide(const UICDM::CUICDMSlideHandle &inActiveSlideHandle)
+{
+ // Make sure we are in the correct master mode based on the inActiveSlideHandle
+ // If we changed mode, then we need to force a rebuild
+ bool theRebuildFlag = isMaster(inActiveSlideHandle) && (m_CurrentModel != m_MasterSlideModel);
+
+ // Check to see if the incoming slide is a sibling of the current active slide
+ // If it is, then we may be able to update without rebuilding everything
+ if (!theRebuildFlag
+ && m_ActiveRoot == GetBridge()->GetOwningComponentInstance(inActiveSlideHandle)) {
+ // If this is a new active slide, but the same root parent
+ if (m_ActiveSlideHandle != inActiveSlideHandle) {
+ m_ActiveSlideHandle = inActiveSlideHandle;
+ }
+ } else {
+ // We have a new parent or a new slide that makes us rebuild the entire list
+ rebuildSlideList(inActiveSlideHandle);
+ }
+}
+
+void SlideView::rebuildSlideList(const UICDM::CUICDMSlideHandle &inActiveSlideHandle)
+{
+ // Clear out the existing slides
+ clearSlideList();
+
+ // Add new slide controls as required
+ if (inActiveSlideHandle.Valid()) {
+ m_ActiveSlideHandle = inActiveSlideHandle;
+ m_ActiveRoot = GetBridge()->GetOwningComponentInstance(inActiveSlideHandle);
+
+ // Get the Master Slide handle and the slide count
+ UICDM::ISlideSystem *theSlideSystem = GetSlideSystem();
+ UICDM::CUICDMSlideHandle theMasterSlide =
+ theSlideSystem->GetMasterSlide(inActiveSlideHandle);
+
+ // update handle for master slide
+ UICDM::CUICDMSlideHandle theMasterSlideHandle =
+ theSlideSystem->GetSlideByIndex(theMasterSlide, 0);
+ m_MasterSlideModel->setData(m_MasterSlideModel->index(0, 0),
+ QVariant::fromValue(theMasterSlideHandle),
+ SlideModel::HandleRole);
+
+ long theSlideCount = (long)theSlideSystem->GetSlideCount(theMasterSlide);
+
+ // Iterate through, creating the new slide controls
+ m_SlidesModel->clear();
+ m_SlidesModel->insertRows(0, theSlideCount - 1, {});
+ int row = 0;
+ for (long theSlideIndex = 1; theSlideIndex < theSlideCount; ++theSlideIndex) {
+ UICDM::CUICDMSlideHandle theSlideHandle =
+ theSlideSystem->GetSlideByIndex(theMasterSlide, theSlideIndex);
+ auto index = m_SlidesModel->index(row, 0);
+ m_SlidesModel->setData(index,
+ QVariant::fromValue(theSlideHandle),
+ SlideModel::HandleRole);
+ const auto instanceHandle =
+ GetDoc()->GetStudioSystem()->GetSlideSystem()->GetSlideInstance(theSlideHandle);
+ m_SlidesModel->setData(index,
+ GetBridge()->GetName(instanceHandle).toQString(),
+ SlideModel::NameRole);
+ // This slide is the active slide
+ if (theSlideHandle == m_ActiveSlideHandle) {
+ m_SlidesModel->setData(index, true, SlideModel::SelectedRole);
+ }
+ row++;
+ }
+ }
+}
+
+CDoc *SlideView::GetDoc()
+{
+ return g_StudioApp.GetCore()->GetDoc();
+}
+
+CClientDataModelBridge *SlideView::GetBridge()
+{
+ return GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+UICDM::ISlideSystem *SlideView::GetSlideSystem()
+{
+ return GetDoc()->GetStudioSystem()->GetSlideSystem();
+}
+
+long SlideView::GetSlideIndex(const UICDM::CUICDMSlideHandle &inSlideHandle)
+{
+ return GetSlideSystem()->GetSlideIndex(inSlideHandle);
+}
+
+bool SlideView::isMaster(const UICDM::CUICDMSlideHandle &inSlideHandle)
+{
+ return (0 == GetSlideIndex(inSlideHandle));
+}
+
+
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.h b/src/Authoring/Studio/Palettes/Slide/SlideView.h
new file mode 100644
index 00000000..a4347ddd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideView.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SLIDEVIEW_H
+#define SLIDEVIEW_H
+
+#include <QQuickWidget>
+
+#include "DispatchListeners.h"
+#include "SlideModel.h"
+
+#include "UICDMHandles.h"
+#include "UICDMSignals.h"
+#include <unordered_map>
+class CClientDataModelBridge;
+class CDoc;
+
+namespace UICDM {
+class ISlideSystem;
+}
+
+class SlideView : public QQuickWidget, public CPresentationChangeListener
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractItemModel *currentModel READ currentModel NOTIFY currentModelChanged FINAL)
+ Q_PROPERTY(bool showMasterSlide READ showMasterSlide WRITE setShowMasterSlide NOTIFY showMasterSlideChanged FINAL)
+public:
+ SlideView(QWidget *parent = nullptr);
+ ~SlideView();
+
+ bool showMasterSlide() const;
+ void setShowMasterSlide(bool show);
+ QAbstractItemModel *currentModel() { return m_CurrentModel; }
+ QSize sizeHint() const override;
+
+ Q_INVOKABLE void deselectAll();
+ Q_INVOKABLE void addNewSlide(int row);
+ Q_INVOKABLE void removeSlide(int row);
+ Q_INVOKABLE void duplicateSlide(int row);
+ Q_INVOKABLE void moveSlide(int from, int to);
+ Q_INVOKABLE void showContextMenu(int x, int y, int row);
+
+ // Presentation Change Listener
+ void OnNewPresentation() override;
+ void OnClosingPresentation() override;
+
+Q_SIGNALS:
+ void currentModelChanged();
+ void showMasterSlideChanged();
+
+
+protected:
+ // UICDM callbacks
+ virtual void OnActiveSlide(const UICDM::CUICDMSlideHandle &inMaster, int inIndex,
+ const UICDM::CUICDMSlideHandle &inSlide);
+ virtual void OnNewSlide(const UICDM::CUICDMSlideHandle &inSlide);
+ virtual void OnDeleteSlide(const UICDM::CUICDMSlideHandle &inSlide);
+ virtual void OnSlideRearranged(const UICDM::CUICDMSlideHandle &inMaster, int inOldIndex,
+ int inNewIndex);
+
+private:
+ void initialize();
+ void clearSlideList();
+ void setActiveSlide(const UICDM::CUICDMSlideHandle &inActiveSlideHandle);
+ inline CDoc *GetDoc();
+ inline CClientDataModelBridge *GetBridge();
+ inline UICDM::ISlideSystem *GetSlideSystem();
+ long GetSlideIndex(const UICDM::CUICDMSlideHandle &inSlideHandle);
+ bool isMaster(const UICDM::CUICDMSlideHandle &inSlideHandle);
+ void rebuildSlideList(const UICDM::CUICDMSlideHandle &inActiveSlideHandle);
+
+ SlideModel *m_CurrentModel = nullptr;
+ SlideModel *m_MasterSlideModel = nullptr;
+ SlideModel *m_SlidesModel = nullptr;
+ QColor m_BaseColor = QColor::fromRgb(75, 75, 75);
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>>
+ m_Connections; /// connections to the UICDM
+ typedef std::unordered_map<int, int> TIntIntMap;
+ // We need to remember which slide we were on when we entered the master slide.
+ // Then, when the users leave the master slide we can go back to roughly the same
+ // state.
+ TIntIntMap m_MasterSlideReturnPointers;
+
+ UICDM::CUICDMInstanceHandle m_ActiveRoot; ///< the object containing the slides to be inspected.
+ UICDM::CUICDMSlideHandle m_ActiveSlideHandle; ///< the active slide handle
+};
+
+#endif // SLIDEVIEW_H
diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.qml b/src/Authoring/Studio/Palettes/Slide/SlideView.qml
new file mode 100644
index 00000000..7bffcad3
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Slide/SlideView.qml
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import "../controls"
+
+Rectangle {
+
+ id: root
+
+ readonly property bool masterSlide: _slideView.showMasterSlide
+
+ color: _backgroundColor
+
+ MouseArea {
+ id: mainMouseArea
+
+ propagateComposedEvents: true
+ anchors.fill: parent
+ acceptedButtons: Qt.AllButtons
+ onClicked: {
+ if (mouse.button === Qt.RightButton) {
+ _slideView.showContextMenu(mouse.x, mouse.y, -1);
+ } else {
+ root.focus = true;
+ //Unselect All element when we click outside slider item in listView.
+ //It worked as it in old version.
+ _slideView.deselectAll();
+ mouse.accepted = false
+ }
+ }
+ }
+
+ Column {
+ anchors {
+ top: parent.top
+ topMargin: 5
+ horizontalCenter: parent.horizontalCenter
+ }
+
+ spacing: 5
+
+ Column {
+ id: masterButtonColumn
+ spacing: -4
+ anchors.horizontalCenter: parent.horizontalCenter
+ Button {
+ id: masterEditButton
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ onClicked: _slideView.showMasterSlide = !_slideView.showMasterSlide
+
+ background: Rectangle {
+ color: "transparent"
+ }
+ contentItem: Image {
+ source: _resDir + "Slide-Master-Active.png"
+ }
+ }
+ StyledLabel {
+ id: masterEditLabel
+ text: _slideView.showMasterSlide ? qsTr("Leave Master") : qsTr("Edit Master")
+ font.pixelSize: _fontSize
+ color: _masterColor
+ verticalAlignment: Text.AlignVCenter
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+ }
+
+ StyledMenuSeparator {
+ id: separator
+ leftPadding: 12
+ rightPadding: 12
+ }
+
+ ListView {
+ id: slideList
+
+ ScrollBar.vertical: ScrollBar {}
+
+ width: root.width
+ height: root.height - masterButtonColumn.height
+ - separator.height - parent.spacing * 2 - 10
+ anchors.horizontalCenter: parent.horizontalCenter
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+
+ model: _slideView.currentModel
+ spacing: 10
+
+
+ delegate: MouseArea {
+ id: delegateArea
+
+ property int dragIndex
+ property bool held : false
+
+ anchors.horizontalCenter: parent.horizontalCenter
+ height: delegateItem.height
+ width: parent.width
+
+ acceptedButtons: Qt.RightButton | Qt.LeftButton
+ drag.target: held ? delegateItem : null
+ drag.axis: Drag.YAxis
+
+
+ onPressed: {
+ dragIndex = model.index;
+ if (mouse.x > delegateItem.x && mouse.x < delegateItem.x + delegateItem.width)
+ held = true;
+ }
+ onReleased: held = false
+
+
+ onClicked: {
+ _slideView.deselectAll();
+ if (mouse.button === Qt.LeftButton) {
+ root.focus = true;
+ model.selected = true;
+ }
+ if (mouse.button === Qt.RightButton) {
+ const coords = mapToItem(root, mouse.x, mouse.y);
+ _slideView.showContextMenu(coords.x, coords.y, model.index);
+ }
+ }
+
+ Item {
+ id: delegateItem
+
+ anchors.centerIn: parent
+ height: column.implicitHeight
+ width: 100
+
+ Drag.keys: "application/x-slide"
+ Drag.active: delegateArea.held
+ Drag.hotSpot.x: width / 2
+ Drag.hotSpot.y: height / 2
+ Drag.source: delegateArea
+
+ Column {
+ id: column
+ spacing: 2
+ anchors.fill: parent
+ Image {
+ id: slideImage
+
+ source: {
+ if (masterSlide)
+ return _resDir + "Slide-Master-Active.png"
+ return model.selected ? _resDir + "Slide-Active.png"
+ : _resDir + "Slide-Normal.png";
+ }
+ }
+
+ Item {
+ anchors.horizontalCenter: slideImage.horizontalCenter
+
+ height: childrenRect.height
+ width: childrenRect.width
+ Row {
+ StyledLabel {
+ visible: !masterSlide
+ text: model.index + 1 + ": "
+ }
+
+ TextInput {
+ id: slideName
+
+ readOnly: masterSlide
+ selectByMouse: !readOnly
+ color: _textColor
+ text: model.name
+ font.pixelSize: _fontSize
+
+ onFocusChanged: {
+ if (focus && !readOnly)
+ selectAll();
+ }
+
+ onEditingFinished: {
+ model.name = text;
+ slideName.focus = false;
+ }
+
+ Keys.onEscapePressed: {
+ slideName.undo();
+ slideName.focus = false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ DropArea {
+ anchors.fill: parent
+ keys: "application/x-slide"
+ onEntered: {
+ var oldIndex = drag.source.dragIndex
+ var newIndex = model.index
+ _slideView.moveSlide(oldIndex, newIndex)
+ drag.source.dragIndex = newIndex
+ }
+ }
+
+ states: State {
+ when: held
+
+ ParentChange {
+ target: delegateItem
+ parent: slideList
+ }
+
+ PropertyChanges {
+ target: delegateItem
+ anchors.centerIn: null
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.cpp b/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.cpp
new file mode 100644
index 00000000..48fe8993
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "AreaBoundingRect.h"
+#include "Renderer.h"
+#include "CColor.h"
+
+//=============================================================================
+/**
+ * Draws the rectangle.
+ */
+void CAreaBoundingRect::Draw(CRenderer *inRenderer)
+{
+ CPt theSize = GetSize();
+ inRenderer->PushPen(CColor(89, 120, 223));
+
+ // Removed because alpha doesn't work on Mac
+ // CColor theOverlayColor( 184, 198, 246 );
+ // inRenderer->FillSolidRect( CPt( theSize.x - 1, theSize.y - 1 ) ), theOverlayColor );
+
+ // Draw the rectangle outline
+ if (theSize.y > 1 && theSize.x > 1) {
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theSize.y - 1));
+ inRenderer->LineTo(CPt(theSize.x - 1, theSize.y - 1));
+ inRenderer->LineTo(CPt(theSize.x - 1, 0));
+ inRenderer->LineTo(CPt(0, 0));
+ }
+ inRenderer->PopPen();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.h b/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.h
new file mode 100644
index 00000000..1ec98c55
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/AreaBoundingRect.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_AREA_BOUNDING_RECT
+#define INCLUDED_AREA_BOUNDING_RECT 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "OverlayControl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+class CAreaBoundingRect : public COverlayControl
+{
+public:
+ virtual ~CAreaBoundingRect(){}
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+};
+#endif // INCLUDED_AREA_BOUNDING_RECT
diff --git a/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.cpp
new file mode 100644
index 00000000..c0541fac
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.cpp
@@ -0,0 +1,440 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "AssetTimelineKeyframe.h"
+#include "StateTimebarlessRow.h"
+#include "Renderer.h"
+#include "MasterP.h"
+#include "StateRow.h"
+#include "KeyframeContextMenu.h"
+#include "HotKeys.h"
+#include "ResourceCache.h"
+#include "ITimelineControl.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "StudioUtils.h"
+#include "TimeEditDlg.h"
+
+CAssetTimelineKeyframe::CAssetTimelineKeyframe(CStateTimebarlessRow *inParentRow,
+ double inTimeRatio)
+ : m_Selected(false)
+ , m_ParentRow(inParentRow)
+ , m_IsMouseDown(false)
+ , m_IsDragging(false)
+ , m_TimeRatio(inTimeRatio)
+{
+ CResourceCache *theCache = CResourceCache::GetInstance();
+ m_Icon = theCache->GetBitmap("Keyframe-Master-Normal.png");
+ m_DisabledIcon = theCache->GetBitmap("Keyframe-Master-Disabled.png");
+ m_SelectedIcon = theCache->GetBitmap("Keyframe-Master-Selected.png");
+ m_DynamicIcon = theCache->GetBitmap("Keyframe-MasterDynamic-Normal.png");
+ m_DynamicSelectedIcon = theCache->GetBitmap("Keyframe-MasterDynamic-Selected.png");
+
+ m_RightIcon = theCache->GetBitmap("Keyframe-MasterRight-Normal.png");
+ m_RightDisabledIcon = theCache->GetBitmap("Keyframe-MasterRight-disabled.png");
+ m_RightSelectedIcon = theCache->GetBitmap("Keyframe-MasterRight-Selected.png");
+ m_RightDynamicIcon = theCache->GetBitmap("Keyframe-MasterRightDynamic-Normal.png");
+ m_RightDynamicSelectedIcon = theCache->GetBitmap("Keyframe-MasterRightDynamic-Selected.png");
+
+ m_LeftIcon = theCache->GetBitmap("Keyframe-MasterLeft-Normal.png");
+ m_LeftDisabledIcon = theCache->GetBitmap("Keyframe-MasterLeft-disabled.png");
+ m_LeftSelectedIcon = theCache->GetBitmap("Keyframe-MasterLeft-Selected.png");
+ m_LeftDynamicIcon = theCache->GetBitmap("Keyframe-MasterLeftDynamic-Normal.png");
+ m_LeftDynamicSelectedIcon = theCache->GetBitmap("Keyframe-MasterLeftDynamic-Selected.png");
+
+ m_RectOverHandled = false;
+ m_PreviousSelectState = false;
+}
+
+CAssetTimelineKeyframe::~CAssetTimelineKeyframe()
+{
+}
+
+//=============================================================================
+/**
+ * SetRectOverHandled: Sets if mouse rectangle has been handled
+ * param@ inState indicates if the rectangle over has been handled.
+ * return@ NONE
+ */
+
+void CAssetTimelineKeyframe::SetRectOverHandled(bool inState)
+{
+ m_RectOverHandled = inState;
+}
+
+//=============================================================================
+/**
+ * GetRectOverHandled: GetRectOverHandled
+ * param@ NONE
+ * return@ m_RectOverHandled, which indicates if the rectangle over has been handled
+ */
+bool CAssetTimelineKeyframe::GetRectOverHandled()
+{
+ return m_RectOverHandled;
+}
+
+//=============================================================================
+/**
+ * SetPreviousSelectState: Sets if the current keyframe was previously selected
+ * param@ inState is used to set m_PreviousSelectState.
+ * return@ NONE
+ */
+void CAssetTimelineKeyframe::SetPreviousSelectState(bool inState)
+{
+ m_PreviousSelectState = inState;
+}
+
+//=============================================================================
+/**
+ * GetPreviousSelectState: Returns the keyframe's previous select state
+ * param@ NONE
+ * return@ m_PreviousSelectState that stores the select state for the keyframe
+ */
+bool CAssetTimelineKeyframe::GetPreviousSelectState()
+{
+ return m_PreviousSelectState;
+}
+
+//=============================================================================
+/**
+* Updates the ToolTip and moves it to the correct place on screen.
+* @param inPoint the point that the tooltip is supposed to be placed.
+*/
+void CAssetTimelineKeyframe::RefreshToolTip(CPt inPoint)
+{
+ Q3DStudio::CString theCommentText;
+ CStateRow *theStateRow = m_ParentRow->GetStateRow();
+ CRct theTimelineBounds(theStateRow->GetTopControl()->GetBounds());
+
+ // format label
+ theCommentText = " " + ::FormatTimeString(GetTime());
+
+ inPoint.y = GetPosition().y - GetSize().y;
+ inPoint.x = GetSize().x / 2;
+ ShowMoveableWindow(inPoint, theCommentText, theTimelineBounds);
+}
+
+//=============================================================================
+/**
+ * Gets the correct image and draws
+ */
+void CAssetTimelineKeyframe::Draw(CRenderer *inRenderer)
+{
+ inRenderer->DrawBitmap(CPt(0, 0), GetImage());
+}
+
+//=============================================================================
+/**
+ * Gets the name of the current bitmap depending on the state of the button,
+ * postion of the mouse, etc. Returns name of the image for the up state by
+ * default.
+ * @return name of the image representing current state of the button
+ */
+QPixmap CAssetTimelineKeyframe::GetImage() const
+{
+ QPixmap theImage = m_Icon;
+ long theStartTime = m_ParentRow->GetStateRow()->GetStartTime();
+ long theEndTime = m_ParentRow->GetStateRow()->GetEndTime();
+
+ if (theStartTime == m_Time) {
+ theImage = m_LeftIcon;
+ if (!IsEnabled())
+ theImage = m_LeftDisabledIcon;
+ else if (m_IsDynamic) {
+ if (m_Selected)
+ return m_LeftDynamicSelectedIcon;
+ else
+ return m_LeftDynamicIcon;
+ } else if (m_Selected)
+ theImage = m_LeftSelectedIcon;
+ } else if (theEndTime == m_Time) {
+ theImage = m_RightIcon;
+ if (!IsEnabled())
+ theImage = m_RightDisabledIcon;
+ else if (m_IsDynamic) {
+ if (m_Selected)
+ return m_RightDynamicSelectedIcon;
+ else
+ return m_RightDynamicIcon;
+ } else if (m_Selected)
+ theImage = m_RightSelectedIcon;
+ } else {
+ if (!IsEnabled())
+ theImage = m_DisabledIcon;
+ else if (m_IsDynamic) {
+ if (m_Selected)
+ return m_DynamicSelectedIcon;
+ else
+ return m_DynamicIcon;
+ } else if (m_Selected)
+ theImage = m_SelectedIcon;
+ }
+ return theImage;
+}
+
+//=============================================================================
+/**
+ * @return true if the mouse is over the keyframe
+ * @param inPoint the point where the mouse is
+ */
+bool CAssetTimelineKeyframe::HitTest(const CPt &inPoint) const
+{
+ bool theRetVal = false;
+ // If not over the control then don't bother with specific checks
+ if (CControl::HitTest(inPoint)) {
+ // If the key is at the beginning or end of the timebar then calculate the test differently
+ long theStartTime = m_ParentRow->GetStateRow()->GetStartTime();
+ long theEndTime = m_ParentRow->GetStateRow()->GetEndTime();
+ CPt thePoint = inPoint - GetPosition();
+ if (theStartTime == m_Time)
+ theRetVal = (thePoint.x > 7);
+ else if (theEndTime == m_Time)
+ theRetVal = (thePoint.x < 9);
+ else {
+ if (m_Selected)
+ theRetVal = (thePoint.x > 1 && thePoint.x < 15);
+ else
+ theRetVal = (thePoint.x > 3 && thePoint.x < 13);
+ }
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Handler for left mouse down events.
+ * @param inPoint the point where the mouse is
+ * @param inFlags indicates modifier keys that were down at time of the event
+ */
+bool CAssetTimelineKeyframe::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Store the mouse down location in screen coordinates so that we can check the dragging buffer
+ // in OnMouseMove
+ m_MouseDownLoc = inPoint;
+
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ bool theClearPreviouslySelectedKeys = false;
+ bool theSelectedFlag = false;
+ // If the control key is down then we change state, otherwise
+ if (!((CHotKeys::MODIFIER_CONTROL & inFlags) == CHotKeys::MODIFIER_CONTROL)) {
+ theClearPreviouslySelectedKeys = !m_Selected; // clear if not multi-selecting
+ theSelectedFlag = true;
+ } else {
+ theSelectedFlag = !m_Selected;
+ }
+ m_ParentRow->OnKeySelected(m_Time, theSelectedFlag, theClearPreviouslySelectedKeys);
+ m_Selected = theSelectedFlag; // set this after OnKeySelected, because the function may
+ // clear out all previously selected keys including this
+ // TODO : sk - 1-1 mapping of seemingly useless calls in
+ // CPropertyTimelineKeyframe::OnMouseDown, see my comments there.
+ // m_StudioDoc->UpdateClientScene( true );
+ // m_ParentRow->GetStateRow( )->GetState( )->FireAnimatedPropertiesChanged( );
+
+ m_IsMouseDown = true;
+ CStateRow *theStateRow = m_ParentRow->GetStateRow();
+ long theStartTime = theStateRow->GetStartTime();
+ long theEndTime = theStateRow->GetEndTime();
+ m_Snapper.SetStartEndTime(theStartTime, theEndTime);
+ m_Snapper.SetSource(this);
+ m_Snapper.SetKeyFrameClicked(true);
+ m_Snapper.SetSnappingSelectedKeyframes(false);
+
+ theStateRow->GetTimebar()->GetSnappingListProvider().PopulateSnappingList(&m_Snapper);
+ m_Snapper.BeginDrag(inPoint.x);
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handler for right mouse down events.
+ * @param inPoint the point where the mouse is
+ * @param inFlags indicates modifier keys that were down at time of the event
+ */
+bool CAssetTimelineKeyframe::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseRDown(inPoint, inFlags)) {
+ if (!m_Selected) {
+ m_Selected = true;
+ m_ParentRow->OnKeySelected(m_Time, m_Selected, true);
+ }
+ ITimelineItemProperty *iProperty = nullptr;
+ if (GetTimelineItemBinding()->GetPropertyCount() > 0) {
+ iProperty = GetTimelineItemBinding()->GetProperty(0);
+ }
+ CKeyframeContextMenu theMenu(GetTimelineItemBinding()->GetKeyframesManager(), iProperty);
+ theMenu.SetTime(GetTime());
+ DoPopup(&theMenu, inPoint);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * called when this key is selected
+ *
+ * @param inState the state this key is selected to
+ */
+void CAssetTimelineKeyframe::Select(bool inState)
+{
+ if (m_Selected != inState) {
+ m_Selected = inState;
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * handler for the mouse up event
+ * @param inFlags the state of things when the mouse button was released
+ * @param inPoint the point where the mouse is
+ */
+void CAssetTimelineKeyframe::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_IsMouseDown = false;
+ m_IsDragging = false;
+
+ GetTimelineItemBinding()->CommitChangedKeyframes();
+
+ HideMoveableWindow();
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * handler for the onMouse Move event. Offsets selected keys.
+ * Displays the StudioToolTip for the keyframe, showing the time it is at.
+ *
+ * @param inFlags the state of things when the mouse was moved
+ * @param inPoint the point where the mouse is
+ */
+void CAssetTimelineKeyframe::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+ UICPROFILE(OnMouseMove);
+ // If the mouse is down and this is slected, then offst the keys
+ if (m_IsMouseDown && m_Selected) {
+ // If we are not yet dragging the keyframe
+ if (!m_IsDragging) {
+ long theDiff = ::abs(inPoint.x) - m_MouseDownLoc.x;
+ // Figure out if the mouse has moved far enough to start the drag, and readjust the drag
+ // postion on the snapper
+ m_IsDragging = (::abs(theDiff) > DRAGBUFFER);
+ if (m_IsDragging && (::abs(theDiff) - DRAGBUFFER) > 2) {
+ m_Snapper.BeginDrag(m_MouseDownLoc.x);
+ } else
+ m_Snapper.BeginDrag(inPoint.x);
+ }
+
+ // If we are now dragging, procceed as normal
+ if (m_IsDragging) {
+ long theNewTime = m_Snapper.ProcessDrag(m_Time, inPoint.x, inFlags);
+ long theDiffTime = theNewTime - m_Time;
+
+ if (theDiffTime != 0) {
+ // theDiffTime can get updated if its invalid.
+ theDiffTime = GetTimelineItemBinding()->OffsetSelectedKeyframes(theDiffTime);
+ // Set this key's time so it won't be recalced in Refresh keyframes in the row
+ SetTime(m_Time + theDiffTime);
+
+ Invalidate();
+ }
+ }
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the time ratio
+ *
+ * @param inTimeRatio the new ratio
+ */
+void CAssetTimelineKeyframe::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ CPt theSize = GetSize();
+ SetPosition(::TimeToPos(GetTime(), m_TimeRatio) - (theSize.x / 2), 0);
+}
+
+//=============================================================================
+/**
+ * Pass the double click notification on to the row and have it process it.
+ * The row will do object-specific actions on doubleclicks.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ * @return true stating that the event was processed.
+ */
+bool CAssetTimelineKeyframe::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ GetTimelineItemBinding()->OnEditKeyframeTime(m_Time, ASSETKEYFRAME);
+ m_IsMouseDown = false;
+ m_IsDragging = false;
+ return true;
+}
+
+//=============================================================================
+/**
+ * @return true if selected
+ */
+bool CAssetTimelineKeyframe::IsSelected()
+{
+ return m_Selected;
+}
+
+void CAssetTimelineKeyframe::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ *
+ */
+ITimelineItemBinding *CAssetTimelineKeyframe::GetTimelineItemBinding() const
+{
+ return m_ParentRow->GetStateRow()->GetTimelineItemBinding();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.h
new file mode 100644
index 00000000..54a8b501
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/AssetTimelineKeyframe.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_ASSET_TIMELINE_KEYFRAME
+#define INCLUDED_ASSET_TIMELINE_KEYFRAME 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "TimelineKeyframe.h"
+#include "Snapper.h"
+
+#include <QPixmap>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CStateTimebarlessRow;
+class ITimelineItemBinding;
+
+class CAssetTimelineKeyframe : public CControl, public CTimelineKeyframe
+{
+
+public:
+ CAssetTimelineKeyframe(CStateTimebarlessRow *inParentRow, double inTimeRatio);
+ ~CAssetTimelineKeyframe();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void Select(bool inState);
+ void SetTimeRatio(double inTimeRatio);
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool IsSelected();
+ void SetSize(CPt inSize) override;
+ bool HitTest(const CPt &inPoint) const override;
+ void SetRectOverHandled(bool inState);
+ bool GetRectOverHandled();
+ void SetPreviousSelectState(bool inState);
+ bool GetPreviousSelectState();
+
+protected:
+ void RefreshToolTip(CPt inPoint);
+ QPixmap GetImage() const;
+ ITimelineItemBinding *GetTimelineItemBinding() const;
+
+protected:
+ bool m_RectOverHandled; ///< Indicates if the mouse rect over has been handled.
+ bool m_PreviousSelectState; ///< Stores the previous select state for the keyframe.
+ bool m_Selected;
+ CStateTimebarlessRow *m_ParentRow;
+ bool m_IsMouseDown;
+ CPt m_MouseDownLoc; ///< Location of the mouse after an OnMouseDownEvent, in client coordinates
+ bool m_IsDragging; ///< Indicates whether or not the keyframe is currently being dragged,
+ ///determined by the pixel buffer
+ double m_TimeRatio;
+
+ CSnapper m_Snapper;
+ QPixmap m_Icon;
+ QPixmap m_DisabledIcon;
+ QPixmap m_SelectedIcon;
+ QPixmap m_DynamicIcon;
+ QPixmap m_DynamicSelectedIcon;
+ QPixmap m_LeftIcon;
+ QPixmap m_LeftDisabledIcon;
+ QPixmap m_LeftSelectedIcon;
+ QPixmap m_LeftDynamicIcon;
+ QPixmap m_LeftDynamicSelectedIcon;
+ QPixmap m_RightIcon;
+ QPixmap m_RightDisabledIcon;
+ QPixmap m_RightSelectedIcon;
+ QPixmap m_RightDynamicIcon;
+ QPixmap m_RightDynamicSelectedIcon;
+};
+
+#endif // INCLUDED_ASSET_TIMELINE_KEYFRAME
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp
new file mode 100644
index 00000000..7960ed96
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp
@@ -0,0 +1,1139 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "BaseStateRow.h"
+#include "PropertyRow.h"
+#include "BaseTimelineTreeControl.h"
+#include "ToggleControl.h"
+#include "BaseTimebarlessRow.h"
+#include "ColorControl.h"
+#include "StateRowFactory.h"
+#include "TimelineTimelineLayout.h"
+#include "ComponentContextMenu.h"
+#include "ITimelineControl.h"
+#include "ResourceCache.h"
+#include "StudioUtils.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "Bindings/ITimelineTimebar.h"
+
+const long CBaseStateRow::DEFAULT_TOGGLE_LENGTH = 57;
+
+CBaseStateRow::CBaseStateRow()
+ : m_TimeRatio(0.0f)
+ , m_TreeList(true)// true to align the children in the timeline.
+ , m_TreeControl(nullptr)
+ , m_ColorControl(nullptr)
+ , m_ToggleControl(nullptr)
+ , m_TimebarControl(nullptr)
+ , m_Loaded(false)
+ , m_IsExpanded(false)
+ , m_Highlighted(false)
+ , m_Dirty(false)
+ , m_Selected(false)
+ , m_TimelineItemBinding(nullptr)
+ , m_ActiveStart(0)
+ , m_ActiveEnd(0)
+{
+}
+
+CBaseStateRow::~CBaseStateRow()
+{
+ delete m_TreeControl;
+ delete m_ColorControl;
+ delete m_ToggleControl;
+ delete m_TimebarControl;
+
+ // Go through all the state rows and delete them, this control owns all child controls.
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->Dispose();
+
+ // Go through all the properties and delete them, this control owns all child controls.
+ TPropertyRowList::iterator thePropertyPos = m_PropertyRows.begin();
+ for (; thePropertyPos != m_PropertyRows.end(); ++thePropertyPos) {
+ CPropertyRow *theRow = (*thePropertyPos);
+ delete theRow;
+ }
+}
+
+void CBaseStateRow::Initialize(ITimelineItemBinding *inTimelineItemBinding)
+{
+ m_Dirty = true;
+ ASSERT(inTimelineItemBinding);
+ m_TimelineItemBinding = inTimelineItemBinding;
+
+ m_TreeControl = new CBaseTimelineTreeControl(this, GetTimelineItem()->IsMaster());
+ m_ColorControl = new CColorControl(this);
+ m_ToggleControl = CreateToggleControl();
+ m_TimebarControl = CreateTimebarRow();
+
+ long theTimebarHeight = CStudioPreferences::GetRowSize();
+ m_TreeControl->SetSize(CPt(500, theTimebarHeight));
+ m_ColorControl->SetAbsoluteSize(CPt(theTimebarHeight, theTimebarHeight));
+ m_ToggleControl->SetAbsoluteSize(CPt(DEFAULT_TOGGLE_LENGTH, theTimebarHeight));
+ m_TimebarControl->SetSize(CPt(800, theTimebarHeight));
+
+ ::CColor theColor = GetTimebarBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_ColorList.AddChild(m_ColorControl);
+ m_TreeList.AddChild(m_TreeControl);
+ m_ToggleList.AddChild(m_ToggleControl);
+ m_TimebarList.AddChild(m_TimebarControl);
+
+ // sk - I think setting controls' names is only useful for debugging.
+ /*Q3DStudio::CString theAssetName( m_Asset->GetName( ) );
+ m_TreeControl->SetName( theAssetName + "TreeControl" );
+ m_TreeList.SetName( theAssetName + "TreeList" );
+ m_ColorControl->SetName( theAssetName + "ColorControl" );
+ m_ColorList.SetName( theAssetName + "ColorList" );
+ m_ToggleControl->SetName( theAssetName + "ToggleControl" );
+ m_ToggleList.SetName( theAssetName + "ToggleList" );
+ m_TimebarControl->SetName( theAssetName + "TimebarControl" );
+ m_TimebarList.SetName( theAssetName + "TimebarList" );*/
+
+ // Bind after all the UI is setup.
+ m_TimelineItemBinding->Bind(this); // see Dispose where it properly unbinds.
+
+ ClearDirty();
+}
+
+//=============================================================================
+/**
+ * Expand this node of the tree control.
+ * This will display all children the fit the filter.
+ */
+void CBaseStateRow::Expand(bool inExpandAll /*= false*/, bool inExpandUp)
+{
+ if (!m_IsExpanded) {
+ m_Filter.SetExpanded(true);
+
+ // Expand/Collapse is done by adding and removing the children, add all the
+ // properties first so they are at the top of the list.
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(m_Filter, false);
+ }
+ // Add all the State rows after the properties.
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(m_Filter, false);
+ }
+
+ m_TreeControl->SetExpanded(true);
+ m_IsExpanded = true;
+ m_ColorControl->UpdateIconStatus();
+ }
+
+ if (inExpandAll) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->Expand(inExpandAll);
+ }
+
+ if (inExpandUp && m_ParentRow)
+ m_ParentRow->Expand(false, inExpandUp);
+}
+
+//=============================================================================
+/**
+ * Collapse this node of the tree control.
+ * This will hide all children of this control.
+ */
+void CBaseStateRow::Collapse(bool inCollapseAll /* = false */)
+{
+ if (m_IsExpanded) {
+ CFilter theFilter = m_Filter;
+ theFilter.SetExpanded(false);
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(theFilter);
+ }
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(theFilter);
+ }
+
+ m_TimelineItemBinding->OnCollapsed();
+
+ m_TreeControl->SetExpanded(false);
+ m_IsExpanded = false;
+ m_ColorControl->UpdateIconStatus();
+ }
+
+ if (inCollapseAll) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->Collapse(inCollapseAll);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Toggle the expansion state of this control.
+ * This will expand the control if it is closed, or collapse it if it is
+ * open.
+ */
+void CBaseStateRow::ToggleExpansion(CToggleButton *, CButtonControl::EButtonState inButtonState)
+{
+ if (inButtonState == CButtonControl::EBUTTONSTATE_UP)
+ Collapse();
+ else
+ Expand();
+}
+
+//=============================================================================
+/**
+ * Shows or hides rows for all children, based on the filter.
+ * @param inFilter Object specifying the filters currently applied to the timeline.
+ * @param inFilterChildren true if the filter should go recursively to children.
+ */
+void CBaseStateRow::Filter(const CFilter &inFilter, bool inFilterChildren /*= true*/)
+{
+ m_Filter = inFilter;
+
+ // For each child object
+ if (inFilterChildren) {
+ CFilter theChildFilter = inFilter;
+ theChildFilter.SetExpanded(m_IsExpanded);
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ // Apply the filter
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(theChildFilter);
+ }
+ }
+
+ // This flag determines whether or not the controls on this row should be shown, based on the
+ // filter
+ bool theVisibleFlag = PerformFilter(m_Filter);
+
+ m_IsViewable = theVisibleFlag;
+
+ theVisibleFlag &= inFilter.IsExpanded();
+
+ // Show or hide the controls on this row before we iterate through the properties
+ m_ColorList.SetVisible(theVisibleFlag);
+ m_TreeList.SetVisible(theVisibleFlag);
+ m_ToggleList.SetVisible(theVisibleFlag);
+ m_TimebarList.SetVisible(theVisibleFlag);
+
+ if (inFilterChildren) {
+ CFilter theChildFilter = inFilter;
+ theChildFilter.SetExpanded(m_IsExpanded);
+
+ // For each property on this object
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ // Apply the filter
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(theChildFilter);
+ }
+ }
+
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Get the color control for this row.
+ * @return the color control for this row.
+ */
+CControl *CBaseStateRow::GetColorControl()
+{
+ return &m_ColorList;
+}
+
+//=============================================================================
+/**
+ * Get the tree control for this row.
+ * @return the tree control for this row.
+ */
+CControl *CBaseStateRow::GetTreeControl()
+{
+ return &m_TreeList;
+}
+
+//=============================================================================
+/**
+ * Get the toggle control for this row.
+ * @return the toggle control for this row.
+ */
+CControl *CBaseStateRow::GetToggleControl()
+{
+ return &m_ToggleList;
+}
+
+//=============================================================================
+/**
+ * Get the timebar control for this row.
+ * @return the timebar control for this row.
+ */
+CControl *CBaseStateRow::GetTimebarControl()
+{
+ return &m_TimebarList;
+}
+
+//=============================================================================
+/**
+ * Remove a row from this control.
+ * @param inState the state of the row to be removed.
+ */
+void CBaseStateRow::RemoveRow(CStateRow *inRow)
+{
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ if (theRow == inRow) {
+ DeleteRow(theRow);
+ m_StateRows.erase(thePos);
+ break;
+ }
+ }
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Helper function to remove all controls of this property row and dispose of it.
+ */
+void CBaseStateRow::DeletePropertyRow(CPropertyRow *inPropertyRow)
+{
+ if (!inPropertyRow)
+ return;
+
+ m_ColorList.RemoveChild(inPropertyRow->GetColorControl());
+ m_TreeList.RemoveChild(inPropertyRow->GetTreeControl());
+ m_ToggleList.RemoveChild(inPropertyRow->GetToggleControl());
+ m_TimebarList.RemoveChild(inPropertyRow->GetTimebarControl());
+ delete inPropertyRow;
+}
+
+//=============================================================================
+/**
+ * By default, we don't show shy/eye/lock toggles
+ */
+CBlankToggleControl *CBaseStateRow::CreateToggleControl()
+{
+ return new CBlankToggleControl(this);
+}
+
+//=============================================================================
+/**
+ * Get the StateRow that is representing this child timeline item.
+ * @param inTimelineItem child timeline item
+ * @return the StateRow for inState.
+ */
+CStateRow *CBaseStateRow::GetRow(ITimelineItem *inTimelineItem)
+{
+ if (inTimelineItem) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ if ((*thePos)->GetTimelineItem() == inTimelineItem)
+ return (*thePos);
+ }
+ }
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Called when a row is to be completely removed from the UI
+ */
+void CBaseStateRow::DeleteRow(CStateRow *inRow)
+{
+ m_ColorList.RemoveChild(inRow->GetColorControl());
+ m_TreeList.RemoveChild(inRow->GetTreeControl());
+ m_ToggleList.RemoveChild(inRow->GetToggleControl());
+ m_TimebarList.RemoveChild(inRow->GetTimebarControl());
+
+ inRow->Dispose();
+}
+
+//=============================================================================
+/**
+ * Call from the child controls that the mouse is over one of the children.
+ * This is used to highlight the entire row on mouse over.
+ */
+void CBaseStateRow::OnMouseOver()
+{
+ if (!m_Highlighted) {
+ try {
+ // TODO: Added the try/catch block to prevent crashing when the instance handle is not
+ // found
+ // this will happen sometimes when delete the object from the timeline
+ // need to really fix this at the root.
+ ::CColor theColor = GetTimebarHighlightBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_Highlighted = true;
+ } catch (...) {
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Call from the child controls that the mouse is no longer over one of the children.
+ * This is used to highlight the entire row on mouse over.
+ */
+void CBaseStateRow::OnMouseOut()
+{
+ if (m_Highlighted) {
+ try {
+ // TODO: Added the try/catch block to prevent crashing when the instance handle is not
+ // found
+ // this will happen sometimes when delete the object from the timeline
+ // need to really fix this at the root.
+ ::CColor theColor = GetTimebarBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_Highlighted = false;
+ } catch (...) {
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Tells this that the Asset data has changed and it needs to be updated.
+ * Someone should call ClearDirty afterwards.
+ */
+void CBaseStateRow::OnDirty()
+{
+ m_Dirty = true;
+}
+
+void CBaseStateRow::ClearDirty()
+{
+ if (m_Dirty) {
+ m_TreeControl->Refresh(m_TimelineItemBinding->GetTimelineItem());
+ m_ToggleControl->Refresh();
+ m_ColorControl->Invalidate();
+ m_TimebarControl->RefreshRowMetaData();
+ m_Dirty = false;
+ }
+}
+
+//=============================================================================
+/**
+ * Recursively load the children of this control, used by derived classes
+ * This will load all the properties and states, and create controls for them.
+ */
+void CBaseStateRow::LoadChildren()
+{
+ if (!m_Loaded) {
+ m_Loaded = true;
+
+ LoadProperties();
+
+ CTimelineItemOrderedIterator theChildIter(m_TimelineItemBinding);
+ // Go through all the children and load them too.
+ for (; !theChildIter.IsDone(); ++theChildIter)
+ CreateChildRow(*theChildIter, nullptr);
+
+ GetTopControl()->OnLayoutChanged();
+ }
+}
+
+//=============================================================================
+/**
+ * Add a row that represents this child timeline item
+ * @param inNextItem indicates row to follow behind the row for inTimeLineItem, nullptr to append inRow
+ * to the end of the current list.
+ */
+void CBaseStateRow::AddChildRow(ITimelineItemBinding *inTimeLineItem,
+ ITimelineItemBinding *inNextItem)
+{
+ if (!inTimeLineItem)
+ return;
+
+ // only add if loaded, else it will get added twice.
+ if (m_Loaded) {
+ CStateRow *theStateRow = CreateChildRow(
+ inTimeLineItem, inNextItem ? GetRow(inNextItem->GetTimelineItem()) : nullptr);
+ if (theStateRow)
+ theStateRow->LoadChildren();
+ }
+ Expand(false, true);
+
+ CBaseStateRow *theRow = GetRow(inTimeLineItem->GetTimelineItem());
+ if (theRow) {
+ CControl *theTreeControl = theRow->GetTreeControl();
+ if (theTreeControl)
+ theTreeControl->EnsureVisible();
+ }
+}
+
+void CBaseStateRow::RemoveChildRow(ITimelineItemBinding *inTimelineItem)
+{
+ CStateRow *theChildRow = GetRow(inTimelineItem->GetTimelineItem());
+ inTimelineItem->SetParent(nullptr);
+ if (theChildRow) {
+ RemoveRow(theChildRow);
+ // preserving legacy behavior.
+ GetTopControl()->HideTimelineMoveableTooltip();
+ }
+}
+
+//=============================================================================
+/**
+ * Removes all child rows from this row. Called prior to a load. The load call is responsible for
+ * updating the UI.
+ */
+void CBaseStateRow::RemoveAllChildren()
+{
+ RemoveAllProperties();
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ DeleteRow(*thePos);
+
+ m_StateRows.clear();
+}
+
+//=============================================================================
+/**
+ * Remove all the properties from this object. Called prior to a load. The load call is responsible
+ * for updating the UI.
+ */
+void CBaseStateRow::RemoveAllProperties()
+{
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos)
+ DeletePropertyRow(*thePropPos);
+
+ m_PropertyRows.clear();
+}
+
+//=============================================================================
+/**
+ * Set this row to selected
+ */
+void CBaseStateRow::Select(SBaseStateRowSelectionKeyState inState,
+ bool inCheckKeySelection /*= true */)
+{
+ bool alreadySelected = m_Selected;
+ m_TimelineItemBinding->SetSelected(inState.IsControlDown());
+ if (inCheckKeySelection) {
+ if (inState.IsShiftDown())
+ m_TimebarControl->SelectAllKeys();
+ else if (!alreadySelected)
+ m_TimelineItemBinding->ClearKeySelection();
+ }
+}
+
+//=============================================================================
+/**
+ * Change the selection state of the row.
+ */
+void CBaseStateRow::OnSelected(bool inSelection)
+{
+ if (inSelection == m_Selected)
+ return;
+
+ m_Selected = inSelection;
+ if (inSelection) {
+ if (m_ParentRow)
+ m_ParentRow->Expand(false, true);
+
+ m_TreeControl->EnsureVisible();
+
+ m_TreeControl->OnSelect();
+ m_ToggleControl->OnSelect();
+ m_ColorControl->OnSelect();
+ m_TimebarControl->OnSelect();
+ } else {
+ m_TreeControl->OnDeselect();
+ m_ToggleControl->OnDeselect();
+ m_ColorControl->OnDeselect();
+ m_TimebarControl->OnDeselect();
+ }
+}
+
+//=============================================================================
+/**
+ * Call to add a property row as a child of this control.
+ * @param inRow the row to be added.
+ */
+void CBaseStateRow::AddPropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow /*= nullptr */)
+{
+ m_PropertyRows.push_back(inRow);
+ InitializePropertyRow(inRow, inNextRow);
+ // For snapping timebars/keyframes
+ inRow->SetSnappingListProvider(GetSnappingListProvider());
+
+ m_TimebarControl->SetDirty(true);
+}
+
+//=============================================================================
+/**
+ * Remove the property row.
+ */
+void CBaseStateRow::RemovePropertyRow(const CPropertyRow *inRow)
+{
+ if (!inRow)
+ return;
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *theRow = *thePropPos;
+ if (theRow == inRow) {
+ DeletePropertyRow(theRow);
+ m_PropertyRows.erase(thePropPos);
+
+ // Update flippy
+ OnChildVisibilityChanged();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Helper function to initialize a new property row
+ * @param inRow the row to be added.
+ * @param inNextRow if specified, row that should be after inRow after
+ * insertion.
+ */
+void CBaseStateRow::InitializePropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow /*= nullptr */)
+{
+ CFilter theFilter = m_Filter;
+ theFilter.SetExpanded(m_IsExpanded);
+
+ if (!inNextRow) { // not provided, this property row would be inserted before the first
+ // non-property row.
+ CTimelineItemOrderedIterator theIterator(m_TimelineItemBinding);
+ if (!theIterator.IsDone())
+ inNextRow = GetRow(theIterator.GetCurrent()->GetTimelineItem());
+ }
+ AddRowToUILists(inRow, inNextRow, theFilter);
+}
+
+void CBaseStateRow::AddRowToUILists(CTimelineRow *inRow, CTimelineRow *inNextRow, CFilter &inFilter)
+{
+ // Default the insert locations to the end of the list.
+ CControl *theNextColorControl = nullptr;
+ CControl *theNextTreeControl = nullptr;
+ CControl *theNextToggleControl = nullptr;
+ CControl *theNextTimebarControl = nullptr;
+ if (inNextRow) {
+ theNextColorControl = inNextRow->GetColorControl();
+ theNextTreeControl = inNextRow->GetTreeControl();
+ theNextToggleControl = inNextRow->GetToggleControl();
+ theNextTimebarControl = inNextRow->GetTimebarControl();
+ }
+ inRow->SetIndent(m_Indent + CTimelineRow::TREE_INDENT);
+ inRow->SetParent(this);
+ inRow->Filter(inFilter);
+ inRow->SetTimeRatio(m_TimeRatio);
+
+ CControl *theColorControl = inRow->GetColorControl();
+ CControl *theTreeControl = inRow->GetTreeControl();
+ CControl *theToggleControl = inRow->GetToggleControl();
+ CControl *theTimebarControl = inRow->GetTimebarControl();
+
+ // If not expanded then hide the controls.
+ if (!m_IsExpanded) {
+ theColorControl->SetVisible(false);
+ theTreeControl->SetVisible(false);
+ theToggleControl->SetVisible(false);
+ theTimebarControl->SetVisible(false);
+ }
+
+ // Add the controls to the lists in the prioritized order
+ m_ColorList.AddChild(theColorControl, theNextColorControl);
+ m_TreeList.AddChild(theTreeControl, theNextTreeControl);
+ m_ToggleList.AddChild(theToggleControl, theNextToggleControl);
+ m_TimebarList.AddChild(theTimebarControl, theNextTimebarControl);
+
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+CStateRow *CBaseStateRow::CreateChildRow(ITimelineItemBinding *inChildBinding, CStateRow *inNextRow)
+{
+ CStateRow *theRow =
+ CStateRowFactory::CreateStateRow(inChildBinding, this, GetSnappingListProvider());
+ if (theRow) { // add by appending to the list
+ AddStateRow(theRow, inNextRow);
+ }
+ inChildBinding->SetParent(m_TimelineItemBinding);
+ return theRow;
+}
+
+long CBaseStateRow::GetNumNonPropertyRows() const
+{
+ return static_cast<long>(m_StateRows.size());
+}
+
+CBaseStateRow *CBaseStateRow::GetNonPropertyRow(long inIndex) const
+{
+ return m_StateRows.at(inIndex);
+}
+
+long CBaseStateRow::GetNumPropertyRows() const
+{
+ return static_cast<long>(m_PropertyRows.size());
+}
+CPropertyRow *CBaseStateRow::GetPropertyRow(long inIndex) const
+{
+ return m_PropertyRows.at(inIndex);
+}
+
+//=============================================================================
+/**
+ * Call to add a state row as a child of this control.
+ * @param inRow the row to be added.
+ * @param inNextRow row to follow behind the row that would be added, nullptr to append inRow to the
+ * end of the current list.
+ */
+void CBaseStateRow::AddStateRow(CStateRow *inRow, CStateRow *inNextRow)
+{
+ if (inNextRow != nullptr) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ while (thePos != m_StateRows.end()) {
+ if ((*thePos) == inNextRow) {
+ m_StateRows.insert(thePos, inRow);
+ thePos = m_StateRows.end();
+ } else
+ ++thePos;
+ }
+ } else {
+ m_StateRows.push_back(inRow);
+ }
+
+ AddRowToUILists(inRow, inNextRow, m_Filter);
+}
+
+//=============================================================================
+/**
+ * Checks to see if there are any visible children of this row.
+ * This is used for figuring out whether the expand button should be displayed
+ * or not.
+ */
+bool CBaseStateRow::HasVisibleChildren()
+{
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ // Apply the filter
+ if ((*thePos)->IsViewable())
+ return true;
+ }
+
+ // For each property on this object
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ // Apply the filter
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is represented by a pixel.
+ * This modifies the length of this control.
+ * @param inTimePerPixel the time per pixel.
+ */
+void CBaseStateRow::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ m_TimebarControl->SetTimeRatio(inTimeRatio);
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->SetTimeRatio(inTimeRatio);
+ }
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->SetTimeRatio(inTimeRatio);
+ }
+}
+
+//=============================================================================
+/**
+ * Called when a child becomes visible/invisible.
+ */
+void CBaseStateRow::OnChildVisibilityChanged()
+{
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Called when the mouse is double clicked.
+ * @param inPoint location of the mouse at time of event
+ * @param inFlags modifier key states at time of event
+ */
+void CBaseStateRow::OnMouseDoubleClick(CPt, Qt::KeyboardModifiers inFlags)
+{
+ // Do nothing by default. Let subclasses define what to do.
+ Q_UNUSED(inFlags);
+}
+
+//=============================================================================
+/**
+ * Show context menu for this row
+ */
+void CBaseStateRow::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ Select(SBaseStateRowSelectionKeyState()); // ensure this is selected, but doesn't affect any key
+ // selections, because this can be triggered from a
+ // key being selected
+ CComponentContextMenu theMenu(m_TreeControl, m_TimelineItemBinding);
+ m_TreeControl->DoPopup(&theMenu, inPoint);
+}
+
+//=============================================================================
+/**
+ * Selects keys in a given rect
+ * @param inRect the rect to use for selection
+ */
+void CBaseStateRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown,
+ bool inGlobalCommitSelectionFlag)
+{
+ CRct theOffsetRect = inRect;
+ theOffsetRect.Offset(-m_TimebarList.GetPosition());
+
+ // Commits the keyframe selection by setting the keyframes' previous state to its current state,
+ // when the user releases the mouse button.
+ // This will help the keyframes to retain their original states even though they are
+ // not in the mouse select region.
+ if (inGlobalCommitSelectionFlag) {
+ m_TimebarControl->CommitSelections();
+
+ // iterates through every property row and commits the selection states of properties
+ // keyframes
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ thePropRow->CommitSelections();
+ }
+ }
+
+ if (m_IsExpanded) {
+ // Iterates each property row and select the keys that are in the rectangle
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ thePropRow->SelectKeysInRect(theOffsetRect, inModifierKeyDown);
+ }
+
+ // Recurse the each state row (or master row) and selects the property keyframes in them
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->SelectKeysInRect(theOffsetRect, inModifierKeyDown,
+ inGlobalCommitSelectionFlag);
+
+ } else {
+ // Selects all the master key frames in the rect
+ m_TimebarControl->SelectKeysInRect(theOffsetRect, inModifierKeyDown);
+ }
+}
+
+//=============================================================================
+/**
+ * Deletes all the keys for the asset that was chosen by the user
+ * @param inBatch the batch used to batch all the deletes together
+ */
+void CBaseStateRow::DeleteAllKeys()
+{
+ // Iterate through all the property rows and delete all their keys
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->DeleteAllKeys();
+ }
+}
+
+//=============================================================================
+/**
+ * Add snapping points to inSnappingList.
+ * This will add the snapping points for any visible objects to inSnappingList.
+ * @param inSnappingList the list to add the snapping points to.
+ */
+void CBaseStateRow::PopulateSnappingList(CSnapper *inSnappingList)
+{
+ inSnappingList->PushOffset(-m_TimebarList.GetPosition().y);
+ m_TimebarControl->PopulateSnappingList(inSnappingList);
+
+ if (IsExpanded()) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->PopulateSnappingList(inSnappingList);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Sets all the child control enable states
+ * @param inEnabled the state to set the controls to
+ */
+void CBaseStateRow::SetEnabled(bool inEnabled)
+{
+ m_TreeControl->SetEnabled(inEnabled);
+ m_ToggleControl->SetEnabled(inEnabled);
+ m_ColorControl->SetEnabled(inEnabled);
+ m_TimebarControl->SetEnabled(inEnabled);
+}
+
+//=============================================================================
+/**
+ * Begin dragging.
+ * sk - potential spot for refactoring the Drag&Drop implementation.
+ * Right now, each IDragable is implicitly assumed to be a asset implementation. See
+ * *DropSource.cpp: each IDragable is dynamically cast to its implementation.
+ */
+void CBaseStateRow::DoStartDrag(CControlWindowListener *inWndListener)
+{
+ m_TimelineItemBinding->DoStartDrag(inWndListener);
+}
+
+void CBaseStateRow::AcceptDropAfter(bool inAccept)
+{
+ m_TreeControl->AcceptDropAfter(inAccept);
+}
+
+void CBaseStateRow::AcceptDropBefore(bool inAccept)
+{
+ m_TreeControl->AcceptDropBefore(inAccept);
+}
+
+//=============================================================================
+/**
+ * Pass through to the binding to set up the target aset for a drag&drop action on this
+ *control.
+ */
+void CBaseStateRow::SetDropTarget(CDropTarget *inDropTarget)
+{
+ m_TimelineItemBinding->SetDropTarget(inDropTarget);
+}
+
+void CBaseStateRow::SetTimelineLatestTime(long inTime)
+{
+ long theLength = ::TimeToPos(inTime, m_TimeRatio) + CTimelineTimelineLayout::END_BUFFER_SIZE;
+ m_TimebarControl->SetAbsoluteSize(CPt(theLength, m_TimebarControl->GetSize().y));
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->SetTimelineLatestTime(inTime);
+}
+
+//=============================================================================
+/**
+ * Determines whether or not a row is expanded. A row can be expanded even if
+ * it is not visible.
+ * @return true if the row is currently expanded, otherwise false
+ */
+bool CBaseStateRow::IsExpanded()
+{
+ return m_IsExpanded;
+}
+
+//=============================================================================
+/**
+ * Determines whether or not a row is loaded. The rows are delayed loaded, i.e.
+ * it will be loaded when it's visible for the first time. Before it's loaded, any
+ * updates to the structure, say, adding dynamic properties does not need to update the
+ * timeline.
+ * @return true if the row is currently loaded, otherwise false
+ */
+bool CBaseStateRow::IsLoaded()
+{
+ return m_Loaded;
+}
+
+long CBaseStateRow::GetStartTime()
+{
+ ITimelineTimebar *theTimebar = m_TimelineItemBinding->GetTimelineItem()->GetTimebar();
+ if (theTimebar)
+ return theTimebar->GetStartTime();
+ return 0;
+}
+
+long CBaseStateRow::GetEndTime()
+{
+ ITimelineTimebar *theTimebar = m_TimelineItemBinding->GetTimelineItem()->GetTimebar();
+ if (theTimebar)
+ return theTimebar->GetEndTime();
+ return 0;
+}
+
+long CBaseStateRow::GetActiveStart()
+{
+ return m_ActiveStart;
+}
+long CBaseStateRow::GetActiveEnd()
+{
+ return m_ActiveEnd;
+}
+
+//=============================================================================
+/**
+ * Get the start time of this row, which is accumulative of all its descendants.
+ */
+long CBaseStateRow::GetEarliestStartTime()
+{
+ long theEarliestStartTime = 0;
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ long theStartTime = theRow->GetEarliestStartTime();
+ if (theStartTime < theEarliestStartTime)
+ theEarliestStartTime = theStartTime;
+ }
+ return theEarliestStartTime;
+}
+
+//=============================================================================
+/**
+ * Get the end time of this row, which is accumulative of all its descendants.
+ */
+long CBaseStateRow::GetLatestEndTime()
+{
+ long theLatestTime = 0;
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ long theEndTime = theRow->GetLatestEndTime();
+ if (theEndTime > theLatestTime)
+ theLatestTime = theEndTime;
+ }
+ return theLatestTime;
+}
+
+//=============================================================================
+/**
+ * Lame switch to get the normal state object specific icon.
+ * @return the icon to be used in the 'normal' state.
+ */
+QPixmap CBaseStateRow::GetIcon()
+{
+ return CResourceCache::GetInstance()->GetBitmap(
+ CStudioObjectTypes::GetNormalIconName(GetObjectType()));
+}
+
+//=============================================================================
+/**
+ * Lame switch to get the disabled state object specific icon.
+ * @return the icon to be used in the disabled state.
+ */
+QPixmap CBaseStateRow::GetDisabledIcon()
+{
+ return CResourceCache::GetInstance()->GetBitmap(
+ CStudioObjectTypes::GetDisabledIconName(GetObjectType()));
+}
+
+//=============================================================================
+/**
+ * @return the studio type of the object represented by this row
+ */
+EStudioObjectType CBaseStateRow::GetObjectType() const
+{
+ return GetTimelineItem()->GetObjectType();
+}
+
+ITimelineItemBinding *CBaseStateRow::GetTimelineItemBinding() const
+{
+ return m_TimelineItemBinding;
+}
+
+ITimelineItem *CBaseStateRow::GetTimelineItem() const
+{
+ return m_TimelineItemBinding->GetTimelineItem();
+}
+
+//=============================================================================
+/**
+ * When this row is no longer useful, clean up.
+ */
+void CBaseStateRow::Dispose()
+{
+ // Disconnection point
+ if (m_TimelineItemBinding)
+ m_TimelineItemBinding->Release();
+
+ CTimelineRow::Dispose();
+}
+
+void CBaseStateRow::UpdateActionStatus()
+{
+ m_ColorControl->UpdateIconStatus();
+}
+
+//=============================================================================
+/**
+ * Restores the focus state of this row.
+ */
+void CBaseStateRow::SetFocus()
+{
+ CControl *theParent = m_TreeControl->GetParent();
+ if (theParent)
+ theParent->GrabFocus(m_TreeControl);
+}
+
+CBaseTimebarlessRow *CBaseStateRow::GetTimebar() const
+{
+ return m_TimebarControl;
+}
+
+void CBaseStateRow::SetNameReadOnly(bool inReadOnly)
+{
+ m_TreeControl->SetNameReadOnly(inReadOnly);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.h b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.h
new file mode 100644
index 00000000..5a62dfc0
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.h
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BASE_STATE_ROW_H
+#define INCLUDED_BASE_STATE_ROW_H 1
+
+#pragma once
+
+#include "TimelineRow.h"
+#include "ListLayout.h"
+#include "ToggleButton.h"
+#include "DispatchListeners.h"
+
+class CPropertyRow;
+class CBaseTimelineTreeControl;
+class CColorControl;
+class CBlankToggleControl;
+class CBaseTimebarlessRow;
+class CStateRow;
+class CCmdBatch;
+class ITimelineItem;
+class ITimelineItemBinding;
+
+struct SBaseStateRowSelectionKeyState
+{
+ enum Enum {
+ NoKeyDown = 0,
+ ShiftKey = 1 << 0,
+ ControlKey = 1 << 1,
+ };
+ qt3ds::QT3DSU32 m_KeyState;
+ SBaseStateRowSelectionKeyState()
+ : m_KeyState(0)
+ {
+ }
+ void SetShiftDown() { m_KeyState = m_KeyState | ShiftKey; }
+ void SetControlDown() { m_KeyState = m_KeyState | ControlKey; }
+ bool IsShiftDown() const { return (m_KeyState & ShiftKey) != 0; }
+ bool IsControlDown() const { return (m_KeyState & ControlKey) != 0; }
+};
+
+class CBaseStateRow : public CTimelineRow
+{
+public:
+ typedef std::vector<CPropertyRow *> TPropertyRowList;
+ typedef std::vector<CStateRow *> TStateRowList;
+ static const long DEFAULT_TOGGLE_LENGTH;
+
+public:
+ CBaseStateRow();
+ virtual ~CBaseStateRow();
+
+ virtual void Initialize(ITimelineItemBinding *inTimelineItemBinding);
+
+ bool IsExpanded();
+ bool IsLoaded();
+ virtual void Expand(bool inExpandAll = false, bool inExpandUp = false);
+ virtual void Collapse(bool inCollapseAll = false);
+ void ToggleExpansion(CToggleButton *, CButtonControl::EButtonState);
+
+ void SetTimeRatio(double inTimePerPixel) override;
+
+ CControl *GetColorControl() override;
+ CControl *GetTreeControl() override;
+ CControl *GetToggleControl() override;
+ CControl *GetTimebarControl() override;
+
+ void Select(SBaseStateRowSelectionKeyState inKeyState, bool inCheckKeySelection = true);
+ void SelectKeysInRect(CRct inRect, bool inModifierKeyDown, bool inGlobalCommitSelectionFlag);
+ void DeleteAllKeys();
+
+ virtual void OnMouseOver();
+ virtual void OnMouseOut();
+ virtual void OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ virtual void OnDirty();
+ virtual void OnSelected(bool inSelected);
+
+ void LoadChildren();
+ void AddChildRow(ITimelineItemBinding *inTimeLineItem, ITimelineItemBinding *inNextItem);
+ void RemoveChildRow(ITimelineItemBinding *inTimeLineItem);
+
+ void RemoveRow(CStateRow *inRow);
+ void AddStateRow(CStateRow *inRow, CStateRow *inNextRow);
+ void AddPropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow = nullptr);
+ void RemovePropertyRow(const CPropertyRow *inRow);
+ void RemoveAllChildren();
+ void RemoveAllProperties();
+
+ long GetNumNonPropertyRows() const;
+ CBaseStateRow *GetNonPropertyRow(long inIndex) const;
+ long GetNumPropertyRows() const;
+ CPropertyRow *GetPropertyRow(long inIndex) const;
+
+ void Filter(const CFilter &inFilter, bool inFilterChildren = true) override;
+ CFilter *GetFilter() { return &m_Filter; }
+ void OnChildVisibilityChanged() override;
+
+ virtual bool HasVisibleChildren();
+
+ void PopulateSnappingList(CSnapper *inSnappingList) override;
+
+ virtual void SetEnabled(bool inEnabled);
+
+ void DoStartDrag(CControlWindowListener *inWndListener);
+ void AcceptDropAfter(bool inAccept);
+ void AcceptDropBefore(bool inAccept);
+ void SetDropTarget(CDropTarget *inDropTarget);
+
+ // CTimelineRow
+ virtual long GetEarliestStartTime();
+ long GetLatestEndTime() override;
+
+ long GetStartTime();
+ long GetEndTime();
+ long GetActiveStart();
+ long GetActiveEnd();
+ virtual bool CalculateActiveStartTime() = 0;
+ virtual bool CalculateActiveEndTime() = 0;
+ void Dispose() override;
+
+ virtual QPixmap GetIcon();
+ virtual QPixmap GetDisabledIcon();
+
+ EStudioObjectType GetObjectType() const;
+ ITimelineItemBinding *GetTimelineItemBinding() const;
+ ITimelineItem *GetTimelineItem() const;
+
+ void UpdateActionStatus();
+ void SetFocus();
+
+ CBaseTimebarlessRow *GetTimebar() const;
+
+ void SetNameReadOnly(bool inReadOnly);
+
+ void ClearDirty();
+
+protected:
+ void DeletePropertyRow(CPropertyRow *inPropertyRow);
+ virtual CBlankToggleControl *CreateToggleControl();
+ virtual CBaseTimebarlessRow *CreateTimebarRow() = 0;
+ virtual bool PerformFilter(const CFilter &inFilter) = 0;
+ CStateRow *GetRow(ITimelineItem *inTimelineItem);
+ void DeleteRow(CStateRow *inRow);
+ void SetTimelineLatestTime(long inLength);
+
+ virtual void LoadProperties() {}
+ void InitializePropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow = nullptr);
+
+ void AddRowToUILists(CTimelineRow *inRow, CTimelineRow *inNextRow, CFilter &inFilter);
+ CStateRow *CreateChildRow(ITimelineItemBinding *inChildBinding, CStateRow *inNextRow);
+
+ double m_TimeRatio;
+ CFilter m_Filter;
+ CListLayout m_ColorList;
+ CListLayout m_TreeList;
+ CListLayout m_ToggleList;
+ CListLayout m_TimebarList;
+
+ CBaseTimelineTreeControl *m_TreeControl;
+ CColorControl *m_ColorControl;
+ CBlankToggleControl *m_ToggleControl;
+ CBaseTimebarlessRow *m_TimebarControl;
+
+ TStateRowList m_StateRows;
+ TPropertyRowList m_PropertyRows;
+
+ bool m_Loaded;
+ bool m_IsExpanded;
+ bool m_Highlighted;
+ bool m_Dirty;
+ bool m_Selected;
+
+ ITimelineItemBinding *m_TimelineItemBinding;
+
+ long m_ActiveStart;
+ long m_ActiveEnd;
+};
+#endif // INCLUDED_BASE_STATE_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.cpp b/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.cpp
new file mode 100644
index 00000000..4ded07dd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "BaseTimebarlessRow.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "BaseStateRow.h"
+
+CBaseTimebarlessRow::CBaseTimebarlessRow()
+ : m_Selected(false)
+ , m_DirtyFlag(true)
+ , m_TimeRatio(0.0f)
+{
+}
+
+CBaseTimebarlessRow::~CBaseTimebarlessRow()
+{
+}
+
+void CBaseTimebarlessRow::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ // Fill in the background
+ if (!m_Selected)
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+ else
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetTimelineSelectColor());
+
+ // Draw the line at the bottom of this control and the one on the side
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Set this control to being highlighted or not.
+ * @param inIsHighlighted true if this is to be highlighted.
+ */
+void CBaseTimebarlessRow::SetBackgroundColor(::CColor inBackgroundColor)
+{
+ if (m_BackgroundColor != inBackgroundColor) {
+ m_BackgroundColor = inBackgroundColor;
+ Invalidate();
+ }
+}
+
+void CBaseTimebarlessRow::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been selected.
+ */
+void CBaseTimebarlessRow::OnSelect()
+{
+ m_Selected = true;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been deselected.
+ */
+void CBaseTimebarlessRow::OnDeselect()
+{
+ m_Selected = false;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * called when meta data for this row is changed... should be overridden by the
+ * timebar row
+ */
+void CBaseTimebarlessRow::RefreshRowMetaData()
+{
+}
+
+//=============================================================================
+/**
+ * called when a child changes and the keyframes need to be refreshed
+ * @param inDirtyFlag true if this object is now dirty
+ */
+void CBaseTimebarlessRow::SetDirty(bool inDirtyFlag)
+{
+ if (m_DirtyFlag == inDirtyFlag)
+ return;
+
+ m_DirtyFlag = inDirtyFlag;
+ Invalidate();
+}
+
+void CBaseTimebarlessRow::UpdateTime(long inStartTime, long inEndTime)
+{
+ Q_UNUSED(inStartTime);
+ Q_UNUSED(inEndTime);
+}
+
+//=============================================================================
+/**
+ * OnMouseOver event, handles the highlighting of the row.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+void CBaseTimebarlessRow::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ GetBaseStateRow()->OnMouseOver();
+}
+
+//=============================================================================
+/**
+ * OnMouseOut event, handles the de-highlighting of this row.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+void CBaseTimebarlessRow::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ GetBaseStateRow()->OnMouseOut();
+}
+
+//=============================================================================
+/**
+ * OnMouseDown event, handles the selecting of this object.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+bool CBaseTimebarlessRow::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ return CControl::OnMouseDown(inPoint, inFlags);
+#if 0
+ // this addition is causing 4085: Cannot do rubber band selection from sections of timeline that don't contain timebars anymore
+ bool theReturn = CControl::OnMouseDown( inPoint, inFlags );
+ if ( !theReturn )
+ {
+ // Tests if the user has pressed the modifier key, where the intention is to multi-select keyframes.
+ if ( !(inFlags & CHotKeys::MODIFIER_CONTROL ) )
+ {
+ // SK - I changed this to select the row when this is clicked, because I think its a nice feature. ie don't always have to click on the timebar (esp for those e.g. scene without one)
+ // when the modifier key is pressed.
+ GetBaseStateRow( )->Select( false );
+
+ theReturn = true;
+ }
+ }
+ return theReturn;
+#endif
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.h b/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.h
new file mode 100644
index 00000000..9c2fbea4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseTimebarlessRow.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BASE_TIMEBARLESS_ROW_H
+#define INCLUDED_BASE_TIMEBARLESS_ROW_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+
+class CSnapper;
+class CBaseStateRow;
+class ISnappingListProvider;
+
+class CBaseTimebarlessRow : public CControl
+{
+public:
+ CBaseTimebarlessRow();
+ virtual ~CBaseTimebarlessRow();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ virtual void SetBackgroundColor(::CColor inColor);
+ virtual void SetTimeRatio(double inTimeRatio);
+
+ virtual void RefreshRowMetaData();
+
+ virtual void OnSelect();
+ virtual void OnDeselect();
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ virtual void SetDirty(bool inIsDirty);
+ virtual void UpdateTime(long inStartTime, long inEndTime);
+
+ virtual void CommitSelections() = 0;
+ virtual void SelectKeysInRect(CRct inRect, bool inModifierKeyDown) = 0;
+ virtual void SelectAllKeys() = 0;
+ virtual void SelectKeysByTime(long inTime, bool inSelected) = 0;
+ virtual void PopulateSnappingList(CSnapper *inSnappingList) = 0;
+ virtual ISnappingListProvider &GetSnappingListProvider() const = 0;
+
+protected:
+ virtual CBaseStateRow *GetBaseStateRow() const = 0;
+
+ ::CColor m_BackgroundColor;
+ bool m_Selected;
+ bool m_DirtyFlag;
+ double m_TimeRatio;
+};
+#endif // INCLUDED_BASE_TIMEBARLESS_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.cpp b/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.cpp
new file mode 100644
index 00000000..3221f6a1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.cpp
@@ -0,0 +1,685 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "Renderer.h"
+#include "ToggleButton.h"
+#include "BaseStateRow.h"
+#include "StudioPreferences.h"
+#include "TimelineDropTarget.h"
+#include "BaseTimelineTreeControl.h"
+#include "NameEdit.h"
+#include "Bindings/ITimelineItem.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Create a new tree control for the specified state row.
+ * This control contains the toggle button and item name controls.
+ * @param inStateRow the state row of which this belongs to.
+ */
+CBaseTimelineTreeControl::CBaseTimelineTreeControl(CBaseStateRow *inStateRow, bool inMaster)
+ : m_Selected(false)
+ , m_MouseDown(false)
+{
+ m_StateRow = inStateRow;
+
+ m_BackgroundColor = m_StateRow->GetTimebarBackgroundColor(m_StateRow->GetObjectType());
+
+ // Create the expand/collapse button.
+ m_ExpandButton = new CToggleButton();
+ m_ExpandButton->SetUpImage("arrow.png");
+ m_ExpandButton->SetDownImage("arrow_down.png");
+
+ // Add the button and initialize all the listeners for the events on it.
+ AddChild(m_ExpandButton);
+ m_ExpandButton->SigToggle.connect(std::bind(&CBaseStateRow::ToggleExpansion, m_StateRow,
+ std::placeholders::_1, std::placeholders::_2));
+ m_ExpandButton->SetVisible(false);
+
+ m_Icon = new CSIcon(m_StateRow->GetIcon(), m_StateRow->GetDisabledIcon());
+ AddChild(m_Icon);
+
+ // Create and add the name label.
+ m_Text = nullptr; // withdrawn from constructor to delay creation of text object
+
+ // Initialize all the component's positions to 0.
+ SetIndent(CStudioPreferences::GetRowSize());
+
+ SetMinimumSize(CPt(CBaseStateRow::DEFAULT_TOGGLE_LENGTH + m_Icon->GetPosition().x
+ + m_Icon->GetSize().x + 5,
+ CStudioPreferences::GetRowSize()));
+
+ m_TrackingPoint.x = 0;
+ m_TrackingPoint.y = 0;
+ m_DrawAcceptBefore = false;
+ m_DrawAcceptAfter = false;
+
+ // Set up default text colors
+ m_NormalTextColor = CStudioPreferences::GetNormalColor();
+ m_SelectedTextColor = CStudioPreferences::GetNormalColor();
+ if (inMaster) {
+ m_NormalTextColor = CStudioPreferences::GetMasterColor();
+ m_SelectedTextColor = CStudioPreferences::GetMasterColor();
+ }
+ m_LockedTextColor = CStudioPreferences::GetLockedTextColor();
+}
+
+CBaseTimelineTreeControl::~CBaseTimelineTreeControl()
+{
+ delete m_Icon;
+ delete m_ExpandButton;
+ delete m_Text;
+}
+
+//=============================================================================
+/**
+ * Create a new text object. For performance reasons we delay
+ * creating this object until it is needed, i.e. until the row is exposed
+ * by the user and the Draw method is called
+ */
+void CBaseTimelineTreeControl::CreateText()
+{
+ if (!m_Text) {
+ ITimelineItem *theTimelineItem = m_StateRow->GetTimelineItem();
+
+ m_Text = new CNameEdit(theTimelineItem);
+
+ m_Text->SetSize(
+ CPt(CStudioPreferences::GetTimelineNameSize(),
+ CStudioPreferences::GetRowSize() - 3)); /* m_ExpandButton->GetSize( ).y - 3*/
+ // m_Text->SetBGColorNoFocus( CStudioPreferences::GetNormalColor( ) );
+ // if ( theTimelineItem->IsMaster( ) )
+ // m_Text->SetBGColorNoFocus( CStudioPreferences::GetMasterColor( ) );
+ // m_Text->SetFillBackground( false );
+ m_Text->SetBoldText(false);
+
+ // If the object is the scene, you can't edit it's name
+ m_Text->SetEditable(m_StateRow->GetObjectType() != OBJTYPE_SCENE);
+ AddChild(m_Text);
+ m_Text->SetPosition(CPt(m_Icon->GetPosition().x + m_Icon->GetSize().x + 5, 1));
+
+ // This was disabled before Text was created.
+ if (!IsEnabled()) {
+ m_Text->SetEnabled(false);
+ m_Text->SetParentEnabled(false);
+ m_Text->SetTextColor(m_LockedTextColor);
+ } else // since we do delay-creation, "sync" with the parent's selection state
+ UpdateTextSelection();
+
+ // This is so that make the timeline scrollbar scrolls correctly
+ // ( i.e. to the end of the asset name )
+ CPt theSize(GetSize());
+ theSize.x =
+ CBaseStateRow::DEFAULT_TOGGLE_LENGTH + m_Text->GetPosition().x + m_Text->GetSize().x;
+ SetAbsoluteSize(theSize);
+ }
+}
+
+void CBaseTimelineTreeControl::UpdateTextSelection()
+{
+ // since we do delay-creation for the Text only when we have to draw it.. this checks if it is
+ // created first
+ if (m_Text) {
+ if (!IsEnabled())
+ m_Text->SetTextColor(m_LockedTextColor);
+ else
+ m_Text->SetTextColor(m_Selected ? m_SelectedTextColor : m_NormalTextColor);
+ // m_Text->SetFillBackground( m_Selected );
+ // m_Text->SetBoldText( m_Selected );
+ }
+}
+
+//=============================================================================
+/**
+ * Perform the drawing of this control.
+ * @param inRenderer the renderer to draw to.
+ */
+void CBaseTimelineTreeControl::Draw(CRenderer *inRenderer)
+{
+ CreateText(); // the row is now exposed and we can't delay creating the text object any longer
+
+ CRct theRect(GetSize());
+ // Fill in the background
+ if (!m_Selected)
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+ else
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetTimelineSelectColor());
+
+ // if ( m_Text )
+ // m_Text->SetBoldText( m_Selected );
+ // m_Text->SetFillBackground( m_Selected );
+
+ // Draw the line at the bottom of this control
+ inRenderer->PushPen(CStudioPreferences::GetTreeFloorColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Override for the set parent enabled function which tells children the state of the parent
+ */
+void CBaseTimelineTreeControl::SetEnabled(bool inIsEnabled)
+{
+ CControl::SetEnabled(inIsEnabled);
+ if (m_Text) {
+ m_Text->SetEnabled(inIsEnabled);
+ if (!inIsEnabled)
+ m_Text->SetTextColor(m_LockedTextColor);
+ else
+ m_Text->SetTextColor(m_Selected ? m_SelectedTextColor : m_NormalTextColor);
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that something has changed on the asset that this
+ * represents, update it.
+ */
+void CBaseTimelineTreeControl::Refresh(ITimelineItem *inTimelineItem)
+{
+ bool theEnabled = !inTimelineItem->IsLocked();
+ if (m_Text) {
+ // Make sure the color is correct depending on if its is a master object
+ if (m_NormalTextColor != CStudioPreferences::GetMasterColor()
+ && inTimelineItem->IsMaster()) {
+ m_NormalTextColor = CStudioPreferences::GetMasterColor();
+ m_Text->SetBGColorNoFocus(CStudioPreferences::GetMasterColor());
+ if (!m_Selected)
+ m_Text->SetTextColor(m_NormalTextColor);
+ }
+
+ m_Text->SetData(inTimelineItem->GetName());
+ }
+ m_Icon->SetImage((theEnabled) ? m_StateRow->GetIcon() : m_StateRow->GetDisabledIcon());
+ SetEnabled(theEnabled);
+}
+
+//=============================================================================
+/**
+ * Set the indent of this control.
+ * The indent gives the semblance of a tree control, and causes the toggle
+ * name and icon to be pushed in some.
+ * @param inIndent the indent for this control.
+ */
+void CBaseTimelineTreeControl::SetIndent(long inIndent)
+{
+ m_Indent = inIndent;
+
+ // Set the new position for all the children.
+ m_ExpandButton->SetPosition(CPt(inIndent, 0));
+
+ m_Icon->SetPosition(CPt(m_ExpandButton->GetPosition().x + m_ExpandButton->GetSize().x, 0));
+ if (m_Text)
+ m_Text->SetPosition(CPt(m_Icon->GetPosition().x + m_Icon->GetSize().x + 5, 1));
+}
+
+//=============================================================================
+/**
+ * Get the current indent of this control.
+ */
+long CBaseTimelineTreeControl::GetIndent()
+{
+ return m_Indent;
+}
+
+//=============================================================================
+/**
+ * Set whether or not to have the toggle control visible.
+ * The toggle is turned off by the state row when there are no visible children.
+ * @param inIsToggleVisible false if the toggle is not to be visible.
+ */
+void CBaseTimelineTreeControl::SetToggleVisible(bool inIsToggleVisible)
+{
+ m_ExpandButton->SetVisible(inIsToggleVisible);
+}
+
+//=============================================================================
+/**
+ * Set whether or not this control is expanded.
+ * This is used to set the state of the expand button.
+ */
+void CBaseTimelineTreeControl::SetExpanded(bool inIsExpanded)
+{
+ m_ExpandButton->SetToggleState(inIsExpanded);
+}
+
+//=============================================================================
+/**
+ * Set the current background color for this control.
+ * The background color changes when the control gets a mouse over/mouse out.
+ */
+void CBaseTimelineTreeControl::SetBackgroundColor(CColor inColor)
+{
+ if (m_BackgroundColor == inColor)
+ return;
+
+ m_BackgroundColor = inColor;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Notify the row that a mouse out occurred.
+ * The row will in turn turn off the highlighting.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CBaseTimelineTreeControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_DrawAcceptAfter = false;
+ m_DrawAcceptBefore = false;
+
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ m_StateRow->OnMouseOut();
+
+ if (m_TimerHandler) {
+
+ // nullptr out our handle so we can create a new one.
+ m_TimerHandler = std::shared_ptr<UICDM::ISignalConnection>();
+ }
+
+ AcceptDropAfter(false);
+ AcceptDropBefore(false);
+}
+
+//=============================================================================
+/**
+ * Notify the row that a mouse over occurred.
+ * The row will in turn turn on the highlighting.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CBaseTimelineTreeControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ m_StateRow->OnMouseOver();
+}
+
+//=============================================================================
+/**
+ * Pass the double click notification on to the row and have it process it.
+ * The row will do object-specific actions on doubleclicks.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ * @return true stating that the event was processed.
+ */
+bool CBaseTimelineTreeControl::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDoubleClick(inPoint, inFlags)) {
+ m_StateRow->OnMouseDoubleClick(inPoint, inFlags);
+ GrabFocus(nullptr);
+ }
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles mouse down on the this control. Flags the button as down which results
+ * in some possible drawing changes.
+ * @param inPoint location of the mouse when event occurred
+ * @param inFlags state of modifier keys when event occurred
+ * @return true
+ */
+bool CBaseTimelineTreeControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ SBaseStateRowSelectionKeyState theKeyState;
+ if ((CHotKeys::MODIFIER_SHIFT & inFlags) == CHotKeys::MODIFIER_SHIFT)
+ theKeyState.SetShiftDown();
+ if ((CHotKeys::MODIFIER_CONTROL & inFlags) == CHotKeys::MODIFIER_CONTROL)
+ theKeyState.SetControlDown();
+ m_StateRow->Select(theKeyState);
+
+ // Always track where the mouse is.
+ m_MouseDown = true;
+
+ Invalidate();
+ }
+
+ return true;
+}
+
+bool CBaseTimelineTreeControl::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseRDown(inPoint, inFlags))
+ m_StateRow->OnMouseRDown(inPoint, inFlags);
+
+ return true;
+}
+
+void CBaseTimelineTreeControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_MouseDown = false;
+
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ AcceptDropAfter(false);
+ AcceptDropBefore(false);
+}
+
+//=============================================================================
+/**
+ * This method handles the keydown event for a StateTreeControl. It calls
+ * CControl::OnKeyDown method to make sure that the keydown event is handled
+ * by its children. If the keydown event is not handled and F2 is down, it
+ * enables text edit mode.
+ * @param inChar is the char pressed
+ * @param inFlags state of modifier keys when event occurred
+ * @return if the key was handled
+ */
+bool CBaseTimelineTreeControl::OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags)
+{
+ bool theKeyWasHandled = CControl::OnKeyDown(inChar, inFlags);
+
+ if (!theKeyWasHandled && (inChar == Qt::Key_F2)) {
+ DoRename();
+ theKeyWasHandled = true;
+ }
+
+ return theKeyWasHandled;
+}
+
+//=============================================================================
+/**
+ * This is so the Gesture can this object to get something ready to Drag.
+ */
+void CBaseTimelineTreeControl::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ if (m_MouseDown /*&& inFlags & MOUSE_LBUTTON*/) {
+ long theDeltaX = inPoint.x - m_TrackingPoint.x;
+ long theDeltaY = inPoint.y - m_TrackingPoint.y;
+
+ if (::abs(theDeltaX) > 3 || ::abs(theDeltaY) > 3) {
+ m_TrackingPoint = inPoint;
+
+ m_StateRow->DoStartDrag(GetWindowListener());
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the state that this is displaying has been selected.
+ */
+void CBaseTimelineTreeControl::OnSelect()
+{
+ m_Selected = true;
+
+ UpdateTextSelection();
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Notification that the state that this is displaying has been deselected.
+ */
+void CBaseTimelineTreeControl::OnDeselect()
+{
+ m_Selected = false;
+
+ UpdateTextSelection();
+ Invalidate();
+}
+
+void CBaseTimelineTreeControl::GrabTextFocus()
+{
+ GrabFocus(m_Text);
+}
+
+//=============================================================================
+/**
+ * To enable F2 editing.
+ */
+void CBaseTimelineTreeControl::OnGainFocus()
+{
+ CControl::OnGainFocus();
+ GrabFocus(m_Text);
+}
+
+//=============================================================================
+/**
+ * Called when this control loses focus. Overridden because we need to set the
+ * text color depending on whether or not the asset for this row is still
+ * selected.
+ */
+void CBaseTimelineTreeControl::OnLoseFocus()
+{
+ CControl::OnLoseFocus();
+
+ if (m_Text) {
+ if (m_Selected) {
+ m_Text->SetTextColor(m_SelectedTextColor);
+ } else // If this asset is no longer selected
+ {
+ // If the row is enabled, use the normal text color
+ if (m_Text->IsEnabled()) {
+ m_Text->SetTextColor(m_NormalTextColor);
+ }
+ // Otherwise use the locked text color
+ else {
+ m_Text->SetTextColor(m_LockedTextColor);
+ }
+ }
+ }
+
+ AcceptDropAfter(false);
+ AcceptDropBefore(false);
+}
+
+//=============================================================================
+/**
+ * If the name is changed, the size has to be adjusted accordingly.
+ */
+void CBaseTimelineTreeControl::OnChildSizeChanged(CControl *inChild)
+{
+ CControl::OnChildSizeChanged(inChild);
+
+ if (inChild == m_Text) { // This is so that make the timeline scrollbar scrolls correctly
+ // ( i.e. to the end of the asset name )
+ CPt theSize(GetSize());
+ theSize.x =
+ CBaseStateRow::DEFAULT_TOGGLE_LENGTH + m_Text->GetPosition().x + m_Text->GetSize().x;
+ SetAbsoluteSize(theSize);
+ }
+}
+
+//=============================================================================
+/**
+ * This will do a vertical hit test on this control.
+ * This need to figure out if the point is toward teh top or toward the bottom, or on this
+ *control.
+ * @param inMousePoint the point where the dropp wants to occure.
+ * @return An enumeration representing the location of the potential drop.
+ */
+CBaseTimelineTreeControl::ECONTROLREGION CBaseTimelineTreeControl::FindHitRegion(CPt &inMousePoint)
+{
+ // Default Region is "on"
+ CBaseTimelineTreeControl::ECONTROLREGION theDropRegion =
+ CBaseTimelineTreeControl::ECONTROLREGION_ON;
+
+ CPt theSize = GetSize();
+ long theTop = 0;
+ long theBottom = theSize.y - 1;
+ long thePointY = inMousePoint.y;
+
+ // check if we are in the upper part of the control
+ if ((thePointY >= theTop) && (thePointY <= (theTop + 3))) {
+ theDropRegion = CBaseTimelineTreeControl::ECONTROLREGION_ABOVE;
+ }
+ // check if we are in the lower part of the control
+ else if ((thePointY <= (theBottom)) && (thePointY >= (theBottom - 3))) {
+ theDropRegion = CBaseTimelineTreeControl::ECONTROLREGION_BELOW;
+ }
+
+ return theDropRegion;
+}
+
+//=============================================================================
+/**
+ * Find an object under the point.
+ * If tht point is close to the top or the bottom of the control,
+ * then the Asset to use would be the parent of the current asset.
+ * @param inMousePoint the point where the Drop wants to occure.
+ */
+CDropTarget *CBaseTimelineTreeControl::BuildDropTarget(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ // This will do the hit testing to see where we are with the point.
+ ECONTROLREGION theRegion = FindHitRegion(inMousePoint);
+
+ // Make a new DropTarget to return.
+ CTimeLineDropTarget *theTarget = new CTimeLineDropTarget();
+
+ EDROPDESTINATION theDropDest = EDROPDESTINATION_ON;
+
+ switch (theRegion) {
+ case ECONTROLREGION_BELOW:
+ theDropDest = EDROPDESTINATION_BELOW;
+
+ AcceptDropAfter(true);
+ AcceptDropBefore(false);
+ break;
+
+ case ECONTROLREGION_ABOVE:
+ theDropDest = EDROPDESTINATION_ABOVE;
+
+ AcceptDropAfter(false);
+ AcceptDropBefore(true);
+ break;
+
+ case ECONTROLREGION_ON:
+
+ AcceptDropAfter(false);
+ AcceptDropBefore(false);
+ break;
+ }
+ theTarget->SetDestination(theDropDest);
+ // For insertion markers
+ theTarget->SetInsertionMarkerRow(this);
+ theTarget->SetInsertionMarkerIndent(m_Icon->GetPosition().x);
+
+ // connect the data portion of the drag&drop action
+ m_StateRow->SetDropTarget(theTarget);
+
+ return theTarget;
+}
+
+//=============================================================================
+/**
+ * This function is overriden from the CControl class.
+ * It will find an Asset that can be dropped upon. Also it will
+ * figureout if the DropTarget should contain this Asset or the parent
+ * of this Asset.
+ * @param inMousePoint the coords [in local space] of the drop action.
+ * @param inFlags the Modifier flags for the keyboard state.
+ * @return the found DropTarget or null if not found.
+ */
+CDropTarget *CBaseTimelineTreeControl::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ // Make sure the Mouse Highlighting happens.
+ m_StateRow->OnMouseOver();
+
+ // This will do all of the work.
+ CDropTarget *theReturnTarget = BuildDropTarget(inMousePoint, inFlags);
+
+ // Expand the object [ once again ask CE for an explaination of this ]
+ if (!m_TimerHandler && m_ExpandButton->IsVisible() && m_ExpandButton->IsEnabled()) {
+ if (!m_DrawAcceptBefore && !m_DrawAcceptAfter)
+ m_TimerHandler = Q3DStudio::ITickTock::GetInstance().AddTimer(
+ 1000, false, std::bind(&CBaseTimelineTreeControl::OnTimer, this),
+ "CStateTreeControl::FindDropCandidate::" + GetName());
+ }
+
+ // we always return true, since we should be the only one to handle it.
+ return theReturnTarget;
+}
+
+//=============================================================================
+/**
+ * Notification that the Hover Time has expired,
+ */
+void CBaseTimelineTreeControl::OnTimer()
+{
+ // Expand the Row to show the children.
+ m_StateRow->Expand();
+}
+
+//=============================================================================
+/**
+ * This will set the Flag so we can Draw the Bottom Line.
+ * @param inAccept true to draw the line false otherwise.
+ */
+void CBaseTimelineTreeControl::AcceptDropAfter(bool inAccept)
+{
+ if (inAccept != m_DrawAcceptAfter) {
+ m_DrawAcceptAfter = inAccept;
+ }
+}
+
+//=============================================================================
+/**
+ * This will set the Flag so we can Draw the Top Line.
+ * @param inAccept true to draw the line false otherwise.
+ */
+void CBaseTimelineTreeControl::AcceptDropBefore(bool inAccept)
+{
+ if (inAccept != m_DrawAcceptBefore) {
+ m_DrawAcceptBefore = inAccept;
+ }
+}
+
+//=============================================================================
+/**
+ * Called by the state context menu to do the renaming portion of the menu
+ */
+void CBaseTimelineTreeControl::DoRename()
+{
+ CreateText();
+ m_Text->SetEditMode(true);
+ GrabFocus(m_Text);
+ m_Text->SelectAllText();
+}
+
+void CBaseTimelineTreeControl::SetNameReadOnly(bool inReadOnly)
+{
+ CreateText(); // Create the text if it's not ready.
+ m_Text->SetEditable(!inReadOnly);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.h b/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.h
new file mode 100644
index 00000000..e348cccf
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseTimelineTreeControl.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BASE_TIMELINE_TREE_CONTROL_H
+#define INCLUDED_BASE_TIMELINE_TREE_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "SIcon.h"
+#include "ToggleButton.h"
+#include "ITickTock.h"
+
+class CButtonControl;
+class CBaseStateRow;
+class CDropTarget;
+class CNameEdit;
+class CTickTockProc;
+struct STickTockHandle;
+class CPt;
+class CToggleButton;
+class ITimelineItem;
+
+class CBaseTimelineTreeControl : public CControl
+{
+
+public:
+ enum ECONTROLREGION { ECONTROLREGION_ON, ECONTROLREGION_ABOVE, ECONTROLREGION_BELOW };
+
+ CBaseTimelineTreeControl(CBaseStateRow *inStateRow, bool inMaster);
+ virtual ~CBaseTimelineTreeControl();
+
+ void Draw(CRenderer *inRenderer) override;
+ void OnChildSizeChanged(CControl *inChild) override;
+
+ void SetIndent(long inIndent);
+ long GetIndent();
+
+ void SetExpanded(bool inIsExpanded);
+
+ void SetToggleVisible(bool inIsToggleVisible);
+ void OnSelect();
+ void OnDeselect();
+ void GrabTextFocus();
+ void OnGainFocus() override;
+ void OnLoseFocus() override;
+ void SetBackgroundColor(::CColor inBackgroundColor);
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnKeyDown(unsigned int inChar, Qt::KeyboardModifiers inFlags) override;
+
+ void OnTimer();
+
+ void Refresh(ITimelineItem *inTimelineItem);
+
+ void SetEnabled(bool inIsEnabled) override;
+
+ CDropTarget *BuildDropTarget(CPt &inMousePoint, Qt::KeyboardModifiers inFlags);
+ CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags) override;
+ void AcceptDropAfter(bool inAccept);
+ void AcceptDropBefore(bool inAccept);
+ void DoRename();
+
+ void SetNameReadOnly(bool inReadOnly);
+
+protected:
+ ECONTROLREGION FindHitRegion(CPt &inMousePoint);
+ void CreateText(); // delay text creation until row is exposed
+ void UpdateTextSelection();
+
+ long m_Indent;
+
+ CBaseStateRow *m_StateRow;
+
+ CToggleButton *m_ExpandButton;
+ CNameEdit *m_Text;
+
+ CSIcon *m_Icon;
+ bool m_Selected;
+
+ ::CColor m_BackgroundColor;
+
+ CPt m_TrackingPoint;
+ CPt m_MouseMovePoint;
+ bool m_MouseDown;
+ bool m_DrawAcceptBefore;
+ bool m_DrawAcceptAfter;
+ std::shared_ptr<UICDM::ISignalConnection> m_TimerHandler;
+ ::CColor m_NormalTextColor;
+ ::CColor m_SelectedTextColor;
+ ::CColor m_LockedTextColor;
+};
+#endif // INCLUDED_BASE_TIMELINE_TREE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.cpp
new file mode 100644
index 00000000..196d345d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BehaviorTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "StudioApp.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "Doc.h"
+
+using namespace UICDM;
+
+CBehaviorTimelineItemBinding::CBehaviorTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+}
+
+EStudioObjectType CBehaviorTimelineItemBinding::GetObjectType() const
+{
+ return OBJTYPE_BEHAVIOR;
+}
+
+//=============================================================================
+/**
+ * Open the associated item as though it was double-clicked in explorer
+ */
+bool CBehaviorTimelineItemBinding::OpenAssociatedEditor()
+{
+ return OpenSourcePathFile();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.h
new file mode 100644
index 00000000..b75d100c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/BehaviorTimelineItemBinding.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_BEHAVIOR_TIMELINEITEM_BINDING_H
+#define INCLUDED_BEHAVIOR_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Behavior type
+ */
+class CBehaviorTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public:
+ CBehaviorTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ ~CBehaviorTimelineItemBinding() {}
+
+ // CUICDMTimelineItemBinding
+ EStudioObjectType GetObjectType() const override;
+ bool OpenAssociatedEditor() override;
+};
+
+#endif // INCLUDED_BEHAVIOR_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.cpp
new file mode 100644
index 00000000..7534fa73
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "EmptyTimelineTimebar.h"
+#include "StudioPreferences.h"
+
+CEmptyTimelineTimebar::CEmptyTimelineTimebar()
+{
+}
+
+CEmptyTimelineTimebar::~CEmptyTimelineTimebar()
+{
+}
+
+long CEmptyTimelineTimebar::GetStartTime() const
+{
+ return 0;
+}
+
+long CEmptyTimelineTimebar::GetEndTime() const
+{
+ return 0;
+}
+
+long CEmptyTimelineTimebar::GetDuration() const
+{
+ return 0;
+}
+
+bool CEmptyTimelineTimebar::ShowHandleBars() const
+{ // makes no sense to show handle bars, when this does not have start/end times.
+ return false;
+}
+
+void CEmptyTimelineTimebar::OnBeginDrag()
+{
+}
+
+void CEmptyTimelineTimebar::OffsetTime(long inDiff)
+{
+ Q_UNUSED(inDiff);
+}
+
+void CEmptyTimelineTimebar::ChangeTime(long inTime, bool inSetStart)
+{
+ Q_UNUSED(inTime);
+ Q_UNUSED(inSetStart);
+}
+
+void CEmptyTimelineTimebar::CommitTimeChange()
+{
+}
+
+void CEmptyTimelineTimebar::RollbackTimeChange()
+{
+}
+
+::CColor CEmptyTimelineTimebar::GetTimebarColor()
+{
+ return CStudioPreferences::GetObjectTimebarColor();
+}
+
+void CEmptyTimelineTimebar::SetTimebarColor(const ::CColor &inColor)
+{
+ Q_UNUSED(inColor);
+}
+
+Q3DStudio::CString CEmptyTimelineTimebar::GetTimebarComment()
+{
+ return "";
+}
+
+void CEmptyTimelineTimebar::SetTimebarComment(const Q3DStudio::CString &inComment)
+{
+ Q_UNUSED(inComment);
+}
+
+void CEmptyTimelineTimebar::SetTimebarTime(ITimeChangeCallback *inCallback /*= nullptr*/)
+{
+ Q_UNUSED(inCallback);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.h
new file mode 100644
index 00000000..9aacb524
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/EmptyTimelineTimebar.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#pragma once
+
+#include "ITimelineTimebar.h"
+
+//=============================================================================
+/**
+ * The current timeline UI design is such that even when no timebar shows up ( with the exception of
+ * the top row, ie the time context )
+ * there is a timebar control to store the keyframes for the animated properties.
+ * Hence, instead of return nullptr for GetTimebar for ITimelineItem, this class will ensure the UI
+ * classes still work.
+ */
+class CEmptyTimelineTimebar : public ITimelineTimebar
+{
+public:
+ CEmptyTimelineTimebar();
+ virtual ~CEmptyTimelineTimebar();
+
+ // ITimelineTimebar
+ long GetStartTime() const override;
+ long GetEndTime() const override;
+ long GetDuration() const override;
+ bool ShowHandleBars() const override;
+ void OnBeginDrag() override;
+ void OffsetTime(long inDiff) override;
+ void ChangeTime(long inTime, bool inSetStart) override;
+ void CommitTimeChange() override;
+ void RollbackTimeChange() override;
+ ::CColor GetTimebarColor() override;
+ void SetTimebarColor(const ::CColor &inColor) override;
+ Q3DStudio::CString GetTimebarComment() override;
+ void SetTimebarComment(const Q3DStudio::CString &inComment) override;
+ void SetTimebarTime(ITimeChangeCallback *inCallback = nullptr) override;
+};
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.cpp
new file mode 100644
index 00000000..322cbd33
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "GroupTimelineItemBinding.h"
+#include "BaseStateRow.h"
+#include "TimelineTranslationManager.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Dialogs.h"
+
+// Data model specific
+#include "Doc.h"
+#include "CmdGeneric.h"
+
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMSlides.h"
+#include "UICDMDataCore.h"
+#include "UICFileTools.h"
+
+using namespace UICDM;
+
+CGroupTimelineItemBinding::CGroupTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+}
+
+//=============================================================================
+/**
+ * Ideally we like to be able to edit the component in a different editor ( we've been hoping for
+ * that feature ) BUT we don't have that,
+ * and it has always been we 'dive' into the component within Studio.
+ */
+bool CGroupTimelineItemBinding::OpenAssociatedEditor()
+{
+ if (GetObjectType() == OBJTYPE_COMPONENT) {
+ ISlideSystem *theSlideSystem = m_StudioSystem->GetSlideSystem();
+
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ Q3DStudio::CId theId = m_StudioSystem->GetClientDataModelBridge()->GetGUID(theInstance);
+ UICDM::CUICDMSlideHandle theMasterSlide =
+ theSlideSystem->GetMasterSlideByComponentGuid(GuidtoSLong4(theId));
+
+ if (theMasterSlide.Valid()) {
+ CUICDMSlideHandle theActiveSlide = theSlideSystem->GetActiveSlide(theMasterSlide);
+
+ CCmd *theCmd = new CCmdGeneric<CDoc, CUICDMSlideHandle>(
+ m_TransMgr->GetDoc(), &CDoc::NotifyActiveSlideChanged,
+ &CDoc::NotifyActiveSlideChanged, theActiveSlide, NULL, "");
+ theCmd->SetUndoable(false);
+ theCmd->SetModifiedFlag(false);
+ m_TransMgr->GetDoc()->GetCore()->ExecuteCommand(theCmd, false);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool CGroupTimelineItemBinding::IsImported() const
+{
+
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetDoc()->GetStudioSystem()->GetPropertySystem();
+ UICDM::SValue theValue;
+ if (thePropertySystem->GetInstancePropertyValue(theInstance, m_TransMgr->GetDoc()
+ ->GetStudioSystem()
+ ->GetClientDataModelBridge()
+ ->GetSourcePathProperty(),
+ theValue)) {
+ UICDM::TDataStrPtr theSrcPath(UICDM::get<UICDM::TDataStrPtr>(theValue));
+ Q3DStudio::CFilePath theFilePath(theSrcPath->GetData());
+ if (theFilePath.GetExtension() == CDialogs::GetWideImportFileExtension())
+ return true;
+ }
+ // If it is, check to be sure that
+ // we can get to the import file.
+ // If we can, then we are imported.
+ return false;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.h
new file mode 100644
index 00000000..a9462c1c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/GroupTimelineItemBinding.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_GROUP_TIMELINEITEM_BINDING_H
+#define INCLUDED_GROUP_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class ITimelineItem;
+class CTimelineTranslationManager;
+class CBaseStateRow;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Group type
+ */
+class CGroupTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public:
+ CGroupTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ ~CGroupTimelineItemBinding() {}
+
+ // CUICDMTimelineItemBinding
+ bool OpenAssociatedEditor() override;
+ bool IsImported() const override;
+};
+
+#endif // INCLUDED_GROUP_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/IKeyframeSelector.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/IKeyframeSelector.h
new file mode 100644
index 00000000..db8b551f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/IKeyframeSelector.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_IKEYFRAME_SELECTOR_H
+#define INCLUDED_IKEYFRAME_SELECTOR_H 1
+
+#pragma once
+
+//=============================================================================
+/**
+ * Interface that performs keyframe selection.
+ */
+//=============================================================================
+class IKeyframeSelector
+{
+public:
+ virtual ~IKeyframeSelector() {}
+
+ //=============================================================================
+ /**
+ * @param inTime -1 to selected (or deselect) ALL keyframes, otherwise only by time.
+ */
+ virtual void SelectKeyframes(bool inSelected, long inTime = -1) = 0;
+};
+
+#endif // INCLUDED_IKEYFRAME_SELECTOR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItem.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItem.h
new file mode 100644
index 00000000..e5af8671
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItem.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_ITIMELINE_ITEM_H
+#define INCLUDED_ITIMELINE_ITEM_H 1
+
+#pragma once
+
+#include "INamable.h"
+#include "StudioObjectTypes.h"
+
+class ITimelineTimebar;
+
+//=============================================================================
+/**
+ * Abstraction of a data model item in the Scene. This might end up deriving from a more generic
+ * interface, so that common
+ * functions can be generalized for items in the different palettes.
+ */
+//=============================================================================
+class ITimelineItem : public INamable
+{
+public:
+ virtual ~ITimelineItem() {}
+
+ virtual EStudioObjectType GetObjectType() const = 0;
+ virtual bool IsMaster() const = 0;
+
+ virtual bool IsShy() const = 0;
+ virtual void SetShy(bool) = 0;
+ virtual bool IsLocked() const = 0;
+ virtual void SetLocked(bool) = 0;
+ virtual bool IsVisible() const = 0;
+ virtual void SetVisible(bool) = 0;
+ virtual bool IsExpanded() const = 0;
+ virtual void SetExpanded(bool) = 0;
+ virtual bool IsImported() const { return false; }
+
+ // Actions
+ virtual bool HasAction(bool inMaster) = 0;
+ virtual bool ChildrenHasAction(bool inMaster) = 0;
+ virtual bool ComponentHasAction(bool inMaster) = 0;
+
+ virtual ITimelineTimebar *GetTimebar() = 0;
+};
+
+#endif // INCLUDED_ITIMELINE_ITEM_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h
new file mode 100644
index 00000000..d251f530
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INCLUDED_ITIMELINE_ITEM_BINDINGS_H
+#define INCLUDED_ITIMELINE_ITEM_BINDINGS_H 1
+
+#pragma once
+
+#include "ITimelineItem.h"
+#include "ITimelineItemProperty.h"
+#include "IKeyframeSelector.h"
+#include "SIterator.h"
+
+class CBaseStateRow;
+class CControlWindowListener;
+class ITimelineKeyframesManager;
+
+// Data model specific ??
+class CDropTarget;
+
+class ITimelineItemKeyframesHolder
+{
+public:
+ virtual ~ITimelineItemKeyframesHolder() {}
+
+ virtual void InsertKeyframe() = 0;
+ virtual void DeleteAllChannelKeyframes() = 0;
+ virtual long GetKeyframeCount() const = 0;
+ virtual IKeyframe *GetKeyframeByTime(long inTime) const = 0;
+ virtual IKeyframe *GetKeyframeByIndex(long inIndex) const = 0;
+ virtual long OffsetSelectedKeyframes(long inOffset) = 0;
+ virtual void CommitChangedKeyframes() = 0;
+ virtual void OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation) = 0;
+};
+
+//=============================================================================
+/**
+ * Interface to encapsulate data model specific functions, that Timeline UI objects can talk to.
+ */
+//=============================================================================
+class ITimelineItemBinding : public ITimelineItemKeyframesHolder, public IKeyframeSelector
+{
+public:
+ // List of possible transactions that requires querying the data model if they are valid
+ enum EUserTransaction {
+ EUserTransaction_None,
+ EUserTransaction_Rename,
+ EUserTransaction_Duplicate,
+ EUserTransaction_Cut,
+ EUserTransaction_Copy,
+ EUserTransaction_Paste,
+ EUserTransaction_Delete,
+ EUserTransaction_MakeComponent,
+ EUserTransaction_EditComponent,
+ };
+
+public:
+ virtual ~ITimelineItemBinding() {}
+
+ virtual ITimelineItem *GetTimelineItem() = 0;
+ virtual CBaseStateRow *GetRow() = 0;
+
+ // Events
+ virtual void SetSelected(bool multiSelect) = 0;
+ virtual void OnCollapsed() = 0;
+ virtual void ClearKeySelection() = 0;
+ virtual bool OpenAssociatedEditor() = 0;
+ virtual void DoStartDrag(CControlWindowListener *inWndListener) = 0;
+ virtual void SetDropTarget(CDropTarget *inTarget) = 0;
+
+ // Hierarchy
+ virtual long GetChildrenCount() = 0;
+ virtual ITimelineItemBinding *GetChild(long inIndex) = 0;
+ virtual ITimelineItemBinding *GetParent() = 0;
+ virtual void SetParent(ITimelineItemBinding *parent) = 0;
+ // Properties
+ virtual long GetPropertyCount() = 0;
+ virtual ITimelineItemProperty *GetProperty(long inIndex) = 0;
+
+ // Eye/Lock toggles
+ virtual bool ShowToggleControls() const = 0;
+ virtual bool IsLockedEnabled() const = 0;
+ virtual bool IsVisibleEnabled() const = 0;
+
+ // Init/Cleanup
+ virtual void Bind(CBaseStateRow *inRow) = 0;
+ virtual void Release() = 0;
+
+ // ContextMenu
+ virtual bool IsValidTransaction(EUserTransaction inTransaction) = 0;
+ virtual void PerformTransaction(EUserTransaction inTransaction) = 0;
+ virtual Q3DStudio::CString GetObjectPath() = 0;
+
+ virtual bool IsExternalizeable() { return false; }
+ virtual void Externalize() {}
+ virtual bool IsInternalizeable() { return false; }
+ virtual void Internalize() {}
+
+ // Selected keyframes
+ virtual ITimelineKeyframesManager *GetKeyframesManager() const = 0;
+
+ // Properties
+ virtual void RemoveProperty(ITimelineItemProperty *inProperty) = 0;
+ virtual void LoadProperties() = 0;
+};
+
+//=============================================================================
+/**
+ * Helper iterator class that iterates over a ITimeline's children in a ordered (priority) list.
+ */
+//=============================================================================
+class CTimelineItemOrderedIterator : public CSIterator<ITimelineItemBinding *>
+{
+public:
+ CTimelineItemOrderedIterator(ITimelineItemBinding *inRootTimelineItem)
+ {
+ m_RootTimelineItem = inRootTimelineItem;
+ Reset();
+ }
+ bool IsDone() override { return (m_Index >= m_Total); }
+ void operator++() override { m_Index++; }
+ void operator+=(const long inNumToInc) override { m_Index += inNumToInc; }
+ ITimelineItemBinding *GetCurrent() override { return m_RootTimelineItem->GetChild(m_Index); }
+ virtual void Reset()
+ {
+ m_Index = 0;
+ m_Total = m_RootTimelineItem->GetChildrenCount();
+ }
+
+protected:
+ ITimelineItemBinding *m_RootTimelineItem;
+ long m_Index;
+ long m_Total;
+};
+
+//=============================================================================
+/**
+ * Helper iterator class that iterates over a ITimeline's properties
+ */
+//=============================================================================
+class CTimelineItemPropertyIterator : public CSIterator<ITimelineItemProperty *>
+{
+public:
+ CTimelineItemPropertyIterator(ITimelineItemBinding *inTimelineItem)
+ {
+ m_TimelineItem = inTimelineItem;
+ Reset();
+ }
+ bool IsDone() override { return (m_Index >= m_Total); }
+ void operator++() override { m_Index++; }
+ void operator+=(const long inNumToInc) override { m_Index += inNumToInc; }
+ ITimelineItemProperty *GetCurrent() override { return m_TimelineItem->GetProperty(m_Index); }
+ virtual void Reset()
+ {
+ m_Index = 0;
+ m_Total = m_TimelineItem->GetPropertyCount();
+ }
+
+protected:
+ ITimelineItemBinding *m_TimelineItem;
+ long m_Index;
+ long m_Total;
+};
+
+#endif // INCLUDED_ITIMELINE_ITEM_BINDINGS_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemProperty.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemProperty.h
new file mode 100644
index 00000000..c241ebfa
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemProperty.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_ITIMELINE_ITEM_PROPERTY_H
+#define INCLUDED_ITIMELINE_ITEM_PROPERTY_H 1
+
+#pragma once
+
+#include "IKeyframeSelector.h"
+#include "UICDMMetaData.h"
+#include "UICString.h"
+
+class CPropertyRow;
+class IKeyframe;
+class ITimelineKeyframesManager;
+
+//=============================================================================
+/**
+ * Abstraction of a data model item's property that is displayed in the Timeline.
+ */
+//=============================================================================
+class ITimelineItemProperty : public IKeyframeSelector
+{
+public:
+ virtual ~ITimelineItemProperty() {}
+
+ virtual Q3DStudio::CString GetName() const = 0;
+ virtual bool IsMaster() const = 0;
+ virtual UICDM::TDataTypePair GetType() const = 0;
+ virtual float GetMaximumValue() const = 0;
+ virtual float GetMinimumValue() const = 0;
+
+ virtual void SetSelected() = 0;
+ virtual void ClearKeySelection() = 0;
+ virtual void DeleteAllKeys() = 0;
+
+ virtual void Bind(CPropertyRow *inRow) = 0;
+ virtual void Release() = 0;
+ virtual CPropertyRow *GetRow() = 0;
+
+ // Keyframes
+ virtual ITimelineKeyframesManager *GetKeyframesManager() const = 0;
+ virtual IKeyframe *GetKeyframeByTime(long inTime) const = 0;
+ virtual IKeyframe *GetKeyframeByIndex(long inIndex) const = 0;
+ virtual long GetKeyframeCount() const = 0;
+ virtual long GetChannelCount() const = 0;
+ virtual float GetChannelValueAtTime(long inChannelIndex, long inTime) = 0;
+ virtual void SetChannelValueAtTime(long inChannelIndex, long inTime, float inValue) = 0;
+ virtual long OffsetSelectedKeyframes(long inOffset) = 0;
+ virtual void CommitChangedKeyframes() = 0;
+ virtual void OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation) = 0;
+ virtual bool IsDynamicAnimation() = 0;
+};
+
+#endif // INCLUDED_ITIMELINE_ITEM_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h
new file mode 100644
index 00000000..e1a6914f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_ITIMELINE_KEYFRAMES_MANAGER_H
+#define INCLUDED_ITIMELINE_KEYFRAMES_MANAGER_H 1
+
+#pragma once
+
+#include "IKeyframesManager.h"
+
+//=============================================================================
+/**
+ * Interface to manage keyframes related actions in the Timeline
+ */
+//=============================================================================
+class ITimelineKeyframesManager : public IKeyframesManager
+{
+public:
+ virtual ~ITimelineKeyframesManager() {}
+
+ virtual void SetKeyframeTime(long inTime) = 0;
+ virtual void SetKeyframesDynamic(bool inDynamic) = 0;
+ virtual long OffsetSelectedKeyframes(long inOffset) = 0;
+ virtual bool CanMakeSelectedKeyframesDynamic() = 0;
+ virtual void CommitChangedKeyframes() = 0;
+ virtual void RollbackChangedKeyframes() = 0;
+};
+
+#endif // INCLUDED_IKEYFRAMES_MANAGER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineTimebar.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineTimebar.h
new file mode 100644
index 00000000..dbeefb85
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineTimebar.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INCLUDED_ITIMELINE_TIMEBAR_H
+#define INCLUDED_ITIMELINE_TIMEBAR_H 1
+
+#pragma once
+
+#include "UICString.h"
+
+class ITimeChangeCallback;
+
+class CColor;
+
+//=============================================================================
+/**
+ * Interface for a Timebar
+ */
+//=============================================================================
+class ITimelineTimebar
+{
+public:
+ virtual ~ITimelineTimebar() {}
+
+ virtual long GetStartTime() const = 0;
+ virtual long GetEndTime() const = 0;
+ virtual long GetDuration() const = 0;
+ virtual bool ShowHandleBars() const = 0;
+ //=============================================================================
+ /**
+ * TODO: consider refactor. drag&drop specfics should not be in the Data Model.
+ */
+ virtual void OnBeginDrag() = 0;
+ //
+ virtual void OffsetTime(long inDiff) = 0;
+ // Change the start time or the end time of the timebar. inTime: time to change to, inSetStart:
+ // true to set start time, false to set end time.
+ virtual void ChangeTime(long inTime, bool inSetStart) = 0;
+ virtual void CommitTimeChange() = 0;
+ virtual void RollbackTimeChange() = 0;
+ //
+ virtual CColor GetTimebarColor() = 0;
+ virtual void SetTimebarColor(const CColor &inColor) = 0;
+ virtual Q3DStudio::CString GetTimebarComment() = 0;
+ virtual void SetTimebarComment(const Q3DStudio::CString &inComment) = 0;
+ virtual void SetTimebarTime(ITimeChangeCallback *inCallback = nullptr) = 0;
+};
+
+#endif // INCLUDED_ITIMELINE_TIMEBAR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.cpp
new file mode 100644
index 00000000..bf41f00d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.cpp
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ImageTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "UICDMHandles.h"
+#include "BaseStateRow.h"
+#include "Doc.h"
+#include "IObjectReferenceHelper.h"
+#include "EmptyTimelineTimebar.h"
+
+using namespace UICDM;
+
+CImageTimelineItemBinding::CImageTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+}
+
+CImageTimelineItemBinding::~CImageTimelineItemBinding()
+{
+}
+
+ITimelineTimebar *CImageTimelineItemBinding::GetTimebar()
+{ // No timebars on images
+ return new CEmptyTimelineTimebar();
+}
+
+Q3DStudio::CString CImageTimelineItemBinding::GetName() const
+{
+ return m_Name;
+}
+
+void CImageTimelineItemBinding::SetName(const Q3DStudio::CString &inName)
+{
+ m_Name = inName;
+}
+
+EStudioObjectType CImageTimelineItemBinding::GetObjectType() const
+{
+ return OBJTYPE_IMAGE;
+}
+
+bool CImageTimelineItemBinding::ShowToggleControls() const
+{
+ // no toggle controls, by design
+ return false;
+}
+
+void CImageTimelineItemBinding::Bind(CBaseStateRow *inRow)
+{
+ CUICDMTimelineItemBinding::Bind(inRow);
+ GetRow()->SetNameReadOnly(true);
+}
+
+//=============================================================================
+/**
+ * Open the associated item as though it was double-clicked in explorer
+ */
+bool CImageTimelineItemBinding::OpenAssociatedEditor()
+{
+ return OpenSourcePathFile();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.h
new file mode 100644
index 00000000..2c27dd2d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ImageTimelineItemBinding.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_IMAGE_TIMELINEITEM_BINDING_H
+#define INCLUDED_IMAGE_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+class CBaseStateRow;
+class ITimelineTimebar;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Image type
+ */
+class CImageTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public:
+ CImageTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ virtual ~CImageTimelineItemBinding();
+
+ // CUICDMTimelineItemBinding
+ ITimelineTimebar *GetTimebar() override;
+ Q3DStudio::CString GetName() const override;
+ void SetName(const Q3DStudio::CString &inName) override;
+ EStudioObjectType GetObjectType() const override;
+ bool ShowToggleControls() const override;
+ void Bind(CBaseStateRow *inRow) override;
+ bool OpenAssociatedEditor() override;
+
+ void SetPropertyHandle(UICDM::CUICDMPropertyHandle inProperty)
+ {
+ m_PropertyHandle = inProperty;
+ }
+ UICDM::CUICDMPropertyHandle GetPropertyHandle() const { return m_PropertyHandle; }
+
+protected:
+ Q3DStudio::CString m_Name;
+ UICDM::CUICDMPropertyHandle m_PropertyHandle;
+};
+
+#endif // INCLUDED_IMAGE_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.cpp
new file mode 100644
index 00000000..a4adefbc
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.cpp
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+#include "KeyframesManager.h"
+#include "IDoc.h"
+#include "TimeEditDlg.h"
+#include "TimelineTranslationManager.h"
+#include "UICDMTimelineKeyframe.h"
+#include "UICDMTimelineItemBinding.h"
+#include "CmdDataModelRemoveKeyframe.h"
+#include "CmdDataModelInsertKeyframe.h"
+#include "CmdDataModelChangeKeyframe.h"
+#include "UICDMAnimation.h"
+#include "ClientDataModelBridge.h"
+#include "PasteKeyframesCommandHelper.h"
+#include "IDocumentEditor.h"
+#include "IKeyframe.h"
+#include "Dispatch.h"
+#include "StudioPreferences.h"
+
+#include "StudioApp.h" //for CommitCurrentCommand
+#include "Core.h"
+#include "Dialogs.h"
+
+using namespace UICDM;
+
+bool SortKeyframeInstancePairByTime(const CKeyframesManager::SKeyframeInstancePair &inLHS,
+ const CKeyframesManager::SKeyframeInstancePair &inRHS)
+{
+ return inLHS.m_Keyframe->GetTime() < inRHS.m_Keyframe->GetTime();
+}
+
+//==============================================================================
+// Keyframe specific.
+// UICDM selection is handled by CTimelineTranslationManager.
+//==============================================================================
+CKeyframesManager::CKeyframesManager(CTimelineTranslationManager *inTransMgr)
+ : m_TransMgr(inTransMgr)
+ , m_OffsetKeyframeCommandHelper(*(g_StudioApp.GetCore()->GetDoc()))
+ , m_PasteKeyframeCommandHelper(nullptr)
+{
+ g_StudioApp.GetCore()->GetDoc()->SetKeyframesManager(this);
+}
+
+CKeyframesManager::~CKeyframesManager()
+{
+ delete m_PasteKeyframeCommandHelper;
+}
+
+bool CKeyframesManager::HasSelectedKeyframes(bool inOnlyDynamic /*= false */)
+{
+ // specifically only to know if there are any selected keyframes that are dynamic
+ if (inOnlyDynamic) {
+ for (size_t theIndex = 0; theIndex < m_SelectedKeyframes.size(); ++theIndex)
+ if (m_SelectedKeyframes[theIndex].m_Keyframe->IsDynamic())
+ return true;
+
+ return false;
+ }
+
+ bool theRetVal = !m_SelectedKeyframes.empty();
+
+ return theRetVal;
+}
+
+bool CKeyframesManager::CanPerformKeyframeCopy()
+{
+ bool theCanCopyNewData = false;
+ // Legacy system actually prevents copy/pasting between different instances, so let's preserve
+ // that
+ if (!m_SelectedKeyframes.empty()) {
+ theCanCopyNewData = true;
+ if (m_SelectedKeyframes.size() > 1) {
+ CUICDMTimelineItemBinding *theInstance = m_SelectedKeyframes[0].m_Instance;
+ TSelectedKeyframeList::iterator theIter = m_SelectedKeyframes.begin();
+ ++theIter;
+ for (; theIter != m_SelectedKeyframes.end() && theCanCopyNewData; ++theIter) {
+ if (theIter->m_Instance != theInstance) // fail!
+ theCanCopyNewData = false;
+ }
+ }
+ }
+ return theCanCopyNewData;
+}
+
+bool CKeyframesManager::CanPerformKeyframePaste()
+{
+ if (m_PasteKeyframeCommandHelper && m_PasteKeyframeCommandHelper->HasCopiedKeyframes()) {
+ UICDM::CUICDMInstanceHandle theSelectedInstance =
+ g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance();
+ if (theSelectedInstance.Valid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CKeyframesManager::CopyKeyframes()
+{
+ CopySelectedKeyframes();
+}
+
+// legacy stuff that we have to support for animation tracks in the old data model to work
+inline void PostExecuteCommand(IDoc *inDoc)
+{
+ CDoc *theDoc = dynamic_cast<CDoc *>(inDoc);
+ theDoc->GetCore()->CommitCurrentCommand();
+ // fire render event.
+ // theDoc->UpdateClientScene( true );
+}
+
+//@param inPerformCopy true if that is a copy/cut command. false if this is a delete.
+// Note: Keyframes are never explicitly copied to the clipboard (only as part of a asset copy),
+// hence that means that the keyframes (only) are never copied across different instances of studio.
+bool CKeyframesManager::RemoveKeyframes(bool inPerformCopy)
+{
+ bool theRetVal = HasSelectedKeyframes();
+
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+
+ if (inPerformCopy) // copy prior to removing the keyframes
+ CopySelectedKeyframes();
+
+ CCmdDataModelRemoveKeyframe *theCmd = nullptr;
+ if (!m_SelectedKeyframes.empty()) {
+ TSelectedKeyframeList::iterator theKeyIter = m_SelectedKeyframes.begin();
+ for (; theKeyIter != m_SelectedKeyframes.end(); ++theKeyIter) {
+ CUICDMTimelineKeyframe::TKeyframeHandleList theKeyframeHandles;
+ theKeyIter->m_Keyframe->GetKeyframeHandles(theKeyframeHandles);
+ ASSERT(!theKeyframeHandles.empty());
+ CUICDMTimelineKeyframe::TKeyframeHandleList::iterator theIter =
+ theKeyframeHandles.begin();
+ if (!theCmd) {
+ theCmd = new CCmdDataModelRemoveKeyframe(theDoc, *theIter);
+ ++theIter;
+ }
+ for (; theIter != theKeyframeHandles.end(); ++theIter)
+ theCmd->AddKeyframeHandle(*theIter);
+ }
+ }
+
+ if (theCmd) {
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+ PostExecuteCommand(theDoc);
+ }
+ return theRetVal;
+}
+
+// note: we can't paste data from old data model system to the new one, and vice versa. so either
+// the old Or the new system succeeds in pasting, never both.
+void CKeyframesManager::PasteKeyframes()
+{
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+
+ if (m_PasteKeyframeCommandHelper && m_PasteKeyframeCommandHelper->HasCopiedKeyframes()) {
+ UICDM::CUICDMInstanceHandle theSelectedInstance = theDoc->GetSelectedInstance();
+ if (theSelectedInstance.Valid()) {
+ long theCurrentViewTimeInMilliseconds = theDoc->GetCurrentViewTime();
+ CCmdDataModelInsertKeyframe *theInsertKeyframesCommand =
+ m_PasteKeyframeCommandHelper->GetCommand(theDoc, theCurrentViewTimeInMilliseconds,
+ theSelectedInstance);
+ if (theInsertKeyframesCommand)
+ g_StudioApp.GetCore()->ExecuteCommand(theInsertKeyframesCommand);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Sets interpolation values of all selected keyframes to the values specified
+ * by the user. Pops up a dialog prompting the user to choose new ease in and
+ * ease out values. Values in the dialog are initialized to the left-most
+ * selected keyframe's interpolation values.
+ */
+void CKeyframesManager::SetKeyframeInterpolation()
+{
+ if (!HasSelectedKeyframes())
+ return;
+
+ float theEaseIn = 0;
+ float theEaseOut = 0;
+ if (CStudioPreferences::GetInterpolation())
+ theEaseIn = theEaseOut = 100;
+
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+
+ if (!m_SelectedKeyframes.empty()) // this is a sorted list, so we only need to grab tge ease
+ // in/out values from the first item in this list.
+ {
+ CUICDMTimelineKeyframe *theTimelineKeyframe = m_SelectedKeyframes.front().m_Keyframe;
+ CUICDMTimelineKeyframe::TKeyframeHandleList theKeyframeHandles;
+ theTimelineKeyframe->GetKeyframeHandles(theKeyframeHandles);
+ TKeyframe theKeyframeData = theAnimationCore->GetKeyframeData(theKeyframeHandles[0]);
+ GetEaseInOutValues(theKeyframeData, theEaseIn, theEaseOut);
+ }
+
+ if (g_StudioApp.GetDialogs()->PromptForKeyframeInterpolation(theEaseIn, theEaseOut)) {
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ Q3DStudio::ScopedDocumentEditor editor(*theDoc, L"Set Keyframe Interpolation", __FILE__,
+ __LINE__);
+ TSelectedKeyframeList::iterator theKeyIter = m_SelectedKeyframes.begin();
+ for (; theKeyIter != m_SelectedKeyframes.end(); ++theKeyIter) {
+ CUICDMTimelineKeyframe *theTimelineKeyframe = theKeyIter->m_Keyframe;
+ CUICDMTimelineKeyframe::TKeyframeHandleList theKeyframeHandles;
+ theTimelineKeyframe->GetKeyframeHandles(theKeyframeHandles);
+ for (size_t i = 0; i < theKeyframeHandles.size(); ++i) {
+ TKeyframe theKeyframeData =
+ theAnimationCore->GetKeyframeData(theKeyframeHandles[i]);
+ SetEaseInOutValues(theKeyframeData, theEaseIn, theEaseOut);
+ theAnimationCore->SetKeyframeData(theKeyframeHandles[i], theKeyframeData);
+ }
+ }
+ }
+}
+
+bool CKeyframesManager::HasDynamicKeyframes()
+{
+ CUICDMTimelineItemBinding *theBinding = m_TransMgr->GetSelectedBinding();
+ if (theBinding) {
+ return theBinding->HasDynamicKeyframes(-1);
+ }
+ return false;
+}
+
+void CKeyframesManager::SelectAllKeyframes()
+{
+ ITimelineItemBinding *theBinding = m_TransMgr->GetSelectedBinding();
+ if (theBinding)
+ theBinding->SelectKeyframes(true);
+}
+
+void CKeyframesManager::DeselectAllKeyframes()
+{
+ m_TransMgr->ClearBindingsKeyframeSelection();
+ m_SelectedKeyframes.clear();
+}
+
+//==============================================================================
+/**
+ * Sets keyframes on all the changed properties of the selected object.
+ * Also known as autoset keyframes, but it only applies to one object, and is
+ * the result of an F6 key press. Tells the TimelineCtrl to autoset keyframes.
+ */
+void CKeyframesManager::SetChangedKeyframes()
+{
+
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::CUICDMInstanceHandle theSelectedInstance = theDoc->GetSelectedInstance();
+ if (theSelectedInstance.Valid()) {
+ using namespace Q3DStudio;
+ Q3DStudio::ScopedDocumentEditor editor(*theDoc, L"Set Changed Keyframes", __FILE__,
+ __LINE__);
+ CStudioSystem *theStudioSystem = theDoc->GetStudioSystem();
+ // Get all animated properties.
+ TPropertyHandleList theProperties;
+ theStudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(theSelectedInstance,
+ theProperties);
+ for (size_t thePropertyIndex = 0; thePropertyIndex < theProperties.size();
+ ++thePropertyIndex) {
+ if (theStudioSystem->GetAnimationSystem()->IsPropertyAnimated(
+ theSelectedInstance, theProperties[thePropertyIndex]))
+ editor->KeyframeProperty(theSelectedInstance, theProperties[thePropertyIndex],
+ true);
+ }
+ }
+}
+
+void CKeyframesManager::SetKeyframeTime(long inTime)
+{
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.SetKeyframesManager(this);
+ theTimeEditDlg.ShowDialog(inTime, 0, g_StudioApp.GetCore()->GetDoc(), ASSETKEYFRAME);
+}
+
+void CKeyframesManager::SetKeyframeDynamic(CUICDMTimelineKeyframe *inKeyframe, bool inDynamic)
+{
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ UICDM::TKeyframeHandleList theKeyframeHandle;
+ CCmdDataModelChangeDynamicKeyframe *theCmd = nullptr;
+
+ if (inKeyframe != nullptr)
+ inKeyframe->GetKeyframeHandles(theKeyframeHandle);
+
+ UICDM::IAnimationCore *theAnimationCore = theDoc->GetStudioSystem()->GetAnimationCore();
+ for (size_t theKeyframe = 0; theKeyframe < theKeyframeHandle.size(); ++theKeyframe) {
+ UICDM::CUICDMAnimationHandle theAnimation(
+ theAnimationCore->GetAnimationForKeyframe(theKeyframeHandle.at(theKeyframe)));
+ if (!theCmd)
+ theCmd = new CCmdDataModelChangeDynamicKeyframe(theDoc, theAnimation, inDynamic);
+ else
+ theCmd->AddHandle(theAnimation);
+ }
+
+ if (theCmd)
+ g_StudioApp.GetCore()->ExecuteCommand(theCmd);
+}
+
+void CKeyframesManager::SetKeyframesDynamic(bool inDynamic)
+{
+
+ CUICDMTimelineKeyframe *theKeyframe;
+
+ if (m_SelectedKeyframes.size() == 0) {
+ CUICDMTimelineItemBinding *theBinding = m_TransMgr->GetSelectedBinding();
+ IKeyframe *key = theBinding->GetKeyframeByIndex(0);
+ theKeyframe = dynamic_cast<CUICDMTimelineKeyframe *>(key);
+ SetKeyframeDynamic(theKeyframe, inDynamic);
+ } else {
+ for (int i = 0; i < (int)m_SelectedKeyframes.size(); ++i) {
+ theKeyframe = m_SelectedKeyframes[i].m_Keyframe;
+ SetKeyframeDynamic(theKeyframe, inDynamic);
+ }
+ }
+}
+
+long CKeyframesManager::OffsetSelectedKeyframes(long inOffset)
+{
+ m_InstanceSet.clear();
+ m_InstanceList.clear();
+ std::set<CUICDMTimelineItemBinding *> &theInstances(m_InstanceSet);
+
+ TSelectedKeyframeList::iterator theKeyIter = m_SelectedKeyframes.begin();
+ for (; theKeyIter != m_SelectedKeyframes.end(); ++theKeyIter) {
+ // since this list is sorted by time and we are iterating from the first in the list
+ long theKeyframeTime = theKeyIter->m_Keyframe->GetTime();
+ if (inOffset < 0 && theKeyframeTime + inOffset < 0)
+ inOffset = -theKeyframeTime;
+
+ theKeyIter->m_Keyframe->UpdateKeyframesTime(&m_OffsetKeyframeCommandHelper,
+ theKeyframeTime + inOffset);
+
+ // this contains unique instancs, i.e. mulitple keyframe can map to the same instances
+ if (theInstances.insert(theKeyIter->m_Instance).second)
+ m_InstanceList.push_back(theKeyIter->m_Instance->GetInstance());
+ }
+
+ // UI update, explicitly because this doesn't generate any events till action is committed
+ std::set<CUICDMTimelineItemBinding *>::iterator theInstanceIter = theInstances.begin();
+ for (; theInstanceIter != theInstances.end(); ++theInstanceIter)
+ (*theInstanceIter)->UIRefreshPropertyKeyframe(inOffset);
+
+ if (m_InstanceList.size())
+ m_TransMgr->GetDoc()->GetCore()->GetDispatch()->FireImmediateRefreshInstance(
+ &m_InstanceList[0], (long)m_InstanceList.size());
+
+ // by contract, this functions returns the "legal" offset, ie time cannot be offset to negative.
+ return inOffset;
+}
+
+void CKeyframesManager::CommitChangedKeyframes()
+{
+ m_OffsetKeyframeCommandHelper.Finalize();
+}
+
+void CKeyframesManager::RollbackChangedKeyframes()
+{
+ m_OffsetKeyframeCommandHelper.Rollback();
+}
+
+bool CKeyframesManager::CanMakeSelectedKeyframesDynamic()
+{
+ using namespace UICDM;
+ TKeyframeHandleList theKeyframes;
+ TKeyframeHandleList allTheKeyframes;
+ IAnimationCore &theAnimationCore(
+ *g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetAnimationCore());
+ // Ensure that all keyframes selected are the first keyframes from the animation track.
+ for (size_t idx = 0, end = m_SelectedKeyframes.size(); idx < end; ++idx) {
+ theKeyframes.clear();
+ m_SelectedKeyframes.at(idx).m_Keyframe->GetKeyframeHandles(theKeyframes);
+ for (size_t specificKeyframeIdx = 0, specificKeyframeEnd = theKeyframes.size();
+ specificKeyframeIdx < specificKeyframeEnd; ++specificKeyframeIdx) {
+ CUICDMKeyframeHandle theKeyframe = theKeyframes[specificKeyframeIdx];
+ CUICDMAnimationHandle theAnimation =
+ theAnimationCore.GetAnimationForKeyframe(theKeyframe);
+ allTheKeyframes.clear();
+ theAnimationCore.GetKeyframes(theAnimation, allTheKeyframes);
+ if (allTheKeyframes[0] != theKeyframe)
+ return false;
+ }
+ }
+ return true;
+}
+
+// keeps track of selected keyframes so that we don't have to iterate through the entire hierarchy
+// to find them
+void CKeyframesManager::SetKeyframeSelected(CUICDMTimelineKeyframe *inKeyframe, bool inSelected,
+ CUICDMTimelineItemBinding *inOwningInstance /*= nullptr */)
+{
+ TSelectedKeyframeList::iterator theKeyIter = m_SelectedKeyframes.begin();
+ for (; theKeyIter != m_SelectedKeyframes.end(); ++theKeyIter) {
+ if (theKeyIter->m_Keyframe == inKeyframe)
+ break;
+ }
+ if (inSelected) {
+ ASSERT(inOwningInstance);
+ if (theKeyIter == m_SelectedKeyframes.end()) { // only this is not already selected
+ m_SelectedKeyframes.push_back(SKeyframeInstancePair(inKeyframe, inOwningInstance));
+ std::sort(m_SelectedKeyframes.begin(), m_SelectedKeyframes.end(),
+ SortKeyframeInstancePairByTime);
+ }
+ } else if (theKeyIter != m_SelectedKeyframes.end()) {
+ m_SelectedKeyframes.erase(theKeyIter);
+ }
+}
+
+UICDM::SGetOrSetKeyframeInfo SetupKeyframeInfo(UICDM::CUICDMKeyframeHandle inKeyframe,
+ UICDM::IAnimationCore &inCore)
+{
+ TKeyframe theKeyframeData = inCore.GetKeyframeData(inKeyframe);
+ SEaseInEaseOutKeyframe theKeyframe = UICDM::get<SEaseInEaseOutKeyframe>(theKeyframeData);
+ // Is this the first keyframe?
+ bool isDynamic = false;
+ if (inCore.IsFirstKeyframe(inKeyframe))
+ isDynamic = inCore.GetAnimationInfo(inCore.GetAnimationForKeyframe(inKeyframe))
+ .m_DynamicFirstKeyframe;
+
+ return SGetOrSetKeyframeInfo(theKeyframe.m_KeyframeValue, theKeyframe.m_EaseIn,
+ theKeyframe.m_EaseOut, isDynamic);
+}
+
+// only deal with this manager's selected keyframes
+void CKeyframesManager::CopySelectedKeyframes()
+{
+ if (!m_SelectedKeyframes.empty()) {
+ if (m_PasteKeyframeCommandHelper)
+ m_PasteKeyframeCommandHelper->Clear(); // clear out previously copied data
+ else
+ m_PasteKeyframeCommandHelper = new CPasteKeyframeCommandHelper();
+
+ // note: m_SelectedKeyframes is already sorted by time
+ float theEarliestKeyframeTimeInSecs =
+ CUICDMTimelineKeyframe::GetTimeInSecs(m_SelectedKeyframes[0].m_Keyframe->GetTime());
+
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+ TSelectedKeyframeList::iterator theIter = m_SelectedKeyframes.begin();
+ for (; theIter != m_SelectedKeyframes.end(); ++theIter) {
+ CUICDMTimelineKeyframe *theKeyframe = (*theIter).m_Keyframe;
+ CUICDMTimelineKeyframe::TKeyframeHandleList theKeyframeHandles;
+ theKeyframe->GetKeyframeHandles(theKeyframeHandles);
+ UICDM::SGetOrSetKeyframeInfo theInfos[3];
+ size_t theValidInfos = 0;
+ if (!theKeyframeHandles.empty()) {
+ // TODO: need to figure out a good way to convert from individual keyframes back to
+ // SValue
+ SValue theValue;
+ switch (theKeyframeHandles.size()) {
+ case 1: {
+ theInfos[0] = SetupKeyframeInfo(theKeyframeHandles[0], *theAnimationCore);
+ theValidInfos = 1;
+
+ } break;
+ case 3: {
+ theInfos[0] = SetupKeyframeInfo(theKeyframeHandles[0], *theAnimationCore);
+ theInfos[1] = SetupKeyframeInfo(theKeyframeHandles[1], *theAnimationCore);
+ theInfos[2] = SetupKeyframeInfo(theKeyframeHandles[2], *theAnimationCore);
+ theValidInfos = 3;
+ } break;
+ default: // not handled
+ break;
+ }
+ // time is relative to the earliest keyframe time.
+ float theRelativeTimeInSecs =
+ CUICDMTimelineKeyframe::GetTimeInSecs(theKeyframe->GetTime())
+ - theEarliestKeyframeTimeInSecs;
+
+ CUICDMAnimationHandle theAnimation =
+ theAnimationCore->GetAnimationForKeyframe(theKeyframeHandles[0]);
+ m_PasteKeyframeCommandHelper->AddKeyframeData(
+ theAnimationCore->GetAnimationInfo(theAnimation).m_Property,
+ theRelativeTimeInSecs, theInfos, theValidInfos);
+ }
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.h
new file mode 100644
index 00000000..61ac2740
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/KeyframesManager.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_KEYFRAMES_MANAGER_H
+#define INCLUDED_KEYFRAMES_MANAGER_H 1
+
+#pragma once
+
+#include "ITimelineKeyframesManager.h"
+#include "OffsetKeyframesCommandHelper.h"
+
+class CTimelineTranslationManager;
+class CUICDMTimelineKeyframe;
+class CUICDMTimelineItemBinding;
+class CPasteKeyframeCommandHelper;
+
+//=============================================================================
+/**
+ * Abstraction layer to the class that manages both selected keyframes.
+ */
+//=============================================================================
+class CKeyframesManager : public ITimelineKeyframesManager
+{
+public:
+ CKeyframesManager(CTimelineTranslationManager *inTransMgr);
+ virtual ~CKeyframesManager();
+
+ // IKeyframesManager
+ bool HasSelectedKeyframes(bool inOnlyDynamic = false) override;
+ bool HasDynamicKeyframes() override;
+ bool CanPerformKeyframeCopy() override;
+ bool CanPerformKeyframePaste() override;
+ void CopyKeyframes() override;
+ bool RemoveKeyframes(bool inPerformCopy) override;
+ void PasteKeyframes() override;
+ void SetKeyframeInterpolation() override;
+ void DeselectAllKeyframes() override;
+ void SelectAllKeyframes() override;
+ void SetChangedKeyframes() override;
+ // ITimelineKeyframesManager
+ void SetKeyframeTime(long inTime) override;
+ void SetKeyframesDynamic(bool inDynamic) override;
+ bool CanMakeSelectedKeyframesDynamic() override;
+ long OffsetSelectedKeyframes(long inOffset) override;
+ void CommitChangedKeyframes() override;
+ void RollbackChangedKeyframes() override;
+
+ void SetKeyframeSelected(CUICDMTimelineKeyframe *inKeyframe, bool inSelected,
+ CUICDMTimelineItemBinding *inOwningInstance = nullptr);
+
+protected:
+ void SetKeyframeDynamic(CUICDMTimelineKeyframe *inKeyframe, bool inDynamic);
+ void CopySelectedKeyframes();
+
+public:
+ struct SKeyframeInstancePair
+ {
+ CUICDMTimelineKeyframe *m_Keyframe;
+ CUICDMTimelineItemBinding *m_Instance;
+
+ SKeyframeInstancePair(CUICDMTimelineKeyframe *inKeyframe,
+ CUICDMTimelineItemBinding *inInstance)
+ {
+ m_Keyframe = inKeyframe;
+ m_Instance = inInstance;
+ }
+ };
+
+protected:
+ typedef std::vector<SKeyframeInstancePair> TSelectedKeyframeList; ///< handle multiple keyframes
+ ///manipulation, e.g.
+ ///offsetting by dragging
+
+ CTimelineTranslationManager *m_TransMgr;
+ TSelectedKeyframeList m_SelectedKeyframes;
+ COffsetKeyframesCommandHelper m_OffsetKeyframeCommandHelper; // so that we can commit on mouseup
+ CPasteKeyframeCommandHelper *m_PasteKeyframeCommandHelper;
+ std::set<CUICDMTimelineItemBinding *> m_InstanceSet;
+ std::vector<UICDM::CUICDMInstanceHandle> m_InstanceList;
+};
+
+#endif // INCLUDED_IKEYFRAMES_MANAGER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.cpp
new file mode 100644
index 00000000..73d5d5d2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "LayerTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "BaseStateRow.h"
+#include "ImageTimelineItemBinding.h"
+#include "EmptyTimelineTimebar.h"
+
+// Data model specific
+#include "IDoc.h"
+#include "ClientDataModelBridge.h"
+#include "DropSource.h"
+#include "Doc.h"
+
+#include "UICDMHandles.h"
+#include "UICDMStudioSystem.h"
+
+#include "UICDMMetaData.h"
+#include "UICDMDataCore.h"
+#include "StudioFullSystem.h"
+#include "StudioCoreSystem.h"
+#include "UICDMSlides.h"
+
+using namespace UICDM;
+
+namespace {
+
+bool ImageSlotIsFilled(UICDM::IPropertySystem *inPropertySystem, CUICDMInstanceHandle inInstance,
+ const TCharStr &inStr)
+{
+ CUICDMPropertyHandle theProperty =
+ inPropertySystem->GetAggregateInstancePropertyByName(inInstance, inStr);
+ SValue theValue;
+ inPropertySystem->GetInstancePropertyValue(inInstance, theProperty, theValue);
+
+ SLong4 theLong4 = UICDM::get<SLong4>(theValue);
+ bool theReturn = theLong4.m_Longs[0] != 0 || theLong4.m_Longs[1] != 0
+ || theLong4.m_Longs[2] != 0 || theLong4.m_Longs[3] != 0;
+
+ return theReturn;
+}
+
+// helper function to find the image binding class that 'represents' this property
+inline CImageTimelineItemBinding *FindImageBindingByProperty(CBaseStateRow *inRow,
+ CUICDMPropertyHandle inProperty)
+{
+ if (!inRow || !inProperty.Valid())
+ return nullptr;
+
+ CImageTimelineItemBinding *theInvalidImageBinding = nullptr;
+ for (long theIndex = 0; theIndex < inRow->GetNumNonPropertyRows(); ++theIndex) {
+ CImageTimelineItemBinding *theImageBinding = dynamic_cast<CImageTimelineItemBinding *>(
+ inRow->GetNonPropertyRow(theIndex)->GetTimelineItemBinding());
+ if (theImageBinding && theImageBinding->GetPropertyHandle() == inProperty) {
+ theInvalidImageBinding = theImageBinding;
+ break;
+ }
+ }
+ return theInvalidImageBinding;
+}
+}
+
+CLayerTimelineItemBinding::CLayerTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ TPropertyHandleList theProperties;
+ thePropertySystem->GetAggregateInstanceProperties(inDataHandle, theProperties);
+
+ size_t thePropertyCount = theProperties.size();
+ for (size_t thePropertyIndex = 0; thePropertyIndex < thePropertyCount; ++thePropertyIndex) {
+ CUICDMPropertyHandle theProperty = theProperties[thePropertyIndex];
+
+ AdditionalMetaDataType::Value theAdditionalMetaDataType =
+ thePropertySystem->GetAdditionalMetaDataType(inDataHandle, theProperty);
+
+ if (theAdditionalMetaDataType == AdditionalMetaDataType::Image) {
+ TCharStr theName(thePropertySystem->GetName(theProperty));
+ TCharStr theFormalName(thePropertySystem->GetFormalName(inDataHandle, theProperty));
+ TNameFormalNamePair thePair =
+ std::make_tuple(theName, theFormalName, theProperty);
+ m_ImageNameFormalNamePairs.push_back(thePair);
+ }
+ }
+}
+
+CLayerTimelineItemBinding::~CLayerTimelineItemBinding()
+{
+}
+
+EStudioObjectType CLayerTimelineItemBinding::GetObjectType() const
+{
+ return OBJTYPE_LAYER;
+}
+
+ITimelineItemBinding *CLayerTimelineItemBinding::GetChild(long inIndex)
+{
+ static const TCharStr theLayerPrefix(L"Layer_");
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ if (theInstance.Valid()) {
+ Q3DStudio::CGraphIterator theChildren;
+ CUICDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide();
+ GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(),
+ theChildren, theActiveSlide);
+ theChildren += inIndex;
+
+ UICDM::CUICDMInstanceHandle theChildInstance = theChildren.GetCurrent();
+ if (theChildInstance.Valid()) {
+ std::shared_ptr<IDataCore> theDataCore =
+ m_TransMgr->GetStudioSystem()->GetFullSystem()->GetCoreSystem()->GetDataCore();
+ ISlideSystem *theSlideSystem = m_TransMgr->GetStudioSystem()->GetSlideSystem();
+ ISlideCore *theSlideCore = m_TransMgr->GetStudioSystem()->GetSlideCore();
+
+ size_t theSlotCursor = (size_t)-1;
+ {
+
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ UICDM::SLong4 theGuid;
+ {
+ CUICDMPropertyHandle theTypeProperty =
+ thePropertySystem->GetAggregateInstancePropertyByName(theChildInstance,
+ L"id");
+ SValue theIdValue;
+ thePropertySystem->GetInstancePropertyValue(theChildInstance, theTypeProperty,
+ theIdValue);
+ theGuid = UICDM::get<UICDM::SLong4>(theIdValue);
+ }
+ for (size_t theSlotIndex = 0, theSlotCount = m_ImageNameFormalNamePairs.size();
+ theSlotIndex < theSlotCount; ++theSlotIndex) {
+ bool theIsMatch = false;
+ UICDM::CUICDMPropertyHandle theProperty =
+ std::get<2>(m_ImageNameFormalNamePairs[theSlotIndex]);
+ SValue theValue;
+ const SUICDMPropertyDefinition &theDefinition(
+ theDataCore->GetProperty(theProperty));
+ if (theDefinition.m_Type == DataModelDataType::Long4) {
+ SValue theDCValue;
+ if (theDataCore->GetInstancePropertyValue(theInstance, theProperty,
+ theDCValue)) {
+ SLong4 thePropGuid = get<SLong4>(theDCValue);
+ if (thePropGuid == theGuid)
+ theIsMatch = true;
+ }
+ CUICDMSlideHandle theSlide =
+ theSlideSystem->GetAssociatedSlide(theChildInstance);
+ CUICDMSlideHandle theMasterSlide = theSlideSystem->GetMasterSlide(theSlide);
+ if (theIsMatch == false && theSlide.Valid()
+ && theSlideCore->GetSpecificInstancePropertyValue(
+ theSlide, theInstance, theProperty, theValue)) {
+ SLong4 thePropGuid = get<SLong4>(theValue);
+ if (thePropGuid == theGuid)
+ theIsMatch = true;
+ }
+ }
+ if (theIsMatch) {
+ theSlotCursor = theSlotIndex;
+ break;
+ }
+ }
+ }
+ if (theSlotCursor != (size_t)-1) {
+ CUICDMPropertyHandle theImageProperty =
+ thePropertySystem->GetAggregateInstancePropertyByName(
+ m_DataHandle, std::get<0>(m_ImageNameFormalNamePairs[theSlotCursor]));
+ return GetOrCreateImageBinding(
+ theImageProperty,
+ std::get<1>(m_ImageNameFormalNamePairs[theSlotCursor]).wide_str());
+ } else
+ return m_TransMgr->GetOrCreate(theChildInstance);
+ }
+ }
+ return nullptr;
+}
+
+void CLayerTimelineItemBinding::OnAddChild(UICDM::CUICDMInstanceHandle inInstance)
+{
+ using namespace UICDM;
+ CClientDataModelBridge *theBridge = m_TransMgr->GetStudioSystem()->GetClientDataModelBridge();
+ // This is handled via the OnPropertyChanged call below
+ if (theBridge->IsImageInstance(inInstance))
+ return;
+ else
+ CUICDMTimelineItemBinding::OnAddChild(inInstance);
+}
+
+void CLayerTimelineItemBinding::OnPropertyChanged(CUICDMPropertyHandle inPropertyHandle)
+{
+ bool theHandled = false;
+ if (m_Row) {
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ CClientDataModelBridge *theBridge =
+ m_TransMgr->GetStudioSystem()->GetClientDataModelBridge();
+ UICDM::TCharStr thePropertyName = thePropertySystem->GetName(inPropertyHandle);
+ size_t theSlotCount = m_ImageNameFormalNamePairs.size();
+ for (size_t theSlotIndex = 0; theSlotIndex < theSlotCount; ++theSlotIndex) {
+ UICDM::TCharStr thePropName = std::get<0>(m_ImageNameFormalNamePairs[theSlotIndex]);
+ if (thePropertyName == thePropName) {
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, thePropName)) {
+ // already created, bail!
+ if (m_TransMgr->GetBinding(GetImage(inPropertyHandle)))
+ return;
+
+ // Image property was changed from one non-zero guid value to another, delete
+ // the old and and create a new one
+ CImageTimelineItemBinding *theReplacedImageBinding =
+ FindImageBindingByProperty(m_Row, inPropertyHandle);
+ if (theReplacedImageBinding)
+ m_Row->RemoveChildRow(theReplacedImageBinding);
+
+ ITimelineItemBinding *theNextImageBinding = nullptr;
+ // Determine if this is inserted somewhere in the existing list.
+ for (size_t theNextImage = theSlotIndex + 1; theNextImage < theSlotCount;
+ ++theNextImage) {
+ UICDM::TCharStr theTempName =
+ std::get<0>(m_ImageNameFormalNamePairs[theNextImage]);
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, theTempName)) {
+ CUICDMPropertyHandle theNextImageProperty =
+ theBridge->GetAggregateInstancePropertyByName(m_DataHandle,
+ theTempName);
+ theNextImageBinding =
+ m_TransMgr->GetBinding(GetImage(theNextImageProperty));
+ break;
+ }
+ }
+ m_Row->AddChildRow(
+ GetOrCreateImageBinding(
+ inPropertyHandle,
+ std::get<1>(m_ImageNameFormalNamePairs[theSlotIndex]).wide_str()),
+ theNextImageBinding);
+ } else // check for delete
+ {
+ // GetImage will not return anything valid since the value is nuked.
+ // From the UI end, there is no way we can tell which image is associated with
+ // this property, since that is "encapsulated" in the property value.
+ CImageTimelineItemBinding *theInvalidImageBinding =
+ FindImageBindingByProperty(m_Row, inPropertyHandle);
+ if (theInvalidImageBinding)
+ m_Row->RemoveChildRow(theInvalidImageBinding);
+ }
+ theHandled = true;
+ break;
+ }
+ }
+ }
+ if (!theHandled)
+ CUICDMTimelineItemBinding::OnPropertyChanged(inPropertyHandle);
+}
+
+UICDM::CUICDMInstanceHandle
+CLayerTimelineItemBinding::GetImage(UICDM::CUICDMPropertyHandle inPropertyHandle)
+{
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ SValue theImageValue;
+ thePropertySystem->GetInstancePropertyValue(m_DataHandle, inPropertyHandle, theImageValue);
+ SLong4 theImageLong4 = UICDM::get<SLong4>(theImageValue);
+ return m_TransMgr->GetStudioSystem()->GetClientDataModelBridge()->GetImageInstanceByGUID(
+ theImageLong4);
+}
+
+ITimelineItemBinding *
+CLayerTimelineItemBinding::GetOrCreateImageBinding(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ const wchar_t *inName)
+{
+ UICDM::CUICDMInstanceHandle theImageInstance = GetImage(inPropertyHandle);
+ ITimelineItemBinding *theImageTimelineRow = m_TransMgr->GetBinding(theImageInstance);
+ if (!theImageTimelineRow) // create
+ {
+ theImageTimelineRow = m_TransMgr->GetOrCreate(theImageInstance);
+ // Set the name, by spec: the nice name.
+ theImageTimelineRow->GetTimelineItem()->SetName(inName);
+ CImageTimelineItemBinding *theImageBinding =
+ dynamic_cast<CImageTimelineItemBinding *>(theImageTimelineRow);
+ if (theImageBinding)
+ theImageBinding->SetPropertyHandle(inPropertyHandle);
+ }
+ return theImageTimelineRow;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.h
new file mode 100644
index 00000000..d4826f2f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/LayerTimelineItemBinding.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_LAYER_TIMELINEITEM_BINDING_H
+#define INCLUDED_LAYER_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+
+namespace UICDM {
+class CStudioSystem;
+}
+
+//=============================================================================
+/**
+ * Binding to generic UICDM object
+ */
+class CLayerTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public: // Types
+ typedef std::tuple<UICDM::TCharStr, UICDM::TCharStr, UICDM::CUICDMPropertyHandle>
+ TNameFormalNamePair;
+ typedef std::vector<TNameFormalNamePair> TNameFormalNamePairList;
+
+protected: // Members
+ TNameFormalNamePairList m_ImageNameFormalNamePairs;
+
+public: // Construction
+ CLayerTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ virtual ~CLayerTimelineItemBinding();
+
+public: // CUICDMTimelineItemBinding
+ EStudioObjectType GetObjectType() const override;
+ // Hierarchy
+ ITimelineItemBinding *GetChild(long inIndex) override;
+ void OnAddChild(UICDM::CUICDMInstanceHandle inInstance) override;
+ // Event callback
+ void OnPropertyChanged(UICDM::CUICDMPropertyHandle inPropertyHandle) override;
+
+protected:
+ UICDM::CUICDMInstanceHandle GetImage(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ ITimelineItemBinding *GetOrCreateImageBinding(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ const wchar_t *inName);
+};
+
+#endif // INCLUDED_LAYER_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.cpp
new file mode 100644
index 00000000..eb91714e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.cpp
@@ -0,0 +1,336 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MaterialTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "BaseStateRow.h"
+#include "ImageTimelineItemBinding.h"
+#include "EmptyTimelineTimebar.h"
+
+// Data model specific
+#include "IDoc.h"
+#include "ClientDataModelBridge.h"
+#include "DropSource.h"
+
+#include "UICDMHandles.h"
+#include "UICDMStudioSystem.h"
+
+#include "UICDMMetaData.h"
+#include "UICDMDataCore.h"
+#include "StudioFullSystem.h"
+#include "StudioCoreSystem.h"
+
+using namespace UICDM;
+
+CMaterialTimelineItemBinding::CMaterialTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ TPropertyHandleList theProperties;
+ thePropertySystem->GetAggregateInstanceProperties(inDataHandle, theProperties);
+
+ size_t thePropertyCount = theProperties.size();
+ for (size_t thePropertyIndex = 0; thePropertyIndex < thePropertyCount; ++thePropertyIndex) {
+ CUICDMPropertyHandle theProperty = theProperties[thePropertyIndex];
+
+ AdditionalMetaDataType::Value theAdditionalMetaDataType =
+ thePropertySystem->GetAdditionalMetaDataType(inDataHandle, theProperty);
+
+ if (theAdditionalMetaDataType == AdditionalMetaDataType::Image) {
+ TCharStr theName(thePropertySystem->GetName(theProperty));
+ TCharStr theFormalName(thePropertySystem->GetFormalName(inDataHandle, theProperty));
+ TNameFormalNamePair thePair = std::make_tuple(theName, theFormalName);
+ m_ImageNameFormalNamePairs.push_back(thePair);
+ }
+ }
+}
+
+CMaterialTimelineItemBinding::~CMaterialTimelineItemBinding()
+{
+}
+
+ITimelineTimebar *CMaterialTimelineItemBinding::GetTimebar()
+{ // No timebars on materials
+ return new CEmptyTimelineTimebar();
+}
+
+EStudioObjectType CMaterialTimelineItemBinding::GetObjectType() const
+{
+ return OBJTYPE_MATERIAL;
+}
+
+bool CMaterialTimelineItemBinding::ShowToggleControls() const
+{
+ // Materials have no toggle controls, by design
+ return false;
+}
+
+bool ImageSlotIsFilled(UICDM::IPropertySystem *inPropertySystem, CUICDMInstanceHandle inInstance,
+ const TCharStr &inStr)
+{
+ CUICDMPropertyHandle theProperty =
+ inPropertySystem->GetAggregateInstancePropertyByName(inInstance, inStr);
+ SValue theValue;
+ inPropertySystem->GetInstancePropertyValue(inInstance, theProperty, theValue);
+
+ // Prevent assertion down the path when changing from edited standard material to reference material
+ if (UICDM::GetValueType(theValue) == DataModelDataType::None)
+ return false;
+
+ SLong4 theLong4 = UICDM::get<SLong4>(theValue);
+ bool theReturn = theLong4.m_Longs[0] != 0 || theLong4.m_Longs[1] != 0
+ || theLong4.m_Longs[2] != 0 || theLong4.m_Longs[3] != 0;
+
+ return theReturn;
+}
+
+long CMaterialTimelineItemBinding::GetChildrenCount()
+{
+ long theReturnCount = 0;
+ if (m_TransMgr->GetStudioSystem()->IsInstance(m_DataHandle)) {
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ size_t theSlotCount = m_ImageNameFormalNamePairs.size();
+ for (size_t theSlotIndex = 0; theSlotIndex < theSlotCount; ++theSlotIndex) {
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle,
+ std::get<0>(m_ImageNameFormalNamePairs[theSlotIndex]))) {
+ ++theReturnCount;
+ }
+ }
+ }
+
+ return theReturnCount;
+}
+
+ITimelineItemBinding *CMaterialTimelineItemBinding::GetChild(long inIndex)
+{
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+
+ size_t theSlotCursor = 0;
+ size_t theSlotCount = m_ImageNameFormalNamePairs.size();
+ for (size_t theSlotIndex = 0; theSlotIndex < theSlotCount; ++theSlotIndex) {
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle,
+ std::get<0>(m_ImageNameFormalNamePairs[theSlotIndex]))) {
+ inIndex--;
+
+ if (inIndex < 0) {
+ theSlotCursor = theSlotIndex;
+ break;
+ }
+ }
+ }
+ CUICDMPropertyHandle theImageProperty = thePropertySystem->GetAggregateInstancePropertyByName(
+ m_DataHandle, std::get<0>(m_ImageNameFormalNamePairs[theSlotCursor]));
+ return GetOrCreateImageBinding(
+ theImageProperty, std::get<1>(m_ImageNameFormalNamePairs[theSlotCursor]).wide_str());
+}
+
+void CMaterialTimelineItemBinding::OnAddChild(UICDM::CUICDMInstanceHandle inInstance)
+{
+ using namespace UICDM;
+ CClientDataModelBridge *theBridge = m_TransMgr->GetStudioSystem()->GetClientDataModelBridge();
+ // This is handled via the OnPropertyChanged call below
+ if (theBridge->IsImageInstance(inInstance))
+ return;
+ else
+ CUICDMTimelineItemBinding::OnAddChild(inInstance);
+}
+
+// helper function to find the image binding class that 'represents' this property
+inline CImageTimelineItemBinding *FindImageBindingByProperty(CBaseStateRow *inRow,
+ CUICDMPropertyHandle inProperty)
+{
+ if (!inRow || !inProperty.Valid())
+ return nullptr;
+
+ CImageTimelineItemBinding *theInvalidImageBinding = nullptr;
+ for (long theIndex = 0; theIndex < inRow->GetNumNonPropertyRows(); ++theIndex) {
+ CImageTimelineItemBinding *theImageBinding = dynamic_cast<CImageTimelineItemBinding *>(
+ inRow->GetNonPropertyRow(theIndex)->GetTimelineItemBinding());
+ if (theImageBinding && theImageBinding->GetPropertyHandle() == inProperty) {
+ theInvalidImageBinding = theImageBinding;
+ break;
+ }
+ }
+ return theInvalidImageBinding;
+}
+
+void CMaterialTimelineItemBinding::OnPropertyChanged(CUICDMPropertyHandle inPropertyHandle)
+{
+ bool theHandled = false;
+ if (m_Row) {
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ CClientDataModelBridge *theBridge =
+ m_TransMgr->GetStudioSystem()->GetClientDataModelBridge();
+ UICDM::TCharStr thePropertyName = thePropertySystem->GetName(inPropertyHandle);
+ size_t theSlotCount = m_ImageNameFormalNamePairs.size();
+ for (size_t theSlotIndex = 0; theSlotIndex < theSlotCount; ++theSlotIndex) {
+ UICDM::TCharStr thePropName = std::get<0>(m_ImageNameFormalNamePairs[theSlotIndex]);
+ if (thePropertyName == thePropName) {
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, thePropName)) {
+ // already created, bail!
+ if (m_TransMgr->GetBinding(GetImage(inPropertyHandle)))
+ return;
+
+ // Image property was changed from one non-zero guid value to another, delete
+ // the old and and create a new one
+ CImageTimelineItemBinding *theReplacedImageBinding =
+ FindImageBindingByProperty(m_Row, inPropertyHandle);
+ if (theReplacedImageBinding)
+ m_Row->RemoveChildRow(theReplacedImageBinding);
+
+ ITimelineItemBinding *theNextImageBinding = nullptr;
+ // Determine if this is inserted somewhere in the existing list.
+ for (size_t theNextImage = theSlotIndex + 1; theNextImage < theSlotCount;
+ ++theNextImage) {
+ UICDM::TCharStr theTempName =
+ std::get<0>(m_ImageNameFormalNamePairs[theNextImage]);
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, theTempName)) {
+ CUICDMPropertyHandle theNextImageProperty =
+ theBridge->GetAggregateInstancePropertyByName(m_DataHandle,
+ theTempName);
+ theNextImageBinding =
+ m_TransMgr->GetBinding(GetImage(theNextImageProperty));
+ break;
+ }
+ }
+ m_Row->AddChildRow(
+ GetOrCreateImageBinding(
+ inPropertyHandle,
+ std::get<1>(m_ImageNameFormalNamePairs[theSlotIndex]).wide_str()),
+ theNextImageBinding);
+ } else // check for delete
+ {
+ // GetImage will not return anything valid since the value is nuked.
+ // From the UI end, there is no way we can tell which image is associated with
+ // this property, since that is "encapsulated" in the property value.
+ CImageTimelineItemBinding *theInvalidImageBinding =
+ FindImageBindingByProperty(m_Row, inPropertyHandle);
+ if (theInvalidImageBinding)
+ m_Row->RemoveChildRow(theInvalidImageBinding);
+ }
+ theHandled = true;
+ break;
+ }
+ }
+ }
+ if (!theHandled)
+ CUICDMTimelineItemBinding::OnPropertyChanged(inPropertyHandle);
+}
+
+void CMaterialTimelineItemBinding::OnPropertyLinked(CUICDMPropertyHandle inPropertyHandle)
+{
+ bool theHandled = false;
+ if (m_Row) {
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ CClientDataModelBridge *theBridge =
+ m_TransMgr->GetStudioSystem()->GetClientDataModelBridge();
+ UICDM::TCharStr thePropertyName = thePropertySystem->GetName(inPropertyHandle);
+ size_t theSlotCount = m_ImageNameFormalNamePairs.size();
+ for (size_t theSlotIndex = 0; theSlotIndex < theSlotCount; ++theSlotIndex) {
+ UICDM::TCharStr thePropName = std::get<0>(m_ImageNameFormalNamePairs[theSlotIndex]);
+ if (thePropertyName == thePropName) {
+ // Refresh image child row by delete and recreate
+ CImageTimelineItemBinding *theInvalidImageBinding =
+ FindImageBindingByProperty(m_Row, inPropertyHandle);
+ if (theInvalidImageBinding)
+ m_Row->RemoveChildRow(theInvalidImageBinding);
+
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, thePropName)) {
+ ITimelineItemBinding *theNextImageBinding = nullptr;
+ // Determine if this is inserted somewhere in the existing list.
+ for (size_t theNextImage = theSlotIndex + 1; theNextImage < theSlotCount;
+ ++theNextImage) {
+ UICDM::TCharStr theTempName =
+ std::get<0>(m_ImageNameFormalNamePairs[theNextImage]);
+ if (ImageSlotIsFilled(thePropertySystem, m_DataHandle, theTempName)) {
+ CUICDMPropertyHandle theNextImageProperty =
+ theBridge->GetAggregateInstancePropertyByName(m_DataHandle,
+ theTempName);
+ theNextImageBinding =
+ m_TransMgr->GetBinding(GetImage(theNextImageProperty));
+ break;
+ }
+ }
+ m_Row->AddChildRow(
+ GetOrCreateImageBinding(
+ inPropertyHandle,
+ std::get<1>(m_ImageNameFormalNamePairs[theSlotIndex]).wide_str()),
+ theNextImageBinding);
+ }
+
+ theHandled = true;
+ break;
+ }
+ }
+ }
+ if (!theHandled)
+ CUICDMTimelineItemBinding::OnPropertyLinked(inPropertyHandle);
+}
+
+UICDM::CUICDMInstanceHandle
+CMaterialTimelineItemBinding::GetImage(UICDM::CUICDMPropertyHandle inPropertyHandle)
+{
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ SValue theImageValue;
+ thePropertySystem->GetInstancePropertyValue(m_DataHandle, inPropertyHandle, theImageValue);
+ SLong4 theImageLong4 = UICDM::get<SLong4>(theImageValue);
+ return m_TransMgr->GetStudioSystem()->GetClientDataModelBridge()->GetImageInstanceByGUID(
+ theImageLong4);
+}
+
+ITimelineItemBinding *
+CMaterialTimelineItemBinding::GetOrCreateImageBinding(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ const wchar_t *inName)
+{
+ UICDM::CUICDMInstanceHandle theImageInstance = GetImage(inPropertyHandle);
+ ITimelineItemBinding *theImageTimelineRow = m_TransMgr->GetBinding(theImageInstance);
+ if (!theImageTimelineRow) // create
+ {
+ theImageTimelineRow = m_TransMgr->GetOrCreate(theImageInstance);
+ // Set the name, by spec: the nice name.
+ theImageTimelineRow->GetTimelineItem()->SetName(inName);
+ CImageTimelineItemBinding *theImageBinding =
+ dynamic_cast<CImageTimelineItemBinding *>(theImageTimelineRow);
+ if (theImageBinding)
+ theImageBinding->SetPropertyHandle(inPropertyHandle);
+ }
+ return theImageTimelineRow;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.h
new file mode 100644
index 00000000..e1c2188d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/MaterialTimelineItemBinding.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_MATERIAL_TIMELINEITEM_BINDING_H
+#define INCLUDED_MATERIAL_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+#include "UICDMDataTypes.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+class CBaseStateRow;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Material type
+ */
+class CMaterialTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public: // Types
+ typedef std::tuple<UICDM::TCharStr, UICDM::TCharStr> TNameFormalNamePair;
+ typedef std::vector<TNameFormalNamePair> TNameFormalNamePairList;
+
+protected: // Members
+ TNameFormalNamePairList m_ImageNameFormalNamePairs;
+
+public: // Construction
+ CMaterialTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ virtual ~CMaterialTimelineItemBinding();
+
+public: // CUICDMTimelineItemBinding
+ ITimelineTimebar *GetTimebar() override;
+ EStudioObjectType GetObjectType() const override;
+ bool ShowToggleControls() const override;
+ // Hierarchy
+ long GetChildrenCount() override;
+ ITimelineItemBinding *GetChild(long inIndex) override;
+ void OnAddChild(UICDM::CUICDMInstanceHandle inInstance) override;
+ // Event callback
+ void OnPropertyChanged(UICDM::CUICDMPropertyHandle inPropertyHandle) override;
+ void OnPropertyLinked(UICDM::CUICDMPropertyHandle inPropertyHandle) override;
+
+protected:
+ UICDM::CUICDMInstanceHandle GetImage(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ ITimelineItemBinding *GetOrCreateImageBinding(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ const wchar_t *inName);
+};
+
+#endif // INCLUDED_MATERIAL_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.cpp
new file mode 100644
index 00000000..374c123b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "OffsetKeyframesCommandHelper.h"
+#include "Core.h"
+
+// Data model specific
+#include "IDoc.h"
+#include "CmdDataModelChangeKeyframe.h"
+#include "IDocumentEditor.h"
+
+#include "UICDMTimelineKeyframe.h" //TODO: remove once we resolve the precision issue
+
+using namespace UICDM;
+
+COffsetKeyframesCommandHelper::COffsetKeyframesCommandHelper(CDoc &inDoc)
+ : Q3DStudio::CUpdateableDocumentEditor(inDoc)
+ , m_Doc(inDoc)
+{
+}
+
+COffsetKeyframesCommandHelper::~COffsetKeyframesCommandHelper()
+{
+ Finalize();
+}
+
+//@param inTime time in millisecs
+void COffsetKeyframesCommandHelper::SetCommandTime(UICDM::CUICDMKeyframeHandle inKeyframe,
+ long inTime)
+{
+ // The UICDM system will take care of merging these under the hood.
+ ENSURE_EDITOR(L"Set Keyframe Time").SetKeyframeTime(inKeyframe, inTime);
+}
+
+// equivalent to commit (onmouseup)
+void COffsetKeyframesCommandHelper::Finalize()
+{
+ CommitEditor();
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.h
new file mode 100644
index 00000000..3a58e672
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/OffsetKeyframesCommandHelper.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_OFFSET_KEYFRAMES_COMMAND_HELPER_H
+#define INCLUDED_OFFSET_KEYFRAMES_COMMAND_HELPER_H 1
+
+#pragma once
+
+// Data model
+#include "UICDMHandles.h"
+#include "IDocumentEditor.h"
+
+namespace Q3DStudio {
+class IDocumentEditor;
+}
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CCmdDataModelSetKeyframeTime;
+
+class COffsetKeyframesCommandHelper : public Q3DStudio::CUpdateableDocumentEditor
+{
+protected:
+ CDoc &m_Doc;
+
+public:
+ COffsetKeyframesCommandHelper(CDoc &inDoc);
+ ~COffsetKeyframesCommandHelper();
+
+ void SetCommandTime(UICDM::CUICDMKeyframeHandle inKeyframe, long inTime);
+ void Finalize();
+ void Rollback() { RollbackEditor(); }
+};
+
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/PasteKeyframesCommandHelper.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/PasteKeyframesCommandHelper.h
new file mode 100644
index 00000000..130f6d20
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/PasteKeyframesCommandHelper.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PASTE_KEYFRAME_COMMAND_HELPER_H
+#define INCLUDED_PASTE_KEYFRAME_COMMAND_HELPER_H 1
+
+#pragma once
+
+//==============================================================================
+// Include
+//==============================================================================
+#include "CmdDataModelInsertKeyframe.h"
+#include "UICDMPropertyDefinition.h"
+#include "UICDMDataCore.h"
+
+// This caches all copied keyframes' time and data, for a paste action.
+// This has to deal with the actual data and not keyframe handles, because a prior Cut can
+// invalidate those handles.
+class CPasteKeyframeCommandHelper
+{
+protected:
+ typedef std::vector<CCmdDataModelInsertKeyframe::STimeKeyframeData> TCopiedKeyframeList;
+ TCopiedKeyframeList m_CopiedKeyframeList;
+
+public: // Construction
+ CPasteKeyframeCommandHelper() {}
+ ~CPasteKeyframeCommandHelper() {}
+
+ // inTime should be relative to the earliest keyframe time in this list
+ void AddKeyframeData(UICDM::CUICDMPropertyHandle inProperty, float inKeyframeTime,
+ UICDM::SGetOrSetKeyframeInfo *inInfos, size_t inInfoCount)
+ {
+ m_CopiedKeyframeList.push_back(CCmdDataModelInsertKeyframe::STimeKeyframeData(
+ inProperty, inKeyframeTime, inInfos, inInfoCount));
+ }
+
+ bool HasCopiedKeyframes() const { return !m_CopiedKeyframeList.empty(); }
+
+ // Triggered by a "Paste Keyframe" action
+ // Note: The logic is based on what the Animation Manager in the old system used to do.
+ // 1. The condition for paste to occur is that the property name matches.
+ // The old data model has a limitation that if the destination property is a linked property,
+ // the source has to come from the same instance, most likely a easy way out than to deal with
+ // with having to 'sync' all linked animation tracks.
+ // but that is not an issue in the new data model.
+ //
+ // 2. The first pasted keyframe is at current view time and the rest are offset accordingly.
+ CCmdDataModelInsertKeyframe *GetCommand(CDoc *inDoc, long inTimeOffsetInMilliseconds,
+ UICDM::CUICDMInstanceHandle inTargetInstance)
+ {
+ using namespace UICDM;
+
+ CCmdDataModelInsertKeyframe *theInsertKeyframesCommand = nullptr;
+ TCopiedKeyframeList::iterator theIter = m_CopiedKeyframeList.begin();
+ UICDM::IPropertySystem *thePropertySystem = inDoc->GetStudioSystem()->GetPropertySystem();
+ CClientDataModelBridge *theBridge = inDoc->GetStudioSystem()->GetClientDataModelBridge();
+
+ for (; theIter != m_CopiedKeyframeList.end(); ++theIter) {
+ TCharStr thePropertyName = thePropertySystem->GetName(theIter->m_Property);
+ DataModelDataType::Value thePropertyType =
+ thePropertySystem->GetDataType(theIter->m_Property);
+ CUICDMPropertyHandle theTargetPropertyHandle =
+ theBridge->GetAggregateInstancePropertyByName(inTargetInstance, thePropertyName);
+ if (theTargetPropertyHandle.Valid()) // property exists on target
+ {
+ // sanity check for type match
+ DataModelDataType::Value theTargetPropertyType =
+ thePropertySystem->GetDataType(theTargetPropertyHandle);
+ if (theTargetPropertyType == thePropertyType) {
+ // 2. Offset keyframe time by current view time
+ double milliseconds = theIter->m_KeyframeTime * 1000.0;
+ double theTimeInMilliseconds = milliseconds + inTimeOffsetInMilliseconds;
+ float theTimeInSeconds = static_cast<float>(theTimeInMilliseconds / 1000.0);
+
+ if (!theInsertKeyframesCommand)
+ theInsertKeyframesCommand = new CCmdDataModelInsertKeyframe(
+ inDoc, inTargetInstance, theTargetPropertyHandle, theTimeInSeconds,
+ theIter->m_Infos, theIter->m_ValidInfoCount);
+ else
+ theInsertKeyframesCommand->AddKeyframeData(
+ theTargetPropertyHandle, theTimeInSeconds, theIter->m_Infos,
+ theIter->m_ValidInfoCount);
+ }
+ }
+ }
+ return theInsertKeyframesCommand;
+ }
+
+ void Clear() { m_CopiedKeyframeList.clear(); }
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.cpp
new file mode 100644
index 00000000..286a899f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "PathAnchorPointTimelineItemBinding.h"
+#include "EmptyTimelineTimebar.h"
+
+using namespace UICDM;
+
+CPathAnchorPointTimelineItemBinding::CPathAnchorPointTimelineItemBinding(
+ CTimelineTranslationManager *inMgr, CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+{
+}
+
+ITimelineTimebar *CPathAnchorPointTimelineItemBinding::GetTimebar()
+{
+ return new CEmptyTimelineTimebar();
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.h
new file mode 100644
index 00000000..2bc3f8d6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathAnchorPointTimelineItemBinding.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef PATH_ANCHOR_POINT_TIMELINE_ITEM_BINDING
+#define PATH_ANCHOR_POINT_TIMELINE_ITEM_BINDING
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+#include "UICDMDataTypes.h"
+#include <boost/tuple/tuple.hpp>
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+class CBaseStateRow;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Material type
+ */
+class CPathAnchorPointTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public: // Construction
+ CPathAnchorPointTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+
+ bool ShowToggleControls() const override { return false; }
+ bool IsVisible() const override { return true; }
+ void SetVisible(bool) override {}
+ ITimelineTimebar *GetTimebar() override;
+ bool IsLocked() const override { return false; }
+ void SetLocked(bool) override {}
+ bool IsShy() const override { return false; }
+ void SetShy(bool) override {}
+ Q3DStudio::CString GetName() const override { return L"Anchor Point"; }
+ void SetName(const Q3DStudio::CString &) override {}
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.cpp
new file mode 100644
index 00000000..3fbed168
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "PathTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "Doc.h"
+
+bool CPathTimelineItemBinding::IsExternalizeable()
+{
+ // If this path has subpath children, then it is externalizeable.
+ return m_TransMgr->GetDoc()->GetDocumentReader().IsPathExternalizeable(GetInstance());
+}
+
+void CPathTimelineItemBinding::Externalize()
+{
+ Q3DStudio::ScopedDocumentEditor(*m_TransMgr->GetDoc(), L"Externalize Path Buffer", __FILE__,
+ __LINE__)
+ ->ExternalizePath(GetInstance());
+}
+
+bool CPathTimelineItemBinding::IsInternalizeable()
+{
+ // If this path has a sourcepath, then it might be internalizeable
+ return m_TransMgr->GetDoc()->GetDocumentReader().IsPathInternalizeable(GetInstance());
+}
+
+void CPathTimelineItemBinding::Internalize()
+{
+ Q3DStudio::ScopedDocumentEditor(*m_TransMgr->GetDoc(), L"Internalize Path Buffer", __FILE__,
+ __LINE__)
+ ->InternalizePath(GetInstance());
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.h
new file mode 100644
index 00000000..1be4b2e1
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/PathTimelineItemBinding.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef PATH_TIMELINE_ITEM_BINDING
+#define PATH_TIMELINE_ITEM_BINDING
+#pragma once
+#include "UICDMTimelineItemBinding.h"
+#include "UICDMDataTypes.h"
+#include <boost/tuple/tuple.hpp>
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+class CBaseStateRow;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Material type
+ */
+class CPathTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public: // Construction
+ CPathTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr, inDataHandle)
+ {
+ }
+
+ bool IsExternalizeable() override;
+ void Externalize() override;
+ bool IsInternalizeable() override;
+ void Internalize() override;
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.cpp
new file mode 100644
index 00000000..ce381f44
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SlideTimelineItemBinding.h"
+#include "BaseStateRow.h"
+
+// Data model specific
+#include "Doc.h"
+#include "CmdGeneric.h"
+#include "EmptyTimelineTimebar.h"
+
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+#include "ClientDataModelBridge.h"
+
+using namespace UICDM;
+
+CSlideTimelineItemBinding::CSlideTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : CUICDMTimelineItemBinding(inMgr)
+{
+ UICDM::CUICDMSlideHandle theSlideHandle =
+ m_StudioSystem->GetSlideSystem()->GetSlideByInstance(inDataHandle);
+
+ // Get the owning component of m_SlideHandle.
+ // This should return CAsset OBJTYPE_SCENE or OBJTYPE_COMPONENT.
+ UICDM::CUICDMInstanceHandle theInstance =
+ m_StudioSystem->GetClientDataModelBridge()->GetOwningComponentInstance(theSlideHandle);
+ SetInstanceHandle(theInstance);
+
+ // Listen to change on Asset name
+ IStudioFullSystemSignalProvider *theEngine = m_StudioSystem->GetFullSystemSignalProvider();
+ std::function<void(CUICDMInstanceHandle, CUICDMPropertyHandle)> theSetter(
+ std::bind(&CSlideTimelineItemBinding::OnPropertyChanged, this, std::placeholders::_2));
+ m_Connection = theEngine->ConnectInstancePropertyValue(
+ std::bind(UICDM::MaybackCallbackInstancePropertyValue<std::function<void(
+ CUICDMInstanceHandle, CUICDMPropertyHandle)>>,
+ std::placeholders::_1, std::placeholders::_2, theInstance,
+ m_StudioSystem->GetClientDataModelBridge()->GetNameProperty(), theSetter));
+}
+
+ITimelineTimebar *CSlideTimelineItemBinding::GetTimebar()
+{ // No timebars on slides
+ return new CEmptyTimelineTimebar();
+}
+
+void CSlideTimelineItemBinding::SetName(const Q3DStudio::CString & /*inName*/)
+{
+ // Do nothing because name is read only
+}
+
+void CSlideTimelineItemBinding::Bind(CBaseStateRow *inRow)
+{
+ CUICDMTimelineItemBinding::Bind(inRow);
+ GetRow()->SetNameReadOnly(true);
+}
+
+bool CSlideTimelineItemBinding::IsValidTransaction(EUserTransaction inTransaction)
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ switch (inTransaction) {
+ // Disable the following context menus
+ case EUserTransaction_Rename:
+ case EUserTransaction_MakeComponent:
+ case EUserTransaction_EditComponent:
+ return false;
+ }
+
+ return CUICDMTimelineItemBinding::IsValidTransaction(inTransaction);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.h
new file mode 100644
index 00000000..759fdfb9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/SlideTimelineItemBinding.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SLIDE_TIMELINEITEM_BINDING_H
+#define INCLUDED_SLIDE_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "UICDMTimelineItemBinding.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class ITimelineItem;
+class CTimelineTranslationManager;
+class CBaseStateRow;
+
+//=============================================================================
+/**
+ * Binding to a UICDM object of Slide type
+ */
+class CSlideTimelineItemBinding : public CUICDMTimelineItemBinding
+{
+public:
+ CSlideTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ ~CSlideTimelineItemBinding() {}
+
+ // CUICDMTimelineItemBinding
+ ITimelineTimebar *GetTimebar() override;
+ void SetName(const Q3DStudio::CString &inName) override;
+ void Bind(CBaseStateRow *inRow) override;
+ bool IsValidTransaction(EUserTransaction inTransaction) override;
+
+ // No properties
+ long GetPropertyCount() override { return 0; }
+ ITimelineItemProperty *GetProperty(long) override { return nullptr; }
+ void LoadProperties() override {}
+
+ // Eye/Lock toggles are not applicable
+ bool ShowToggleControls() const override { return false; }
+ bool IsLockedEnabled() const override { return false; }
+ bool IsVisibleEnabled() const override { return false; }
+
+ // Shy, Locked, Visible are not applicable
+ bool IsShy() const override { return false; }
+ void SetShy(bool) override {}
+ bool IsLocked() const override { return false; }
+ void SetLocked(bool) override {}
+ bool IsVisible() const override { return true; }
+ void SetVisible(bool) override {}
+
+ // Keyframes, not applicable to a Slide
+ void InsertKeyframe() override {}
+ void DeleteAllChannelKeyframes() override {}
+ long GetKeyframeCount() const override { return 0; }
+ IKeyframe *GetKeyframeByTime(long) const override { return nullptr; }
+ IKeyframe *GetKeyframeByIndex(long) const override { return nullptr; }
+ long OffsetSelectedKeyframes(long) override { return 0; }
+ void CommitChangedKeyframes() override {}
+ void OnEditKeyframeTime(long, long) override {}
+ // IKeyframeSelector
+ void SelectKeyframes(bool, long inTime = -1) override { Q_UNUSED(inTime); }
+
+ // Keyframe manipulation, not applicable
+ void UIRefreshPropertyKeyframe(long inOffset) override { Q_UNUSED(inOffset); }
+ bool HasDynamicKeyframes(long inTime) override
+ {
+ Q_UNUSED(inTime);
+ return false;
+ }
+ void SetDynamicKeyframes(long inTime, bool inDynamic) override
+ {
+ Q_UNUSED(inTime);
+ Q_UNUSED(inDynamic);
+ }
+ void DoSelectKeyframes(bool inSelected, long inTime, bool inUpdateUI) override
+ {
+ Q_UNUSED(inSelected);
+ Q_UNUSED(inTime);
+ Q_UNUSED(inUpdateUI);
+ }
+ void OnPropertySelection(long inTime) override { Q_UNUSED(inTime); }
+
+protected:
+ std::shared_ptr<UICDM::ISignalConnection>
+ m_Connection; // Callback when the Asset name changes
+
+ bool AmITimeParent() const override { return true; }
+};
+
+#endif // INCLUDED_SLIDE_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.cpp
new file mode 100644
index 00000000..4bd1fc01
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "TimelineBreadCrumbProvider.h"
+#include "Core.h"
+
+// Link to data model
+#include "Doc.h"
+#include "StudioApp.h"
+#include "Cmd.h"
+#include "ResourceCache.h"
+#include "Strings.h"
+#include "StringLoader.h"
+#include "CColor.h"
+
+#include "ClientDataModelBridge.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+#include "CmdActivateSlide.h"
+
+using namespace UICDM;
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimelineBreadCrumbProvider::CTimelineBreadCrumbProvider(CDoc *inDoc)
+ : m_Doc(inDoc)
+{
+}
+
+//=============================================================================
+/**
+ */
+CTimelineBreadCrumbProvider::~CTimelineBreadCrumbProvider()
+{
+}
+
+//=============================================================================
+/**
+ * determine the color and text string for this breadcrumb
+ */
+static inline void FillBreadCrumb(SBreadCrumb &outBreadCrumb,
+ UICDM::CUICDMInstanceHandle inInstance, CDoc *inDoc)
+{
+ // Get the MasterSlide Handle associated with inAsset
+ CClientDataModelBridge *theBridge = inDoc->GetStudioSystem()->GetClientDataModelBridge();
+ ISlideSystem *theSlideSystem = inDoc->GetStudioSystem()->GetSlideSystem();
+ Q3DStudio::CId theId = theBridge->GetGUID(inInstance);
+ UICDM::CUICDMSlideHandle theMasterSlide =
+ theSlideSystem->GetMasterSlideByComponentGuid(GuidtoSLong4(theId));
+ ASSERT(theMasterSlide.Valid()); // it should be valid because inAsset should be OBJTYPE_SCENE or
+ // non-library OBJTYPE_COMPONENT
+
+ // Get the active slide index of the master slide. Master Slide always has index 0
+ long theActiveIndex = theSlideSystem->GetActiveSlideIndex(theMasterSlide);
+ bool theIsMaster = (theActiveIndex == 0);
+
+ // Determine the color
+ outBreadCrumb.m_Color =
+ theIsMaster ? CColor(0, 0, 255) : CColor(0, 0, 0); // blue for master, black otherwise
+
+ // Determine the text string
+ outBreadCrumb.m_String = theBridge->GetName(inInstance).toQString();
+ outBreadCrumb.m_String += " (";
+ if (theIsMaster)
+ outBreadCrumb.m_String += ::LoadResourceString(IDS_OBJTYPE_MASTER).toQString();
+ else {
+ CUICDMSlideHandle theActiveSlide =
+ theSlideSystem->GetSlideByIndex(theMasterSlide, theActiveIndex);
+ CUICDMInstanceHandle theInstanceHandle = theSlideSystem->GetSlideInstance(theActiveSlide);
+ ASSERT(theInstanceHandle.Valid());
+ outBreadCrumb.m_String += theBridge->GetName(theInstanceHandle).toQString();
+ }
+ outBreadCrumb.m_String += ")";
+}
+
+//=============================================================================
+/**
+ * return the trail of breadcrumb.
+ * This constructs a list of the "time context tree" from Scene down to the current active time
+ * context.
+ * @param inRefresh true to refresh the list, false to get existing.
+ */
+CTimelineBreadCrumbProvider::TTrailList
+CTimelineBreadCrumbProvider::GetTrail(bool inRefresh /*= true */)
+{
+ if (inRefresh)
+ RefreshSlideList();
+
+ TTrailList theList;
+ for (size_t theIndex = 0; theIndex < m_BreadCrumbList.size(); ++theIndex) {
+ SBreadCrumb theBreadCrumb;
+ FillBreadCrumb(theBreadCrumb, m_BreadCrumbList[theIndex], m_Doc);
+ theList.push_back(theBreadCrumb);
+ }
+ return theList;
+}
+
+//=============================================================================
+/**
+ * switch current time context to the one 'represented' by the breadcrumbs.
+ * @param inTrailIndex index into the trail list
+ */
+void CTimelineBreadCrumbProvider::OnBreadCrumbClicked(long inTrailIndex)
+{
+ if (inTrailIndex >= 0 && inTrailIndex < (long)m_BreadCrumbList.size()) {
+ CCmdActivateSlide *theCmd = new CCmdActivateSlide(m_Doc, m_BreadCrumbList[inTrailIndex]);
+ theCmd->SetForceRefresh(false);
+ m_Doc->GetCore()->ExecuteCommand(theCmd, false);
+ }
+}
+
+QPixmap CTimelineBreadCrumbProvider::GetRootImage() const
+{
+ return CResourceCache::GetInstance()->GetBitmap("breadcrumb_component_scene.png");
+}
+
+QPixmap CTimelineBreadCrumbProvider::GetBreadCrumbImage() const
+{
+ return CResourceCache::GetInstance()->GetBitmap("breadcrumb_component_button.png");
+}
+
+QPixmap CTimelineBreadCrumbProvider::GetSeparatorImage() const
+{
+ return CResourceCache::GetInstance()->GetBitmap("breadcrumb_component_colon_button.png");
+}
+
+QPixmap CTimelineBreadCrumbProvider::GetActiveBreadCrumbImage() const
+{
+ return CResourceCache::GetInstance()->GetBitmap("breadcrumb_component_grey_button.png");
+}
+
+//=============================================================================
+/**
+ * Called when active time context is changed.
+ */
+void CTimelineBreadCrumbProvider::RefreshSlideList()
+{
+ ClearSlideList();
+
+ UICDM::CUICDMInstanceHandle theActiveRoot = m_Doc->GetActiveRootInstance();
+ if (!theActiveRoot.Valid())
+ return;
+ FillSlideList(theActiveRoot);
+}
+
+//=============================================================================
+/**
+ * Callback that inAsset has its name changed, fire off a signal to the UI control.
+ * All the assets' signals are connected to this object and we'll let the UI control check iterate
+ * through the list for changes and refresh.
+ * Alternative we can set up additional classes that listens to specific assets and only the asset
+ * affected refreshed. the former is easier for now.
+ */
+void CTimelineBreadCrumbProvider::OnNameDirty()
+{
+ SigBreadCrumbUpdate();
+}
+
+void CTimelineBreadCrumbProvider::ClearSlideList()
+{
+ m_Connections.clear();
+ m_BreadCrumbList.clear();
+}
+
+//=============================================================================
+/**
+ * This will recurse up the time context tree, so that we can fill the list in a top-down (i.e
+ * Scene) first manner
+ */
+void CTimelineBreadCrumbProvider::FillSlideList(UICDM::CUICDMInstanceHandle inInstance)
+{
+ if (!inInstance.Valid())
+ return;
+
+ CClientDataModelBridge *theBridge = m_Doc->GetStudioSystem()->GetClientDataModelBridge();
+ ISlideSystem *theSlideSystem = m_Doc->GetStudioSystem()->GetSlideSystem();
+ Q3DStudio::CId theId = theBridge->GetGUID(inInstance);
+
+ // Recurse
+ FillSlideList(theBridge->GetParentComponent(inInstance));
+
+ m_BreadCrumbList.push_back(inInstance);
+
+ CUICDMPropertyHandle theNameProp =
+ m_Doc->GetStudioSystem()->GetClientDataModelBridge()->GetNameProperty();
+ IStudioFullSystemSignalProvider *theEngine =
+ m_Doc->GetStudioSystem()->GetFullSystemSignalProvider();
+ std::function<void(CUICDMInstanceHandle, CUICDMPropertyHandle)> theSetter(
+ std::bind(&CTimelineBreadCrumbProvider::OnNameDirty, this));
+
+ // Listen to name changes on the Asset
+ m_Connections.push_back(theEngine->ConnectInstancePropertyValue(
+ std::bind(UICDM::MaybackCallbackInstancePropertyValue<std::function<void(
+ CUICDMInstanceHandle, CUICDMPropertyHandle)>>,
+ std::placeholders::_1, std::placeholders::_2, inInstance, theNameProp, theSetter)));
+
+ // Listen to name changes on the non-master Slides
+ UICDM::CUICDMSlideHandle theMasterSlide =
+ theSlideSystem->GetMasterSlideByComponentGuid(GuidtoSLong4(theId));
+ long theSlideCount = (long)theSlideSystem->GetSlideCount(theMasterSlide);
+
+ for (long theIndex = 1; theIndex < theSlideCount; ++theIndex) {
+ CUICDMSlideHandle theSlide = theSlideSystem->GetSlideByIndex(theMasterSlide, theIndex);
+ CUICDMInstanceHandle theSlideInstance = theSlideSystem->GetSlideInstance(theSlide);
+ m_Connections.push_back(theEngine->ConnectInstancePropertyValue(
+ std::bind(UICDM::MaybackCallbackInstancePropertyValue<std::function<void(
+ CUICDMInstanceHandle, CUICDMPropertyHandle)>>,
+ std::placeholders::_1, std::placeholders::_2, theSlideInstance, theNameProp, theSetter)));
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.h
new file mode 100644
index 00000000..d71b57b9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineBreadCrumbProvider.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BREADCRUMBPROVIDER_H
+#define INCLUDED_BREADCRUMBPROVIDER_H 1
+
+#pragma once
+
+#include "IBreadCrumbProvider.h"
+#include "UICDMSignals.h"
+
+// Link to data model
+class CDoc;
+class CTimelineBreadCrumbProvider;
+
+//=============================================================================
+/**
+ * Bread crumb provider for displaying a trail of time contexts
+ */
+class CTimelineBreadCrumbProvider : public IBreadCrumbProvider
+{
+public:
+ CTimelineBreadCrumbProvider(CDoc *inDoc);
+ virtual ~CTimelineBreadCrumbProvider();
+
+ TTrailList GetTrail(bool inRefresh = true) override;
+ void OnBreadCrumbClicked(long inTrailIndex) override;
+
+ QPixmap GetRootImage() const override;
+ QPixmap GetBreadCrumbImage() const override;
+ QPixmap GetSeparatorImage() const override;
+ QPixmap GetActiveBreadCrumbImage() const override;
+
+ void RefreshSlideList();
+ void OnNameDirty();
+
+protected:
+ void ClearSlideList();
+ void FillSlideList(UICDM::CUICDMInstanceHandle inInstance);
+
+protected:
+ std::vector<UICDM::CUICDMInstanceHandle> m_BreadCrumbList;
+ CDoc *m_Doc;
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>>
+ m_Connections; /// connections to the UICDM
+};
+
+#endif // INCLUDED_BREADCRUMBPROVIDER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.cpp
new file mode 100644
index 00000000..cc28b0cc
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TimelineTranslationManager.h"
+#include "SlideTimelineItemBinding.h"
+#include "GroupTimelineItemBinding.h"
+#include "BehaviorTimelineItemBinding.h"
+#include "MaterialTimelineItemBinding.h"
+#include "ImageTimelineItemBinding.h"
+#include "PathAnchorPointTimelineItemBinding.h"
+#include "PathTimelineItemBinding.h"
+#include "LayerTimelineItemBinding.h"
+#include "KeyframesManager.h"
+#include "TimelineBreadCrumbProvider.h"
+#include "BaseStateRow.h"
+#include "PropertyRow.h"
+#include "IDoc.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+#include "UICDMSignals.h"
+
+// Link to Data model
+#include "ClientDataModelBridge.h"
+#include "UICDMDataCore.h"
+#include "Doc.h" //Because we need to access Client Data Model Bridge
+#include "StudioApp.h"
+#include "Core.h"
+
+using namespace UICDM;
+
+CTimelineTranslationManager::CTimelineTranslationManager()
+{
+ m_KeyframesManager = new CKeyframesManager(this);
+ m_BreadCrumbProvider = new CTimelineBreadCrumbProvider(g_StudioApp.GetCore()->GetDoc());
+}
+
+CTimelineTranslationManager::~CTimelineTranslationManager()
+{
+ // clean up all bindings
+ Clear();
+ m_Connections.clear();
+ delete m_KeyframesManager;
+ delete m_BreadCrumbProvider;
+}
+
+ITimelineItemBinding *CTimelineTranslationManager::GetOrCreate(CUICDMInstanceHandle inInstance)
+{
+ ITimelineItemBinding *theBinding = GetBinding(inInstance);
+ if (!theBinding) {
+ CUICDMTimelineItemBinding *theReturn = nullptr;
+ UICDM::IPropertySystem *thePropertySystem = GetStudioSystem()->GetPropertySystem();
+ CUICDMPropertyHandle theTypeProperty =
+ thePropertySystem->GetAggregateInstancePropertyByName(inInstance, L"type");
+
+ SValue theTypeValue;
+ thePropertySystem->GetInstancePropertyValue(inInstance, theTypeProperty, theTypeValue);
+
+ std::wstring theWideTypeString(UICDM::get<TDataStrPtr>(theTypeValue)->GetData());
+
+ if (theWideTypeString == L"Material" || theWideTypeString == L"CustomMaterial"
+ || theWideTypeString == L"ReferencedMaterial")
+ theReturn = new CMaterialTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Image")
+ theReturn = new CImageTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Group" || theWideTypeString == L"Component")
+ theReturn = new CGroupTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Behavior")
+ theReturn = new CBehaviorTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Slide")
+ theReturn = new CSlideTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"PathAnchorPoint")
+ theReturn = new CPathAnchorPointTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Path")
+ theReturn = new CPathTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Layer")
+ theReturn = new CLayerTimelineItemBinding(this, inInstance);
+ else if (theWideTypeString == L"Model" || theWideTypeString == L"Text"
+ || theWideTypeString == L"Camera" || theWideTypeString == L"Effect"
+ || theWideTypeString == L"Light" || theWideTypeString == L"RenderPlugin"
+ || theWideTypeString == L"Alias" || theWideTypeString == L"SubPath")
+ theReturn = new CUICDMTimelineItemBinding(this, inInstance);
+ else {
+ // Add support for additional UICDM types here.
+ ASSERT(0);
+ }
+
+ m_InstanceHandleBindingMap.insert(
+ std::make_pair(theReturn->GetInstanceHandle(), theReturn));
+ m_InstanceHandleExpandedMap.insert(std::make_pair(theReturn->GetInstanceHandle(), false));
+ theBinding = theReturn;
+ }
+
+ return theBinding;
+}
+
+//==============================================================================
+/**
+ * Create a new CPropertyRow that maps to this ITimelineItemProperty.
+ * The caller is assumed to have ensured that this is only called once per property binding.
+ */
+void CTimelineTranslationManager::CreateNewPropertyRow(
+ ITimelineItemProperty *inTimelineItemPropertyBinding, CBaseStateRow *inParentRow,
+ CPropertyRow *inNextRow)
+{
+ if (!inParentRow || !inTimelineItemPropertyBinding)
+ return;
+
+ CPropertyRow *theNewRow = new CPropertyRow(inTimelineItemPropertyBinding);
+ inParentRow->AddPropertyRow(theNewRow, inNextRow);
+ inTimelineItemPropertyBinding->Bind(theNewRow);
+}
+
+//==============================================================================
+/**
+ * Does the reverse of CreateNewPropertyRow, when a property has been de-animated.
+ */
+void CTimelineTranslationManager::RemovePropertyRow(
+ ITimelineItemProperty *inTimelineItemPropertyBinding)
+{
+ CPropertyRow *theRow = nullptr;
+ if (!inTimelineItemPropertyBinding
+ || (theRow = inTimelineItemPropertyBinding->GetRow()) == nullptr)
+ return;
+
+ CBaseStateRow *theParentRow = theRow->GetParentRow();
+ if (theParentRow) {
+ inTimelineItemPropertyBinding->Release();
+ theParentRow->RemovePropertyRow(theRow); // this implicitly delete the row
+ }
+}
+
+//==============================================================================
+/**
+ * Clear all bindings, typically when a presentation is closed.
+ */
+void CTimelineTranslationManager::Clear()
+{
+ // clean up all bindings
+ m_InstanceHandleBindingMap.clear();
+}
+
+//==============================================================================
+/**
+ * Called when the associated UI is no longer valid.
+ */
+void CTimelineTranslationManager::Unregister(ITimelineItemBinding *inTimelineItem)
+{
+ // UICDM
+ bool theDeselectItem = false;
+ TInstanceHandleBindingMap::iterator theInstanceIter = m_InstanceHandleBindingMap.begin();
+ for (; theInstanceIter != m_InstanceHandleBindingMap.end(); ++theInstanceIter) {
+ if (theInstanceIter->second == inTimelineItem) {
+ // If this is the currently selected object and make sure that is cleared.
+ UICDM::CUICDMInstanceHandle theSelectedInstance =
+ g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance();
+ if (theSelectedInstance.Valid() && theSelectedInstance == theInstanceIter->first)
+ theDeselectItem = true;
+
+ m_InstanceHandleBindingMap.erase(theInstanceIter);
+ break;
+ }
+ }
+
+ delete inTimelineItem;
+}
+
+CKeyframesManager *CTimelineTranslationManager::GetKeyframesManager() const
+{
+ return m_KeyframesManager;
+}
+
+IBreadCrumbProvider *CTimelineTranslationManager::GetBreadCrumbProvider() const
+{
+ return m_BreadCrumbProvider;
+}
+
+CBaseStateRow *CTimelineTranslationManager::GetSelectedRow() const
+{
+ ITimelineItemBinding *theBinding = GetSelectedBinding();
+ if (theBinding)
+ return theBinding->GetRow();
+ return nullptr;
+}
+
+long CTimelineTranslationManager::GetCurrentViewTime() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetCurrentViewTime();
+}
+
+//==============================================================================
+/**
+ * @return the Binding object that corresponds to this instance.
+ */
+CUICDMTimelineItemBinding *
+CTimelineTranslationManager::GetBinding(CUICDMInstanceHandle inHandle) const
+{
+ TInstanceHandleBindingMap::const_iterator theIter = m_InstanceHandleBindingMap.find(inHandle);
+ if (theIter != m_InstanceHandleBindingMap.end())
+ return theIter->second;
+ return nullptr;
+}
+
+CUICDMTimelineItemBinding *CTimelineTranslationManager::GetSelectedBinding() const
+{
+ UICDM::CUICDMInstanceHandle theSelectedInstance =
+ g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance();
+ if (theSelectedInstance.Valid()) {
+ CUICDMTimelineItemBinding *theBinding = GetBinding(theSelectedInstance);
+ return theBinding;
+ }
+ return nullptr;
+}
+
+//==============================================================================
+/**
+ * Triggered from individual binding classes, to clear all keyframe selection
+ */
+void CTimelineTranslationManager::ClearKeyframeSelection()
+{
+ m_KeyframesManager->DeselectAllKeyframes();
+}
+
+//==============================================================================
+/**
+ * Set up callbacks for animation changes
+ */
+void CTimelineTranslationManager::OnNewPresentation()
+{
+ m_Connections.clear();
+ m_InstanceHandleExpandedMap.clear();
+
+ IStudioFullSystemSignalProvider *theSignalProvider =
+ g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider();
+ m_Connections.push_back(theSignalProvider->ConnectAnimationCreated(
+ std::bind(&CTimelineTranslationManager::OnAnimationCreated, this,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectAnimationDeleted(
+ std::bind(&CTimelineTranslationManager::OnAnimationDeleted, this,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectPropertyLinked(
+ std::bind(&CTimelineTranslationManager::OnPropertyLinked, this,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectPropertyUnlinked(
+ std::bind(&CTimelineTranslationManager::OnPropertyUnlinked, this,
+ std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectKeyframeInserted(
+ std::bind(&CTimelineTranslationManager::OnKeyframeInserted, this,
+ std::placeholders::_1, std::placeholders::_2)));
+ m_Connections.push_back(theSignalProvider->ConnectKeyframeErased(
+ std::bind(&CTimelineTranslationManager::OnKeyframeDeleted, this,
+ std::placeholders::_1, std::placeholders::_2)));
+ m_Connections.push_back(theSignalProvider->ConnectKeyframeUpdated(
+ std::bind(&CTimelineTranslationManager::OnKeyframeUpdated, this, std::placeholders::_1)));
+ m_Connections.push_back(theSignalProvider->ConnectInstancePropertyValue(
+ std::bind(&CTimelineTranslationManager::OnPropertyChanged, this,
+ std::placeholders::_1, std::placeholders::_2)));
+ m_Connections.push_back(theSignalProvider->ConnectFirstKeyframeDynamicSet(
+ std::bind(&CTimelineTranslationManager::OnDynamicKeyframeChanged, this,
+ std::placeholders::_1, std::placeholders::_2)));
+
+ m_Connections.push_back(theSignalProvider->ConnectInstanceCreated(
+ std::bind(&CTimelineTranslationManager::OnAssetCreated, this, std::placeholders::_1)));
+ m_Connections.push_back(theSignalProvider->ConnectInstanceDeleted(
+ std::bind(&CTimelineTranslationManager::OnAssetDeleted, this, std::placeholders::_1)));
+
+ m_Connections.push_back(theSignalProvider->ConnectActionCreated(
+ std::bind(&CTimelineTranslationManager::OnActionEvent, this,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectActionDeleted(
+ std::bind(&CTimelineTranslationManager::OnActionEvent, this,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
+
+ Q3DStudio::CGraph &theGraph(*g_StudioApp.GetCore()->GetDoc()->GetAssetGraph());
+ m_Connections.push_back(theGraph.ConnectChildAdded(
+ std::bind(&CTimelineTranslationManager::OnChildAdded, this,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theGraph.ConnectChildRemoved(
+ std::bind(&CTimelineTranslationManager::OnChildRemoved, this,
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
+ m_Connections.push_back(theGraph.ConnectChildMoved(
+ std::bind(&CTimelineTranslationManager::OnChildMoved, this, std::placeholders::_1,
+ std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)));
+}
+
+//==============================================================================
+/**
+ * Selection events on the old data model was triggered via signals on the actual objects.
+ * For the new data model, it would be via this OnSelectionChange event.
+ */
+void CTimelineTranslationManager::OnSelectionChange(Q3DStudio::SSelectedValue inNewSelectable)
+{
+ // Deselect all items
+ TInstanceHandleBindingMap::const_iterator theIter = m_InstanceHandleBindingMap.begin();
+ for (; theIter != m_InstanceHandleBindingMap.end(); ++theIter) {
+ ITimelineItemBinding *theBinding = theIter->second;
+ CBaseStateRow *theRow = theBinding->GetRow();
+ if (theRow)
+ theRow->OnSelected(false);
+ }
+
+ // Select new
+ if (inNewSelectable)
+ SetSelected(inNewSelectable, true);
+}
+
+CDoc *CTimelineTranslationManager::GetDoc() const
+{
+ return dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc());
+}
+
+CStudioSystem *CTimelineTranslationManager::GetStudioSystem() const
+{
+ // TODO: figure if we can just deal with IDoc instead of CDoc
+ return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+}
+
+void CTimelineTranslationManager::OnAnimationCreated(CUICDMInstanceHandle inInstance,
+ CUICDMPropertyHandle inProperty)
+{
+ CUICDMTimelineItemBinding *theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inInstance));
+ if (theTimelineBinding)
+ theTimelineBinding->AddPropertyRow(inProperty);
+}
+
+void CTimelineTranslationManager::OnAnimationDeleted(CUICDMInstanceHandle inInstance,
+ CUICDMPropertyHandle inProperty)
+{
+ CUICDMTimelineItemBinding *theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inInstance));
+ if (theTimelineBinding)
+ theTimelineBinding->RemovePropertyRow(inProperty);
+}
+
+void CTimelineTranslationManager::OnPropertyLinked(CUICDMInstanceHandle inInstance,
+ CUICDMPropertyHandle inProperty)
+{
+ CUICDMTimelineItemBinding *theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inInstance));
+ if (theTimelineBinding)
+ theTimelineBinding->OnPropertyLinked(inProperty);
+}
+
+void CTimelineTranslationManager::OnPropertyUnlinked(CUICDMInstanceHandle inInstance,
+ CUICDMPropertyHandle inProperty)
+{
+ OnPropertyLinked(inInstance, inProperty);
+}
+
+void CTimelineTranslationManager::RefreshKeyframe(CUICDMAnimationHandle inAnimation,
+ CUICDMKeyframeHandle inKeyframe,
+ ETimelineKeyframeTransaction inTransaction)
+{
+ CUICDMTimelineItemBinding *theTimelineBinding = nullptr;
+ if (GetStudioSystem()->GetAnimationCore()->AnimationValid(inAnimation)) {
+ SAnimationInfo theAnimationInfo =
+ GetStudioSystem()->GetAnimationCore()->GetAnimationInfo(inAnimation);
+ theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(theAnimationInfo.m_Instance));
+
+ if (theTimelineBinding)
+ theTimelineBinding->RefreshPropertyKeyframe(theAnimationInfo.m_Property, inKeyframe,
+ inTransaction);
+ }
+ // else, animation has been nuked, ignore this event, we'll get a AnimationDelete
+}
+
+void CTimelineTranslationManager::OnKeyframeInserted(CUICDMAnimationHandle inAnimation,
+ CUICDMKeyframeHandle inKeyframe)
+{
+ RefreshKeyframe(inAnimation, inKeyframe, ETimelineKeyframeTransaction_Add);
+}
+
+void CTimelineTranslationManager::OnKeyframeDeleted(CUICDMAnimationHandle inAnimation,
+ CUICDMKeyframeHandle inKeyframe)
+{
+ RefreshKeyframe(inAnimation, inKeyframe, ETimelineKeyframeTransaction_Delete);
+}
+
+void CTimelineTranslationManager::OnKeyframeUpdated(CUICDMKeyframeHandle inKeyframe)
+{
+ IAnimationCore *theAnimationCore = GetStudioSystem()->GetAnimationCore();
+ if (theAnimationCore->KeyframeValid(inKeyframe)) {
+ CUICDMAnimationHandle theAnimationHandle =
+ theAnimationCore->GetAnimationForKeyframe(inKeyframe);
+ RefreshKeyframe(theAnimationHandle, inKeyframe, ETimelineKeyframeTransaction_Update);
+ }
+ // else, keyframe has been nuked, ignore this event, we'll get a KeyframeDeleted
+}
+
+void CTimelineTranslationManager::OnPropertyChanged(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty)
+{
+ CUICDMTimelineItemBinding *theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inInstance));
+ if (theTimelineBinding)
+ theTimelineBinding->OnPropertyChanged(inProperty);
+}
+
+void CTimelineTranslationManager::OnDynamicKeyframeChanged(UICDM::CUICDMAnimationHandle inAnimation,
+ bool inDynamic)
+{
+ Q_UNUSED(inDynamic);
+
+ CUICDMTimelineItemBinding *theTimelineBinding = nullptr;
+ if (GetStudioSystem()->GetAnimationCore()->AnimationValid(inAnimation)) {
+ SAnimationInfo theAnimationInfo =
+ GetStudioSystem()->GetAnimationCore()->GetAnimationInfo(inAnimation);
+ theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(theAnimationInfo.m_Instance));
+ if (theTimelineBinding)
+ theTimelineBinding->RefreshPropertyKeyframe(
+ theAnimationInfo.m_Property, 0, ETimelineKeyframeTransaction_DynamicChanged);
+ }
+}
+
+void CTimelineTranslationManager::OnAssetCreated(UICDM::CUICDMInstanceHandle inInstance)
+{
+ CClientDataModelBridge *theDataModelBridge = GetStudioSystem()->GetClientDataModelBridge();
+
+ if (theDataModelBridge->IsSceneGraphInstance(inInstance))
+ EnsureLoaded(inInstance);
+}
+
+void CTimelineTranslationManager::OnAssetDeleted(UICDM::CUICDMInstanceHandle inInstance)
+{
+ // You can't assume the instance is valid. Someone may have deleted a large number of items
+ // from the model and then decided to send notifications after the fact.
+ // if the created asset is library asset, do nothing
+ // start to add the scene asset to the timeline
+ CUICDMTimelineItemBinding *theItemBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inInstance));
+ if (theItemBinding) {
+ CUICDMTimelineItemBinding *theParentBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(theItemBinding->GetParent());
+ if (theParentBinding)
+ theParentBinding->OnDeleteChild(inInstance);
+ }
+}
+
+void CTimelineTranslationManager::OnChildAdded(int /*inParent*/, int inChild, long /*inIndex*/)
+{
+ OnAssetCreated(inChild);
+}
+void CTimelineTranslationManager::OnChildRemoved(int /*inParent*/, int inChild, long /*inIndex*/)
+{
+ OnAssetDeleted(inChild);
+}
+void CTimelineTranslationManager::OnChildMoved(int /*inParent*/, int inChild, long /*inOldIndex*/,
+ long /*inNewIndex*/)
+{
+ OnAssetDeleted(inChild);
+ OnAssetCreated(inChild);
+}
+
+//==============================================================================
+/**
+ * Callback method whenever an action is either created or removed.
+ * Basically, it tells the owner of the action to update its timeline control to
+ * update the icon that shows action association status
+ */
+void CTimelineTranslationManager::OnActionEvent(UICDM::CUICDMActionHandle inAction,
+ UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inOwner)
+{
+ Q_UNUSED(inAction);
+
+ // the slide that action is added to is the current slide or
+ // is added to the master slide of the current slide
+ CUICDMTimelineItemBinding *theTimelineBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetBinding(inOwner));
+ if (theTimelineBinding)
+ theTimelineBinding->UpdateActionStatus();
+}
+
+//==============================================================================
+/**
+ * Helper functions to go through ALL binding and clear any keyframes selection.
+ */
+void CTimelineTranslationManager::ClearBindingsKeyframeSelection()
+{
+ // UICDM bindings handle their own selections
+ TInstanceHandleBindingMap::const_iterator theIter = m_InstanceHandleBindingMap.begin();
+ for (; theIter != m_InstanceHandleBindingMap.end(); ++theIter)
+ theIter->second->DoSelectKeyframes(false, -1, true);
+}
+
+//==============================================================================
+/**
+ * Helper function to find the binding that corresponds to inSelectable and set its selection state
+ */
+void CTimelineTranslationManager::SetSelected(Q3DStudio::SSelectedValue inSelectable,
+ bool inSelected)
+{
+ UICDM::TInstanceHandleList theInstances = inSelectable.GetSelectedInstances();
+ for (size_t idx = 0, end = theInstances.size(); idx < end; ++idx) {
+ CUICDMInstanceHandle theInstance(theInstances[idx]);
+ if (GetStudioSystem()->IsInstance(theInstance)) {
+ ITimelineItemBinding *theBinding = EnsureLoaded(theInstance);
+ if (theBinding) {
+ CBaseStateRow *theRow = theBinding->GetRow();
+ if (theRow)
+ theRow->OnSelected(inSelected);
+ }
+ }
+ }
+}
+
+ITimelineItemBinding *CTimelineTranslationManager::EnsureLoaded(CUICDMInstanceHandle inHandle)
+{
+ ITimelineItemBinding *theBinding = GetBinding(inHandle);
+ bool rowLoaded = theBinding != nullptr && theBinding->GetRow() != nullptr;
+ if (rowLoaded == false) {
+ // tell my parent to load me
+ CClientDataModelBridge *theDataModelBridge = GetStudioSystem()->GetClientDataModelBridge();
+ CUICDMInstanceHandle theParent = theDataModelBridge->GetParentInstance(inHandle);
+ if (theParent.Valid()) {
+ ITimelineItemBinding *theParentBinding = EnsureLoaded(theParent);
+ if (theParentBinding)
+ theParentBinding->GetRow()->LoadChildren();
+
+ // The LoadChildren has an optimzation such that if it's already loaded, it won't
+ // recreate again
+ // So, if we still can't get the binding after LoadChildren, it very likely means that
+ // this is newly added
+ // so call OnAddChild to let it just add this.
+ theBinding = GetBinding(inHandle);
+ bool rowLoaded = theBinding != nullptr && theBinding->GetRow() != nullptr;
+ if (theParentBinding && rowLoaded == false) {
+ // start to add the scene asset to the timeline
+ CUICDMTimelineItemBinding *theUICDMBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(theParentBinding);
+ theUICDMBinding->OnAddChild(inHandle);
+ theBinding = GetBinding(inHandle);
+ }
+ }
+ }
+ return theBinding;
+}
+
+//==============================================================================
+/**
+ * remember the expanded state for the current presentation
+ */
+bool CTimelineTranslationManager::IsExpanded(CUICDMInstanceHandle inInstance) const
+{
+ TInstanceHandleExpandedMap::const_iterator theIter =
+ m_InstanceHandleExpandedMap.find(inInstance);
+ if (theIter != m_InstanceHandleExpandedMap.end()) {
+ return theIter->second;
+ }
+ return false;
+}
+
+//==============================================================================
+/**
+ * remember the expanded state for the current presentation
+ */
+void CTimelineTranslationManager::SetExpanded(CUICDMInstanceHandle inInstance, bool inExpanded)
+{
+ TInstanceHandleExpandedMap::iterator theIter = m_InstanceHandleExpandedMap.find(inInstance);
+ if (theIter != m_InstanceHandleExpandedMap.end()) {
+ theIter->second = inExpanded;
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.h
new file mode 100644
index 00000000..48f7bebe
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/TimelineTranslationManager.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TIMELINE_TRANSLATIONMANAGER_H
+#define INCLUDED_TIMELINE_TRANSLATIONMANAGER_H 1
+
+#pragma once
+
+#include "UICDMHandles.h"
+#include "UICDMTimeline.h"
+
+#include "Doc.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class ITimelineItemBinding;
+class ITimelineItemProperty;
+class CUICDMTimelineItemBinding;
+class CBaseStateRow;
+class CPropertyRow;
+class CKeyframesManager;
+class IBreadCrumbProvider;
+
+// Link to data model
+class CAsset;
+class IUICDMSelectable;
+class CClientDataModelBridge;
+
+// UICDM
+namespace UICDM {
+class CStudioSystem;
+class ISignalConnection;
+}
+
+class CDoc;
+
+//=============================================================================
+/**
+ * There is a TranslationManager per presentation (project)
+ */
+class CTimelineTranslationManager
+{
+protected: // Typedefs
+ // UICDM support
+ typedef std::map<UICDM::CUICDMInstanceHandle, CUICDMTimelineItemBinding *>
+ TInstanceHandleBindingMap;
+
+ // Store expanded state
+ typedef std::map<UICDM::CUICDMInstanceHandle, bool> TInstanceHandleExpandedMap; // UICDM support
+
+protected: // Properties
+ // UICDM support
+ TInstanceHandleBindingMap m_InstanceHandleBindingMap;
+
+ CKeyframesManager *m_KeyframesManager;
+ IBreadCrumbProvider *m_BreadCrumbProvider;
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>>
+ m_Connections; /// connections to the UICDM
+
+ TInstanceHandleExpandedMap m_InstanceHandleExpandedMap;
+
+public:
+ CTimelineTranslationManager();
+ ~CTimelineTranslationManager();
+
+public:
+ ITimelineItemBinding *GetOrCreate(UICDM::CUICDMInstanceHandle inInstance);
+ void CreateNewPropertyRow(ITimelineItemProperty *inTimelineItemPropertyBinding,
+ CBaseStateRow *inParentRow, CPropertyRow *inNextRow);
+ void RemovePropertyRow(ITimelineItemProperty *inTimelineItemPropertyBinding);
+
+ void Clear();
+ void Unregister(ITimelineItemBinding *inTimelineItem);
+
+ CKeyframesManager *GetKeyframesManager() const;
+ IBreadCrumbProvider *GetBreadCrumbProvider() const;
+ CBaseStateRow *GetSelectedRow() const;
+ long GetCurrentViewTime() const;
+ CUICDMTimelineItemBinding *GetBinding(UICDM::CUICDMInstanceHandle inHandle) const;
+ CUICDMTimelineItemBinding *GetSelectedBinding() const;
+
+ void ClearKeyframeSelection();
+ void OnNewPresentation();
+ void OnSelectionChange(Q3DStudio::SSelectedValue inNewSelectable);
+
+ UICDM::CStudioSystem *GetStudioSystem() const;
+
+ // UICDM callback
+ void OnAnimationCreated(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void OnAnimationDeleted(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void OnPropertyLinked(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void OnPropertyUnlinked(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void RefreshKeyframe(UICDM::CUICDMAnimationHandle inAnimation,
+ UICDM::CUICDMKeyframeHandle inKeyframe,
+ ETimelineKeyframeTransaction inTransaction);
+ void OnKeyframeInserted(UICDM::CUICDMAnimationHandle inAnimation,
+ UICDM::CUICDMKeyframeHandle inKeyframe);
+ void OnKeyframeDeleted(UICDM::CUICDMAnimationHandle inAnimation,
+ UICDM::CUICDMKeyframeHandle inKeyframe);
+ void OnKeyframeUpdated(UICDM::CUICDMKeyframeHandle inKeyframe);
+ void OnPropertyChanged(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void OnDynamicKeyframeChanged(UICDM::CUICDMAnimationHandle inAnimation, bool inDynamic);
+
+ void OnAssetCreated(UICDM::CUICDMInstanceHandle inInstance);
+ void OnAssetDeleted(UICDM::CUICDMInstanceHandle inInstance);
+ void OnChildAdded(int inParent, int inChild, long inIndex);
+ void OnChildRemoved(int inParent, int inChild, long inIndex);
+ void OnChildMoved(int inParent, int inChild, long inOldIndex, long inNewIndex);
+
+ void OnActionEvent(UICDM::CUICDMActionHandle inAction, UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inOwner);
+
+ // Helper function to iterate over all bindings
+ void ClearBindingsKeyframeSelection();
+ CDoc *GetDoc() const;
+
+ // Store expanded state
+ bool IsExpanded(UICDM::CUICDMInstanceHandle inInstance) const;
+ void SetExpanded(UICDM::CUICDMInstanceHandle inInstance, bool inExpanded);
+
+protected:
+ void SetSelected(Q3DStudio::SSelectedValue inSelectable, bool inSelected);
+ ITimelineItemBinding *EnsureLoaded(UICDM::CUICDMInstanceHandle inHandle);
+};
+
+#endif // INCLUDED_TIMELINE_TRANSLATIONMANAGER_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.cpp
new file mode 100644
index 00000000..4eb0f906
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "UICDMAssetTimelineKeyframe.h"
+#include "UICDMTimelineItemBinding.h"
+
+using namespace UICDM;
+
+CUICDMAssetTimelineKeyframe::CUICDMAssetTimelineKeyframe(CUICDMTimelineItemBinding *inOwningBinding,
+ long inTime)
+ : m_OwningBinding(inOwningBinding)
+ , m_Time(inTime)
+ , m_Selected(false)
+{
+}
+
+CUICDMAssetTimelineKeyframe::~CUICDMAssetTimelineKeyframe()
+{
+}
+
+bool CUICDMAssetTimelineKeyframe::IsSelected() const
+{
+ return m_Selected;
+}
+
+long CUICDMAssetTimelineKeyframe::GetTime() const
+{
+ return m_Time;
+}
+
+void CUICDMAssetTimelineKeyframe::SetTime(const long inNewTime)
+{
+ Q_UNUSED(inNewTime);
+ // note: this is not used. because setting time is currently only done through offsetting by
+ // moving keyframes OR using the edit time dialog.
+ ASSERT(0);
+}
+
+void CUICDMAssetTimelineKeyframe::SetDynamic(bool inIsDynamic)
+{
+ m_OwningBinding->SetDynamicKeyframes(m_Time, inIsDynamic);
+}
+
+bool CUICDMAssetTimelineKeyframe::IsDynamic() const
+{
+ // return true if any of its property keyframes is dynamic
+ return m_OwningBinding->HasDynamicKeyframes(m_Time);
+}
+
+void CUICDMAssetTimelineKeyframe::SetSelected(bool inSelected)
+{
+ m_Selected = inSelected;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.h
new file mode 100644
index 00000000..e8cf6870
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMAssetTimelineKeyframe.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INCLUDED_UICDMASSETKEYFRAME_H
+#define INCLUDED_UICDMASSETKEYFRAME_H 1
+
+#pragma once
+
+#include "IKeyframe.h"
+
+// Data model specific
+#include "UICDMHandles.h"
+
+class CUICDMTimelineItemBinding;
+
+//==============================================================================
+/**
+ * Represents a keyframe displayed for a Asset( e.g. material ), for all keyframes (of the
+ *animated properties) at time t.
+ */
+//==============================================================================
+class CUICDMAssetTimelineKeyframe : public IKeyframe
+{
+protected:
+ CUICDMTimelineItemBinding *m_OwningBinding;
+ long m_Time;
+ bool m_Selected;
+
+public:
+ CUICDMAssetTimelineKeyframe(CUICDMTimelineItemBinding *inOwningBinding, long inTime);
+ virtual ~CUICDMAssetTimelineKeyframe();
+
+ // IKeyframe
+ bool IsSelected() const override;
+ long GetTime() const override;
+ void SetTime(const long inNewTime) override;
+ void SetDynamic(bool inIsDynamic) override;
+ bool IsDynamic() const override;
+
+ void SetSelected(bool inSelected);
+ void UpdateTime(const long inTime) { m_Time = inTime; }
+};
+
+#endif // INCLUDED_UICDMKEYFRAME_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimeline.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimeline.h
new file mode 100644
index 00000000..7989956f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimeline.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_UICDMTIMELINE_H
+#define INCLUDED_UICDMTIMELINE_H 1
+
+#pragma once
+
+enum ETimelineKeyframeTransaction {
+ ETimelineKeyframeTransaction_Add,
+ ETimelineKeyframeTransaction_Delete,
+ ETimelineKeyframeTransaction_Update,
+ ETimelineKeyframeTransaction_DynamicChanged,
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.cpp
new file mode 100644
index 00000000..b6dd94d9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.cpp
@@ -0,0 +1,1260 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMTimelineItemBinding.h"
+#include "TimelineTranslationManager.h"
+#include "TimeEditDlg.h"
+#include "EmptyTimelineTimebar.h"
+#include "UICDMTimelineTimebar.h"
+#include "BaseStateRow.h"
+#include "BaseTimebarlessRow.h"
+#include "PropertyTimebarRow.h"
+#include "PropertyRow.h"
+#include "KeyframesManager.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Dialogs.h"
+#include "GraphUtils.h"
+#include "UICDMDataCore.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+// Data model specific
+#include "IDoc.h"
+#include "ClientDataModelBridge.h"
+#include "Dispatch.h"
+#include "DropSource.h"
+#include "UICDMTimelineItemProperty.h"
+#include "UICDMSlides.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlideGraphCore.h"
+#include "UICDMActionCore.h"
+#include "UICDMAnimation.h"
+#include "CmdDataModelChangeKeyframe.h"
+#include "RelativePathTools.h"
+#include "IDocumentEditor.h"
+#include "UICFileTools.h"
+#include "ImportUtils.h"
+
+#include <QMessageBox>
+
+using namespace UICDM;
+
+CUICDMTimelineItemBinding::CUICDMTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ CUICDMInstanceHandle inDataHandle)
+ : m_Row(nullptr)
+ , m_TransMgr(inMgr)
+ , m_DataHandle(inDataHandle)
+ , m_Parent(nullptr)
+ , m_TimelineTimebar(nullptr)
+
+{
+ m_StudioSystem = m_TransMgr->GetStudioSystem();
+ m_TransMgr->GetDoc()->GetCore()->GetDispatch()->AddDataModelListener(this);
+}
+
+CUICDMTimelineItemBinding::CUICDMTimelineItemBinding(CTimelineTranslationManager *inMgr)
+ : m_Row(nullptr)
+ , m_TransMgr(inMgr)
+ , m_DataHandle(0)
+ , m_Parent(nullptr)
+ , m_TimelineTimebar(nullptr)
+{
+ m_StudioSystem = m_TransMgr->GetStudioSystem();
+ m_TransMgr->GetDoc()->GetCore()->GetDispatch()->AddDataModelListener(this);
+}
+
+CUICDMTimelineItemBinding::~CUICDMTimelineItemBinding()
+{
+ RemoveAllPropertyBindings();
+ delete m_TimelineTimebar;
+ m_TransMgr->GetDoc()->GetCore()->GetDispatch()->RemoveDataModelListener(this);
+}
+
+// helpers
+bool CUICDMTimelineItemBinding::UICDMGetBoolean(UICDM::CUICDMPropertyHandle inProperty) const
+{
+ UICDM::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem();
+ SValue theValue;
+ thePropertySystem->GetInstancePropertyValue(m_DataHandle, inProperty, theValue);
+ return UICDM::get<bool>(theValue);
+}
+
+void CUICDMTimelineItemBinding::UICDMSetBoolean(UICDM::CUICDMPropertyHandle inProperty,
+ bool inValue, const QString &inNiceText) const
+{
+ CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc());
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*theDoc, inNiceText)
+ ->SetInstancePropertyValue(m_DataHandle, inProperty, inValue);
+}
+
+void CUICDMTimelineItemBinding::SetInstanceHandle(UICDM::CUICDMInstanceHandle inDataHandle)
+{
+ m_DataHandle = inDataHandle;
+}
+
+EStudioObjectType CUICDMTimelineItemBinding::GetObjectType() const
+{
+ return m_StudioSystem->GetClientDataModelBridge()->GetObjectType(m_DataHandle);
+}
+
+bool CUICDMTimelineItemBinding::IsMaster() const
+{
+ CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc());
+ Q3DStudio::IDocumentReader &theReader(theDoc->GetDocumentReader());
+ if (GetObjectType() == OBJTYPE_IMAGE) {
+ CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge();
+ CUICDMInstanceHandle theParent;
+ CUICDMPropertyHandle theProperty;
+ bool isPropertyLinked;
+
+ theBridge->GetMaterialFromImageInstance(GetInstance(), theParent, theProperty);
+ isPropertyLinked = theReader.IsPropertyLinked(theParent, theProperty);
+
+ // Also check light probe
+ if (!isPropertyLinked) {
+ theBridge->GetLayerFromImageProbeInstance(GetInstance(), theParent, theProperty);
+ isPropertyLinked = theReader.IsPropertyLinked(theParent, theProperty);
+ }
+
+ return isPropertyLinked;
+ }
+ CUICDMInstanceHandle theQueryHandle(m_DataHandle);
+ if (GetObjectType() == OBJTYPE_PATHANCHORPOINT)
+ theQueryHandle = theReader.GetParent(m_DataHandle);
+
+ // logic: you can't unlink name, so if name is linked then, this is master.
+ CUICDMPropertyHandle theNamePropHandle =
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName(theQueryHandle,
+ L"name");
+ return theReader.IsPropertyLinked(theQueryHandle, theNamePropHandle);
+}
+
+bool CUICDMTimelineItemBinding::IsShy() const
+{
+ return UICDMGetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Shy);
+}
+void CUICDMTimelineItemBinding::SetShy(bool inShy)
+{
+ UICDMSetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Shy, inShy,
+ QObject::tr("Shy Toggle"));
+}
+bool CUICDMTimelineItemBinding::IsLocked() const
+{
+ return UICDMGetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Locked);
+}
+
+void ToggleChildrenLock(Q3DStudio::ScopedDocumentEditor &scopedDocEditor,
+ CUICDMTimelineItemBinding *inTimelineItemBinding,
+ SDataModelSceneAsset inSceneAsset, bool inLocked)
+{
+ scopedDocEditor->SetInstancePropertyValue(inTimelineItemBinding->GetInstanceHandle(),
+ inSceneAsset.m_Locked, inLocked);
+ long childrenCount = inTimelineItemBinding->GetChildrenCount();
+ if (childrenCount == 0)
+ return;
+ for (long i = 0; i < childrenCount; ++i) {
+ CUICDMTimelineItemBinding *child =
+ static_cast<CUICDMTimelineItemBinding *>(inTimelineItemBinding->GetChild(i));
+ ToggleChildrenLock(scopedDocEditor, child, inSceneAsset, inLocked);
+ }
+}
+
+void CUICDMTimelineItemBinding::SetLocked(bool inLocked)
+{
+ CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc());
+ Q3DStudio::ScopedDocumentEditor scopedDocEditor(*theDoc, L"SetLock", __FILE__, __LINE__);
+
+ SDataModelSceneAsset sceneAsset = m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset();
+ ToggleChildrenLock(scopedDocEditor, this, sceneAsset, inLocked);
+
+ if (inLocked)
+ g_StudioApp.GetCore()->GetDoc()->NotifySelectionChanged();
+}
+
+bool CUICDMTimelineItemBinding::IsVisible() const
+{
+ return UICDMGetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Eyeball);
+}
+
+void CUICDMTimelineItemBinding::SetVisible(bool inVisible)
+{
+ UICDMSetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Eyeball,
+ inVisible, QObject::tr("Visibility Toggle"));
+}
+
+// remember the expanded state for the current presentation
+bool CUICDMTimelineItemBinding::IsExpanded() const
+{
+ return m_TransMgr->IsExpanded(m_DataHandle);
+}
+// remember the expanded state for the current presentation
+void CUICDMTimelineItemBinding::SetExpanded(bool inExpanded)
+{
+ m_TransMgr->SetExpanded(m_DataHandle, inExpanded);
+}
+
+bool CUICDMTimelineItemBinding::HasAction(bool inMaster)
+{
+ TActionHandleList theActions;
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+
+ CUICDMSlideHandle theSlide = theDoc->GetActiveSlide();
+ UICDM::ISlideCore &theSlideCore(*m_StudioSystem->GetSlideCore());
+ if (theSlideCore.IsSlide(theSlide)) {
+ if (inMaster)
+ theSlide =
+ m_StudioSystem->GetSlideSystem()->GetMasterSlide(theSlide); // use the master slide
+
+ m_StudioSystem->GetActionCore()->GetActions(theSlide, m_DataHandle, theActions);
+ }
+ return theActions.size() > 0;
+}
+
+bool CUICDMTimelineItemBinding::ChildrenHasAction(bool inMaster)
+{
+ // Get all the instances in this slidegraph
+ // check whehter it's an action instance and is in the slide of interst
+ // check also it's owner is a descendent of the viewed instances
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ IActionCore *theActionCore(m_StudioSystem->GetActionCore());
+ CClientDataModelBridge *theBridge(m_StudioSystem->GetClientDataModelBridge());
+
+ CUICDMSlideHandle theSlide = theDoc->GetActiveSlide();
+ UICDM::ISlideCore &theSlideCore(*m_StudioSystem->GetSlideCore());
+ if (theSlideCore.IsSlide(theSlide)) {
+ if (inMaster)
+ theSlide =
+ m_StudioSystem->GetSlideSystem()->GetMasterSlide(theSlide); // use the master slide
+
+ TSlideInstancePairList theGraphInstances;
+ m_StudioSystem->GetSlideSystem()->GetAssociatedInstances(theSlide, theGraphInstances);
+
+ UICDM::CUICDMInstanceHandle theObservedInstance = GetInstance();
+ if (theObservedInstance.Valid()) {
+ for (TSlideInstancePairList::const_iterator theIter = theGraphInstances.begin();
+ theIter != theGraphInstances.end(); ++theIter) {
+ if (theIter->first == theSlide && theBridge->IsActionInstance(theIter->second)) {
+ CUICDMActionHandle theAction =
+ theActionCore->GetActionByInstance(theIter->second);
+ SActionInfo theActionInfo = theActionCore->GetActionInfo(theAction);
+ CUICDMInstanceHandle theAcionOwner = theActionInfo.m_Owner;
+ if (theAcionOwner.Valid()
+ && IsAscendant(theAcionOwner, theObservedInstance, theDoc->GetAssetGraph()))
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+bool CUICDMTimelineItemBinding::ComponentHasAction(bool inMaster)
+{
+ // Get all the instances in this component slidegraph
+ // check whether the instance is an action instance
+ // if inMaster is true, we only interest with those that are in the master slide, else we want
+ // those that are not in the master slide
+ CClientDataModelBridge *theBridge(m_StudioSystem->GetClientDataModelBridge());
+ if (!theBridge->IsComponentInstance(m_DataHandle))
+ return false;
+
+ Q3DStudio::CId theAssetId = theBridge->GetGUID(m_DataHandle);
+ CUICDMSlideHandle theMasterSlide =
+ m_StudioSystem->GetSlideSystem()->GetMasterSlideByComponentGuid(GuidtoSLong4(theAssetId));
+
+ TSlideInstancePairList theGraphInstances;
+ m_StudioSystem->GetSlideSystem()->GetAssociatedInstances(theMasterSlide, theGraphInstances);
+
+ for (TSlideInstancePairList::const_iterator theIter = theGraphInstances.begin();
+ theIter != theGraphInstances.end(); ++theIter) {
+ if (((inMaster && theIter->first == theMasterSlide)
+ || (!inMaster && theIter->first != theMasterSlide))
+ && theBridge->IsActionInstance(theIter->second))
+ return true;
+ }
+ return false;
+}
+
+ITimelineTimebar *CUICDMTimelineItemBinding::GetTimebar()
+{
+ if (!m_TimelineTimebar)
+ m_TimelineTimebar = CreateTimelineTimebar();
+ return m_TimelineTimebar;
+}
+
+Q3DStudio::CString CUICDMTimelineItemBinding::GetName() const
+{
+ if (m_StudioSystem->IsInstance(m_DataHandle) == false)
+ return L"";
+ CUICDMPropertyHandle theNamePropHandle =
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName(m_DataHandle,
+ L"name");
+ SValue theNameValue;
+ m_StudioSystem->GetPropertySystem()->GetInstancePropertyValue(m_DataHandle, theNamePropHandle,
+ theNameValue);
+ TDataStrPtr theName = UICDM::get<TDataStrPtr>(theNameValue);
+
+ return (theName) ? Q3DStudio::CString(theName->GetData()) : "";
+}
+
+void CUICDMTimelineItemBinding::SetName(const Q3DStudio::CString &inName)
+{
+ // Display warning dialog if user tried to enter an empty string
+ if (inName.IsEmpty()) {
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_ERROR_OBJECT_RENAME_TITLE));
+ Q3DStudio::CString theString(::LoadResourceString(IDS_ERROR_OBJECT_RENAME_EMPTY_STRING));
+ g_StudioApp.GetDialogs()->DisplayMessageBox(theTitle, theString, CUICMessageBox::ICON_ERROR,
+ false);
+
+ return;
+ }
+
+ CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge();
+ if (!theBridge->CheckNameUnique(m_DataHandle, inName)) {
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_ERROR_OBJECT_RENAME_TITLE));
+ Q3DStudio::CString theString(
+ ::LoadResourceString(IDS_ERROR_OBJECT_RENAME_DUPLICATED_STRING));
+ int theUserChoice = g_StudioApp.GetDialogs()->DisplayChoiceBox(
+ theTitle, theString, CUICMessageBox::ICON_WARNING);
+ if (theUserChoice == QMessageBox::Yes) {
+ // Set with the unique name
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*m_TransMgr->GetDoc(), QObject::tr("Set Name"))
+ ->SetName(m_DataHandle, inName, true);
+ return;
+ }
+ }
+ // Set the name no matter it's unique or not
+ CUICDMPropertyHandle theNamePropHandle =
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName(m_DataHandle,
+ L"name");
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*m_TransMgr->GetDoc(), QObject::tr("Set Name"))
+ ->SetInstancePropertyValue(m_DataHandle, theNamePropHandle,
+ std::make_shared<CDataStr>(inName));
+}
+
+ITimelineItem *CUICDMTimelineItemBinding::GetTimelineItem()
+{
+ return this;
+}
+
+CBaseStateRow *CUICDMTimelineItemBinding::GetRow()
+{
+ return m_Row;
+}
+
+void CUICDMTimelineItemBinding::SetSelected(bool inMultiSelect)
+{
+ if (!inMultiSelect)
+ g_StudioApp.GetCore()->GetDoc()->SelectUICDMObject(m_DataHandle);
+ else
+ g_StudioApp.GetCore()->GetDoc()->ToggleUICDMObjectToSelection(m_DataHandle);
+}
+
+void CUICDMTimelineItemBinding::OnCollapsed()
+{
+ // Preserves legacy behavior where collapsing a tree will select that root, if any of its
+ // descendant was selected
+ // TODO: This won't work for Image (because Image is Material's property, not child)
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ if (theInstance.Valid()) {
+ CDoc *theDoc = m_TransMgr->GetDoc();
+ UICDM::CUICDMInstanceHandle theSelectedInstance = theDoc->GetSelectedInstance();
+ if (theSelectedInstance.Valid()
+ && IsAscendant(theSelectedInstance, theInstance, theDoc->GetAssetGraph()))
+ SetSelected(false);
+ }
+}
+
+void CUICDMTimelineItemBinding::ClearKeySelection()
+{
+ m_TransMgr->ClearKeyframeSelection();
+}
+
+bool CUICDMTimelineItemBinding::OpenAssociatedEditor()
+{
+ return false; // nothing to do by default
+}
+
+void CUICDMTimelineItemBinding::DoStartDrag(CControlWindowListener *inWndListener)
+{
+ inWndListener->DoStartDrag(this);
+}
+
+inline UICDM::CUICDMInstanceHandle CUICDMTimelineItemBinding::GetInstance() const
+{
+ return m_DataHandle;
+}
+
+void CUICDMTimelineItemBinding::SetDropTarget(CDropTarget *inTarget)
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ inTarget->SetInstance(theInstance);
+}
+
+long CUICDMTimelineItemBinding::GetChildrenCount()
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ if (theInstance.Valid()) {
+ Q3DStudio::CGraphIterator theChildren;
+ CUICDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide();
+ GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(),
+ theChildren, theActiveSlide);
+ return (long)theChildren.GetCount();
+ }
+ return 0;
+}
+
+ITimelineItemBinding *CUICDMTimelineItemBinding::GetChild(long inIndex)
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ if (theInstance.Valid()) {
+ Q3DStudio::CGraphIterator theChildren;
+ CUICDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide();
+ GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(),
+ theChildren, theActiveSlide);
+ theChildren += inIndex;
+
+ UICDM::CUICDMInstanceHandle theChildInstance = theChildren.GetCurrent();
+ if (theChildInstance.Valid())
+ return m_TransMgr->GetOrCreate(theChildInstance);
+ }
+ return nullptr;
+}
+
+ITimelineItemBinding *CUICDMTimelineItemBinding::GetParent()
+{
+ return m_Parent;
+}
+void CUICDMTimelineItemBinding::SetParent(ITimelineItemBinding *parent)
+{
+ if (parent != m_Parent) {
+ ASSERT(parent == nullptr || m_Parent == nullptr);
+ m_Parent = parent;
+ }
+}
+
+long CUICDMTimelineItemBinding::GetPropertyCount()
+{
+ long theCount = 0;
+ if (m_StudioSystem->IsInstance(m_DataHandle)) {
+ TPropertyHandleList theProperties;
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle,
+ theProperties);
+ for (size_t thePropertyIndex = 0; thePropertyIndex < theProperties.size();
+ ++thePropertyIndex) {
+ if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(
+ m_DataHandle, theProperties[thePropertyIndex]))
+ ++theCount;
+ }
+ }
+ return theCount;
+}
+
+ITimelineItemProperty *CUICDMTimelineItemBinding::GetProperty(long inIndex)
+{
+ TPropertyHandleList theProperties;
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle,
+ theProperties);
+ long theIndex = -1;
+ size_t thePropertyIndex = 0;
+ for (; thePropertyIndex < theProperties.size(); ++thePropertyIndex) {
+ if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(
+ m_DataHandle, theProperties[thePropertyIndex])) {
+ ++theIndex;
+ if (theIndex == inIndex)
+ break;
+ }
+ }
+ ASSERT(thePropertyIndex < theProperties.size()); // no reason why this would be out of range!!
+ return GetOrCreatePropertyBinding(theProperties[thePropertyIndex]);
+}
+
+bool CUICDMTimelineItemBinding::ShowToggleControls() const
+{
+ return true;
+}
+bool CUICDMTimelineItemBinding::IsLockedEnabled() const
+{
+ return IsLocked();
+}
+bool CUICDMTimelineItemBinding::IsVisibleEnabled() const
+{
+ // You can only toggle visible if you aren't on the master slide.
+ return m_StudioSystem->GetSlideSystem()->GetSlideIndex(m_TransMgr->GetDoc()->GetActiveSlide())
+ != 0;
+}
+
+void CUICDMTimelineItemBinding::Bind(CBaseStateRow *inRow)
+{
+ ASSERT(!m_Row);
+ m_Row = inRow;
+
+ // Because children(properties included) may only be loaded later, check if there are any
+ // keyframes without having to have the UI created.
+ TPropertyHandleList theProperties;
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle,
+ theProperties);
+ for (size_t thePropertyIndex = 0; thePropertyIndex < theProperties.size(); ++thePropertyIndex) {
+ if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(
+ m_DataHandle, theProperties[thePropertyIndex]))
+ AddKeyframes(GetOrCreatePropertyBinding(theProperties[thePropertyIndex]));
+ }
+
+ // Set selection status
+ CUICDMInstanceHandle theSelectedInstance = m_TransMgr->GetDoc()->GetSelectedInstance();
+ m_Row->OnSelected(m_DataHandle == theSelectedInstance);
+}
+
+void CUICDMTimelineItemBinding::Release()
+{
+ m_Row = nullptr;
+ RemoveAllPropertyBindings();
+ m_TransMgr->Unregister(this);
+}
+
+bool CUICDMTimelineItemBinding::IsValidTransaction(EUserTransaction inTransaction)
+{
+ UICDM::CUICDMInstanceHandle theInstance = GetInstance();
+ switch (inTransaction) {
+ case EUserTransaction_Rename:
+ return (GetObjectType() != OBJTYPE_SCENE && GetObjectType() != OBJTYPE_IMAGE);
+
+ case EUserTransaction_Duplicate:
+ if (theInstance.Valid())
+ return m_StudioSystem->GetClientDataModelBridge()->IsDuplicateable(theInstance);
+ break;
+
+ case EUserTransaction_Cut:
+ return g_StudioApp.CanCut();
+
+ case EUserTransaction_Copy:
+ return g_StudioApp.CanCopy();
+
+ case EUserTransaction_Paste:
+ return m_TransMgr->GetDoc()->CanPasteObject();
+
+ case EUserTransaction_Delete:
+ if (theInstance.Valid())
+ return m_StudioSystem->GetClientDataModelBridge()->CanDelete(theInstance);
+ break;
+
+ case EUserTransaction_MakeComponent: {
+ bool theCanMakeFlag = false;
+ if (theInstance.Valid()) {
+ CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge();
+ EStudioObjectType theObjectType = theBridge->GetObjectType(theInstance);
+
+ if (!IsLocked()) {
+ // Any assets that are attached to the Scene directly must not be wrapped in a
+ // component.
+ // This may include behavior assets which may be directly attached to the Scene.
+ // This is because by principal, components cannot exist on the Scene directly.
+ UICDM::CUICDMInstanceHandle theParentInstance =
+ theBridge->GetParentInstance(theInstance);
+ if (theObjectType != OBJTYPE_LAYER && theObjectType != OBJTYPE_SCENE
+ && theObjectType != OBJTYPE_MATERIAL && theObjectType != OBJTYPE_IMAGE
+ && theObjectType != OBJTYPE_EFFECT
+ && (theParentInstance.Valid()
+ && theBridge->GetObjectType(theParentInstance)
+ != OBJTYPE_SCENE)) // This checks if the object is
+ // AttachedToSceneDirectly
+ {
+ theCanMakeFlag = true;
+ }
+ }
+ }
+ return theCanMakeFlag;
+ }
+
+ case EUserTransaction_EditComponent:
+ return (GetObjectType() == OBJTYPE_COMPONENT);
+
+ default: // not handled
+ break;
+ }
+
+ return false;
+}
+
+using namespace Q3DStudio;
+
+inline void DoCut(CDoc &inDoc, const UICDM::TInstanceHandleList &inInstances)
+{
+ inDoc.DeselectAllKeyframes();
+ inDoc.CutObject(inInstances);
+}
+
+inline void DoDelete(CDoc &inDoc, const UICDM::TInstanceHandleList &inInstances)
+{
+ inDoc.DeselectAllKeyframes();
+ inDoc.DeleteObject(inInstances);
+}
+
+inline void DoMakeComponent(CDoc &inDoc, const UICDM::TInstanceHandleList &inInstances)
+{
+ SCOPED_DOCUMENT_EDITOR(inDoc, QObject::tr("Make Component"))->MakeComponent(inInstances);
+}
+
+void CUICDMTimelineItemBinding::PerformTransaction(EUserTransaction inTransaction)
+{
+ CDoc *theDoc = m_TransMgr->GetDoc();
+ UICDM::TInstanceHandleList theInstances = theDoc->GetSelectedValue().GetSelectedInstances();
+ if (theInstances.empty())
+ return;
+ CDispatch &theDispatch(*theDoc->GetCore()->GetDispatch());
+
+ // Transactions that could result in *this* object being deleted need to be executed
+ // via postmessage, not in this context because it could result in the currently
+ // active timeline row being deleted while in its own mouse handler.
+ switch (inTransaction) {
+ case EUserTransaction_Duplicate: {
+ theDoc->DeselectAllKeyframes();
+ SCOPED_DOCUMENT_EDITOR(*theDoc, QObject::tr("Duplicate Object"))->DuplicateInstances(theInstances);
+ } break;
+ case EUserTransaction_Cut: {
+ theDispatch.FireOnAsynchronousCommand(
+ std::bind(DoCut, std::ref(*theDoc), theInstances));
+ } break;
+ case EUserTransaction_Copy: {
+ theDoc->DeselectAllKeyframes();
+ theDoc->CopyObject(theInstances);
+ } break;
+ case EUserTransaction_Paste: {
+ theDoc->DeselectAllKeyframes();
+ theDoc->PasteObject(GetInstance());
+ } break;
+ case EUserTransaction_Delete: {
+ theDispatch.FireOnAsynchronousCommand(
+ std::bind(DoDelete, std::ref(*theDoc), theInstances));
+ } break;
+ case EUserTransaction_MakeComponent: {
+ theDispatch.FireOnAsynchronousCommand(
+ std::bind(DoMakeComponent, std::ref(*theDoc), theInstances));
+ }
+ default: // not handled
+ break;
+ }
+}
+
+Q3DStudio::CString CUICDMTimelineItemBinding::GetObjectPath()
+{
+ CDoc *theDoc = m_TransMgr->GetDoc();
+ // Because we are getting absolute path, the base id doesn't matter.
+ return CRelativePathTools::BuildAbsoluteReferenceString(m_DataHandle, theDoc);
+}
+
+ITimelineKeyframesManager *CUICDMTimelineItemBinding::GetKeyframesManager() const
+{
+ return m_TransMgr->GetKeyframesManager();
+}
+
+void CUICDMTimelineItemBinding::RemoveProperty(ITimelineItemProperty *inProperty)
+{
+ Q_UNUSED(inProperty);
+ // TODO: This function has no use in UICDM world. This is replaced by RemovePropertyRow(
+ // CUICDMPropertyHandle inPropertyHandle ).
+ // Decide if this function should be removed from ITimelineItemBinding.
+}
+
+void CUICDMTimelineItemBinding::LoadProperties()
+{
+ TPropertyHandleList theProperties;
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle,
+ theProperties);
+ for (size_t thePropertyIndex = 0; thePropertyIndex < theProperties.size(); ++thePropertyIndex) {
+ if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(
+ m_DataHandle, theProperties[thePropertyIndex]))
+ AddPropertyRow(theProperties[thePropertyIndex], true);
+ }
+}
+
+void CUICDMTimelineItemBinding::InsertKeyframe()
+{
+ if (m_PropertyBindingMap.empty())
+ return;
+
+ TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin();
+ ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(), L"Insert Keyframe", __FILE__,
+ __LINE__);
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter)
+ editor->KeyframeProperty(m_DataHandle, theIter->first, false);
+}
+
+void CUICDMTimelineItemBinding::DeleteAllChannelKeyframes()
+{
+ if (m_PropertyBindingMap.empty())
+ return;
+
+ CDoc *theDoc = m_TransMgr->GetDoc();
+ Q3DStudio::ScopedDocumentEditor editor(*theDoc, L"Delete Channel Keyframes", __FILE__,
+ __LINE__);
+ for (TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin(),
+ theEnd = m_PropertyBindingMap.end();
+ theIter != theEnd; ++theIter)
+ theIter->second->DeleteAllKeys();
+}
+
+long CUICDMTimelineItemBinding::GetKeyframeCount() const
+{
+ // This list is updated when properties are loaded and when keyframes are added & deleted.
+ return (long)m_Keyframes.size();
+}
+
+IKeyframe *CUICDMTimelineItemBinding::GetKeyframeByTime(long inTime) const
+{
+ TAssetKeyframeList::const_iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ if ((*theIter).GetTime() == inTime)
+ return const_cast<CUICDMAssetTimelineKeyframe *>(&(*theIter));
+ }
+ return nullptr;
+}
+
+IKeyframe *CUICDMTimelineItemBinding::GetKeyframeByIndex(long inIndex) const
+{
+ if (inIndex >= 0 && inIndex < (long)m_Keyframes.size())
+ return const_cast<CUICDMAssetTimelineKeyframe *>(&m_Keyframes[inIndex]);
+
+ ASSERT(0); // should not happen
+ return nullptr;
+}
+
+long CUICDMTimelineItemBinding::OffsetSelectedKeyframes(long inOffset)
+{
+ return m_TransMgr->GetKeyframesManager()->OffsetSelectedKeyframes(inOffset);
+}
+
+void CUICDMTimelineItemBinding::CommitChangedKeyframes()
+{
+ m_TransMgr->GetKeyframesManager()->CommitChangedKeyframes();
+}
+
+void CUICDMTimelineItemBinding::OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation)
+{
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.SetKeyframesManager(m_TransMgr->GetKeyframesManager());
+ theTimeEditDlg.ShowDialog(inCurrentTime, 0, g_StudioApp.GetCore()->GetDoc(),
+ inObjectAssociation);
+}
+
+void CUICDMTimelineItemBinding::SelectKeyframes(bool inSelected, long inTime /*= -1 */)
+{
+ // Callback from UI, hence skip the UI update
+ DoSelectKeyframes(inSelected, inTime, false);
+}
+
+CUICDMInstanceHandle CUICDMTimelineItemBinding::GetInstanceHandle() const
+{
+ return m_DataHandle;
+}
+
+long CUICDMTimelineItemBinding::GetFlavor() const
+{
+ return EUIC_FLAVOR_ASSET_TL;
+}
+
+void CUICDMTimelineItemBinding::OnBeginDataModelNotifications()
+{
+}
+void CUICDMTimelineItemBinding::OnEndDataModelNotifications()
+{
+ RefreshStateRow();
+}
+void CUICDMTimelineItemBinding::OnImmediateRefreshInstanceSingle(
+ UICDM::CUICDMInstanceHandle inInstance)
+{
+ if (inInstance == m_DataHandle)
+ RefreshStateRow(true);
+}
+void CUICDMTimelineItemBinding::OnImmediateRefreshInstanceMultiple(
+ UICDM::CUICDMInstanceHandle *inInstance, long inInstanceCount)
+{
+ for (long idx = 0; idx < inInstanceCount; ++idx)
+ if (inInstance[idx] == m_DataHandle) {
+ RefreshStateRow();
+ break;
+ }
+}
+
+void CUICDMTimelineItemBinding::RefreshStateRow(bool inRefreshChildren)
+{
+ CStateRow *theRow = dynamic_cast<CStateRow *>(m_Row);
+ if (theRow) {
+ theRow->OnTimeChange();
+ theRow->ClearDirty();
+ if (inRefreshChildren) {
+ long theChildrenCount = GetChildrenCount();
+ for (long theIndex = 0; theIndex < theChildrenCount; ++theIndex) {
+ ITimelineItemBinding *theChild = GetChild(theIndex);
+ CUICDMTimelineItemBinding *theBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(theChild);
+ if (theBinding)
+ theBinding->RefreshStateRow(inRefreshChildren);
+ }
+ }
+ }
+}
+
+ITimelineTimebar *CUICDMTimelineItemBinding::CreateTimelineTimebar()
+{
+ return new CUICDMTimelineTimebar(m_TransMgr, m_DataHandle);
+}
+
+ITimelineItemProperty *
+CUICDMTimelineItemBinding::GetPropertyBinding(CUICDMPropertyHandle inPropertyHandle)
+{
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle);
+ // check if it already exists
+ if (theIter != m_PropertyBindingMap.end())
+ return theIter->second;
+ return nullptr;
+}
+
+ITimelineItemProperty *
+CUICDMTimelineItemBinding::GetOrCreatePropertyBinding(CUICDMPropertyHandle inPropertyHandle)
+{
+ ITimelineItemProperty *theProperty = GetPropertyBinding(inPropertyHandle);
+ // check if it already exists
+ if (theProperty)
+ return theProperty;
+
+ // Create
+ CUICDMTimelineItemProperty *theTimelineProperty =
+ new CUICDMTimelineItemProperty(m_TransMgr, inPropertyHandle, m_DataHandle);
+ m_PropertyBindingMap.insert(std::make_pair(inPropertyHandle, theTimelineProperty));
+
+ return theTimelineProperty;
+}
+
+//=============================================================================
+/**
+ * Add a new property row for this property.
+ * @param inAppend true to skip the check to find where to insert. ( true if this is a
+ * loading/initializing step, where the call is already done in order )
+ */
+void CUICDMTimelineItemBinding::AddPropertyRow(CUICDMPropertyHandle inPropertyHandle,
+ bool inAppend /*= false */)
+{
+ ITimelineItemProperty *theTimelineProperty = GetPropertyBinding(inPropertyHandle);
+ if (theTimelineProperty && theTimelineProperty->GetRow()) // if created, bail
+ return;
+
+ if (!theTimelineProperty)
+ theTimelineProperty = GetOrCreatePropertyBinding(inPropertyHandle);
+
+ // Find the row to insert this new property, if any, this preserves the order the property rows
+ // is displayed in the timeline.
+ ITimelineItemProperty *theNextProperty = nullptr;
+ if (!inAppend) {
+ TPropertyHandleList theProperties;
+ m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle,
+ theProperties);
+ size_t thePropertyIndex = 0;
+ size_t thePropertyCount = theProperties.size();
+ for (; thePropertyIndex < thePropertyCount; ++thePropertyIndex) {
+ if (theProperties[thePropertyIndex] == inPropertyHandle) {
+ ++thePropertyIndex;
+ break;
+ }
+ }
+ // Not all properties are displayed, so another loop to search for the first one that maps
+ // to a existing propertyrow
+ for (; thePropertyIndex < thePropertyCount; ++thePropertyIndex) {
+ TPropertyBindingMap::iterator theNextPropIter =
+ m_PropertyBindingMap.find(theProperties[thePropertyIndex]);
+ if (theNextPropIter != m_PropertyBindingMap.end()) {
+ theNextProperty = theNextPropIter->second;
+ break;
+ }
+ }
+ }
+ // Create a new property row
+ m_TransMgr->CreateNewPropertyRow(theTimelineProperty, m_Row,
+ theNextProperty ? theNextProperty->GetRow() : nullptr);
+
+ // Update keyframes
+ AddKeyframes(theTimelineProperty);
+}
+
+void CUICDMTimelineItemBinding::RemovePropertyRow(CUICDMPropertyHandle inPropertyHandle)
+{
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle);
+ if (theIter != m_PropertyBindingMap.end()) {
+ ITimelineItemProperty *thePropertyBinding = theIter->second;
+
+ bool theUpdateUI = DeleteAssetKeyframesWhereApplicable(thePropertyBinding);
+
+ m_TransMgr->RemovePropertyRow(thePropertyBinding);
+ m_PropertyBindingMap.erase(theIter);
+
+ // UI must update
+ if (m_Row && theUpdateUI) {
+ m_Row->OnChildVisibilityChanged();
+ m_Row->GetTimebar()->SetDirty(true);
+ }
+ }
+}
+
+// called when a keyframe is inserted, deleted or updated in the data model
+void CUICDMTimelineItemBinding::RefreshPropertyKeyframe(
+ UICDM::CUICDMPropertyHandle inPropertyHandle, UICDM::CUICDMKeyframeHandle inKeyframe,
+ ETimelineKeyframeTransaction inTransaction)
+{
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle);
+ if (theIter != m_PropertyBindingMap.end()) {
+ CUICDMTimelineItemProperty *theProperty = theIter->second;
+ if (theProperty) {
+ if (theProperty->RefreshKeyframe(inKeyframe, inTransaction)) {
+ // Update asset keyframes
+ UpdateKeyframe(theProperty->GetKeyframeByHandle(inKeyframe), inTransaction);
+ if (m_Row) // UI update
+ m_Row->GetTimebar()->SetDirty(true);
+ }
+ }
+ }
+}
+
+// called when the keyframes are updated in the UI and data model hasn't committed the change, ie no
+// event callback from UICDM
+void CUICDMTimelineItemBinding::UIRefreshPropertyKeyframe(long inOffset)
+{
+ if (!m_Row)
+ return;
+
+ // TODO: figure out a better way to sync m_Keyframes
+ TAssetKeyframeList::iterator theKeyIter = m_Keyframes.begin();
+ for (; theKeyIter != m_Keyframes.end(); ++theKeyIter) {
+ if (theKeyIter->IsSelected())
+ theKeyIter->UpdateTime(theKeyIter->GetTime() + inOffset);
+ }
+ // If a asset keyframe was "shared" by several properties' keyframes
+ // we need to 'break' this sharing and create for the remaining unmoved keyframes.
+ TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter) {
+ (*theIter).second->RefreshKeyFrames();
+
+ for (long i = 0; i < theIter->second->GetKeyframeCount(); ++i) {
+ IKeyframe *theKeyframe = theIter->second->GetKeyframeByIndex(i);
+ UpdateKeyframe(theKeyframe, ETimelineKeyframeTransaction_Add);
+
+ // Unfortunately, this is the way we can propagate UI updates to ALL selected keyframes
+ if (theKeyframe->IsSelected()) {
+ CPropertyRow *thePropertyRow = theIter->second->GetRow();
+ if (thePropertyRow)
+ thePropertyRow->GetTimebar()->SetDirty(true);
+ }
+ }
+ }
+ m_Row->GetTimebar()->SetDirty(true);
+}
+
+void CUICDMTimelineItemBinding::OnPropertyChanged(CUICDMPropertyHandle inPropertyHandle)
+{ // Refresh UI
+ CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge();
+ if (m_Row && (inPropertyHandle == theBridge->GetNameProperty()
+ || inPropertyHandle == theBridge->GetSceneAsset().m_Eyeball
+ || inPropertyHandle == theBridge->GetSceneAsset().m_Locked
+ || inPropertyHandle == theBridge->GetSceneAsset().m_Shy
+ || inPropertyHandle == theBridge->GetSceneAsset().m_StartTime
+ || inPropertyHandle == theBridge->GetSceneAsset().m_EndTime))
+ m_Row->OnDirty();
+}
+
+void CUICDMTimelineItemBinding::OnPropertyLinked(CUICDMPropertyHandle inPropertyHandle)
+{
+ if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(m_DataHandle, inPropertyHandle)) {
+ // Refresh property row by delete and recreate
+ RemovePropertyRow(inPropertyHandle);
+ AddPropertyRow(inPropertyHandle);
+ }
+}
+
+bool CUICDMTimelineItemBinding::HasDynamicKeyframes(long inTime)
+{
+ if (inTime == -1) {
+ if (GetPropertyCount() == 0)
+ return false;
+
+ for (long i = 0; i < GetPropertyCount(); ++i) {
+ ITimelineItemProperty *theTimelineItemProperty = GetProperty(i);
+ if (!theTimelineItemProperty->IsDynamicAnimation())
+ return false;
+ }
+ return true;
+ } else {
+ TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter) {
+ IKeyframe *theKeyframe = theIter->second->GetKeyframeByTime(inTime);
+ if (theKeyframe && theKeyframe->IsDynamic())
+ return true;
+ }
+ }
+ return false;
+}
+
+void CUICDMTimelineItemBinding::SetDynamicKeyframes(long inTime, bool inDynamic)
+{
+ TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter) {
+ IKeyframe *theKeyframe = theIter->second->GetKeyframeByTime(inTime);
+ if (theKeyframe)
+ theKeyframe->SetDynamic(inDynamic); // TODO: we want this in 1 batch command
+ }
+}
+
+// Update UI on the selection state of all keyframes on this row and all its properties' keyframes.
+void CUICDMTimelineItemBinding::DoSelectKeyframes(bool inSelected, long inTime, bool inUpdateUI)
+{
+ if (inTime == -1) // all keyframes
+ {
+ TAssetKeyframeList::iterator theKeyIter = m_Keyframes.begin();
+ for (; theKeyIter != m_Keyframes.end(); ++theKeyIter)
+ theKeyIter->SetSelected(inSelected);
+ } else {
+ CUICDMAssetTimelineKeyframe *theKeyframe =
+ dynamic_cast<CUICDMAssetTimelineKeyframe *>(GetKeyframeByTime(inTime));
+ if (theKeyframe)
+ theKeyframe->SetSelected(inSelected);
+ }
+ if (inUpdateUI && m_Row)
+ m_Row->GetTimebar()->SelectKeysByTime(-1, inSelected);
+
+ // legacy feature: all properties with keyframes at inTime or all if inTime is -1 are selected
+ // as well.
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter)
+ theIter->second->DoSelectKeyframes(inSelected, inTime, true, this);
+}
+
+// When selecting by mouse-drag, if all properties are selected, select the asset keyframe. And if
+// one gets de-selected, de-select. Legacy feature.
+// Note that if only 1 property has a keyframe at time t, the asset keyframe gets selected
+// automatically when that keyframe is selected. Its odd to me but
+// that's how it has always behaved.
+void CUICDMTimelineItemBinding::OnPropertySelection(long inTime)
+{
+ IKeyframe *theAssetKeyframe = GetKeyframeByTime(inTime);
+ if (theAssetKeyframe) {
+ bool theAllSelectedFlag = true;
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter) {
+ IKeyframe *theKeyframe = theIter->second->GetKeyframeByTime(inTime);
+ if (theKeyframe && !theKeyframe->IsSelected()) // done, i.e selection remain unchanged.
+ {
+ theAllSelectedFlag = false;
+ break;
+ }
+ }
+ if (theAssetKeyframe->IsSelected() != theAllSelectedFlag) {
+ dynamic_cast<CUICDMAssetTimelineKeyframe *>(theAssetKeyframe)
+ ->SetSelected(theAllSelectedFlag);
+ // Update UI
+ if (m_Row)
+ m_Row->GetTimebar()->SelectKeysByTime(inTime, theAllSelectedFlag);
+ }
+ }
+}
+
+Q3DStudio::CId CUICDMTimelineItemBinding::GetGuid() const
+{
+ CClientDataModelBridge *theClientBridge = m_StudioSystem->GetClientDataModelBridge();
+ UICDM::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem();
+ SValue theValue;
+ if (thePropertySystem->GetInstancePropertyValue(m_DataHandle, theClientBridge->GetIdProperty(),
+ theValue)) {
+ SLong4 theLong4 = UICDM::get<SLong4>(theValue);
+ return Q3DStudio::CId(theLong4.m_Longs[0], theLong4.m_Longs[1], theLong4.m_Longs[2],
+ theLong4.m_Longs[3]);
+ }
+ return Q3DStudio::CId();
+}
+
+// Delete asset keyframes at time t if no property keyframes exist at time t
+//@param inSkipPropertyBinding property that to skip, e.g. in cases where property is deleted
+//@return true if there are asset keyframes deleted.
+bool CUICDMTimelineItemBinding::DeleteAssetKeyframesWhereApplicable(
+ ITimelineItemProperty *inSkipPropertyBinding /*= nullptr */)
+{
+ // iterate through m_Keyframes because we cannot obtain time information from the Animation
+ // keyframes anymore, since they are deleted.
+ std::vector<long> theDeleteIndicesList;
+ for (size_t theIndex = 0; theIndex < m_Keyframes.size(); ++theIndex) {
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter) {
+ if ((!inSkipPropertyBinding || theIter->second != inSkipPropertyBinding)
+ && theIter->second->GetKeyframeByTime(m_Keyframes[theIndex].GetTime())) // done!
+ break;
+ }
+ if (theIter == m_PropertyBindingMap.end())
+ theDeleteIndicesList.push_back((long)theIndex);
+ }
+ // start with the last item, so that the indices remain valid.
+ for (long i = (long)theDeleteIndicesList.size() - 1; i >= 0; --i) {
+ TAssetKeyframeList::iterator theKeyIter = m_Keyframes.begin();
+ std::advance(theKeyIter, theDeleteIndicesList[i]);
+ m_Keyframes.erase(theKeyIter);
+ }
+
+ return !theDeleteIndicesList.empty();
+}
+
+void CUICDMTimelineItemBinding::RemoveAllPropertyBindings()
+{
+ TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin();
+ for (; theIter != m_PropertyBindingMap.end(); ++theIter)
+ delete theIter->second;
+ m_PropertyBindingMap.clear();
+}
+
+void CUICDMTimelineItemBinding::AddKeyframes(ITimelineItemProperty *inPropertyBinding)
+{
+ for (long i = 0; i < inPropertyBinding->GetKeyframeCount(); ++i)
+ UpdateKeyframe(inPropertyBinding->GetKeyframeByIndex(i), ETimelineKeyframeTransaction_Add);
+}
+
+// Update the asset keyframes based on the properties' keyframes.
+void CUICDMTimelineItemBinding::UpdateKeyframe(IKeyframe *inKeyframe,
+ ETimelineKeyframeTransaction inTransaction)
+{
+ bool theDoAddFlag = (inTransaction == ETimelineKeyframeTransaction_Add);
+ bool theDoDeleteFlag = (inTransaction == ETimelineKeyframeTransaction_Delete);
+
+ // For update, if there isn't already a asset keyframe at the associated time, create one
+ if (inTransaction == ETimelineKeyframeTransaction_Update) {
+ theDoAddFlag = inKeyframe && !GetKeyframeByTime(inKeyframe->GetTime());
+ theDoDeleteFlag = true; // plus, since we don't keep track of indiviual property keyframes
+ // here, iterate and make sure list is correct.
+ }
+
+ if (theDoDeleteFlag)
+ DeleteAssetKeyframesWhereApplicable();
+
+ // Add when a new keyframe is added or MAYBE when a keyframe is moved
+ if (theDoAddFlag && inKeyframe) {
+ long theKeyframeTime = inKeyframe->GetTime();
+ if (theKeyframeTime >= 0) {
+ bool theAppend = true;
+ // insert this in the order that it should be. and we trust the
+ TAssetKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ long theTime = (*theIter).GetTime();
+ if (theTime == theKeyframeTime) {
+ theAppend = false;
+ break; // already exists, we are done. Because we only need 1 to represent ALL
+ // properties
+ }
+ }
+ if (theAppend)
+ m_Keyframes.push_back(CUICDMAssetTimelineKeyframe(this, theKeyframeTime));
+ }
+ }
+ if (m_Row && (theDoAddFlag
+ || inTransaction == ETimelineKeyframeTransaction_DynamicChanged)) // dynamic =>
+ // only UI needs
+ // to refresh
+ m_Row->GetTimebar()->SetDirty(true);
+}
+
+void CUICDMTimelineItemBinding::OnAddChild(CUICDMInstanceHandle inInstance)
+{
+ CDoc *theDoc = m_TransMgr->GetDoc();
+ CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge();
+ ISlideSystem *theSlideSystem = m_StudioSystem->GetSlideSystem();
+
+ UICDM::CUICDMSlideHandle theSlide = theSlideSystem->GetAssociatedSlide(inInstance);
+ if (theBridge->IsInActiveComponent(inInstance)
+ && (theSlideSystem->IsMasterSlide(theSlide) || theSlide == theDoc->GetActiveSlide())) {
+ // Only add if the asset is in the current active component, and it's a master asset or in
+ // the current slide
+ ITimelineItemBinding *theNextItem = nullptr;
+ UICDM::CUICDMInstanceHandle theParentInstance = GetInstance();
+ // Figure out where to insert this row, if applicable.
+ // CAsset has a list of children, and not necessarily all are active in this slide (e.g.
+ // non-master children)
+ Q3DStudio::TIdentifier theNextChild = 0;
+ if (theParentInstance.Valid()) {
+ // Get the next prioritized child in the same slide
+ Q3DStudio::CGraphIterator theChildren;
+ GetAssetChildrenInSlide(theDoc, theParentInstance, theDoc->GetActiveSlide(),
+ theChildren);
+ theNextChild = GetSibling(inInstance, true, theChildren);
+ }
+
+ if (theNextChild != 0)
+ theNextItem = m_TransMgr->GetOrCreate(theNextChild);
+
+ m_Row->AddChildRow(m_TransMgr->GetOrCreate(inInstance), theNextItem);
+ }
+}
+
+void CUICDMTimelineItemBinding::OnDeleteChild(CUICDMInstanceHandle inInstance)
+{
+ ITimelineItemBinding *theChild = m_TransMgr->GetOrCreate(inInstance);
+ if (theChild) {
+ m_Row->RemoveChildRow(theChild);
+ }
+}
+
+void CUICDMTimelineItemBinding::UpdateActionStatus()
+{
+ if (m_Row)
+ m_Row->UpdateActionStatus();
+}
+
+//=============================================================================
+/**
+ * Open the associated item as though it was double-clicked in explorer
+ * Respective subclasses (for example Image and Behavior) can call this function
+ */
+bool CUICDMTimelineItemBinding::OpenSourcePathFile()
+{
+ // Get source path property value
+ CClientDataModelBridge *theClientBridge = m_StudioSystem->GetClientDataModelBridge();
+ UICDM::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem();
+ SValue theValue;
+ if (thePropertySystem->GetInstancePropertyValue(
+ m_DataHandle, theClientBridge->GetSourcePathProperty(), theValue)) {
+ // Open the respective file
+ Q3DStudio::CFilePath theSourcePath(UICDM::get<UICDM::TDataStrPtr>(theValue)->GetData());
+ CUICFile theFile(m_TransMgr->GetDoc()->GetResolvedPathToDoc(theSourcePath));
+ theFile.Execute();
+ return true;
+ }
+ return false;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.h
new file mode 100644
index 00000000..2866d698
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemBinding.h
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_UICDM_TIMELINEITEM_BINDING_H
+#define INCLUDED_UICDM_TIMELINEITEM_BINDING_H 1
+
+#pragma once
+
+#include "ITimelineItemBinding.h"
+#include "ITimelineItem.h"
+
+// Data model
+#include "UICDMHandles.h"
+#include "IDragable.h"
+#include "UICDMAssetTimelineKeyframe.h"
+#include "OffsetKeyframesCommandHelper.h"
+#include "UICDMTimeline.h"
+#include "UICDMSignals.h"
+#include "DispatchListeners.h"
+
+//==============================================================================
+// Classes
+//==============================================================================
+class CTimelineTranslationManager;
+class CBaseStateRow;
+class CUICDMTimelineItemProperty;
+class CCmdDataModelSetKeyframeTime;
+
+namespace UICDM {
+class CStudioSystem;
+}
+
+//=============================================================================
+/**
+ * Binding to generic UICDM object
+ */
+class CUICDMTimelineItemBinding : public ITimelineItemBinding,
+ public ITimelineItem,
+ public IDragable,
+ public IDataModelListener
+
+{
+protected: // Typedef
+ typedef std::map<UICDM::CUICDMPropertyHandle, CUICDMTimelineItemProperty *> TPropertyBindingMap;
+ typedef std::vector<CUICDMAssetTimelineKeyframe> TAssetKeyframeList;
+
+protected:
+ CBaseStateRow *m_Row;
+ CTimelineTranslationManager *m_TransMgr;
+ UICDM::CUICDMInstanceHandle m_DataHandle;
+ ITimelineItemBinding *m_Parent;
+ ITimelineTimebar *m_TimelineTimebar;
+ TPropertyBindingMap m_PropertyBindingMap;
+ TAssetKeyframeList m_Keyframes; /// Sorted (by time) list of keyframes
+ UICDM::CStudioSystem *m_StudioSystem;
+
+ UICDM::TSignalConnectionPtr m_StartTimeConnection;
+ UICDM::TSignalConnectionPtr m_EndTimeConnection;
+
+public:
+ CUICDMTimelineItemBinding(CTimelineTranslationManager *inMgr,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ CUICDMTimelineItemBinding(CTimelineTranslationManager *inMgr);
+ virtual ~CUICDMTimelineItemBinding();
+
+protected:
+ bool UICDMGetBoolean(UICDM::CUICDMPropertyHandle inProperty) const;
+ void UICDMSetBoolean(UICDM::CUICDMPropertyHandle inProperty, bool inValue,
+ const QString &inNiceText) const;
+ void SetInstanceHandle(UICDM::CUICDMInstanceHandle inDataHandle);
+
+public:
+ // ITimelineItem
+ EStudioObjectType GetObjectType() const override;
+ bool IsMaster() const override;
+ bool IsShy() const override;
+ void SetShy(bool) override;
+ bool IsLocked() const override;
+ void SetLocked(bool) override;
+ bool IsVisible() const override;
+ void SetVisible(bool) override;
+ bool IsExpanded() const override;
+ void SetExpanded(bool inExpanded) override;
+ bool HasAction(bool inMaster) override;
+ bool ChildrenHasAction(bool inMaster) override;
+ bool ComponentHasAction(bool inMaster) override;
+ ITimelineTimebar *GetTimebar() override;
+
+ // INamable
+ Q3DStudio::CString GetName() const override;
+ void SetName(const Q3DStudio::CString &inName) override;
+
+ // ITimelineItemBinding
+ ITimelineItem *GetTimelineItem() override;
+ CBaseStateRow *GetRow() override;
+ void SetSelected(bool inMultiSelect) override;
+ void OnCollapsed() override;
+ void ClearKeySelection() override;
+ bool OpenAssociatedEditor() override;
+ void DoStartDrag(CControlWindowListener *inWndListener) override;
+ void SetDropTarget(CDropTarget *inTarget) override;
+ // Hierarchy
+ long GetChildrenCount() override;
+ ITimelineItemBinding *GetChild(long inIndex) override;
+ ITimelineItemBinding *GetParent() override;
+ void SetParent(ITimelineItemBinding *parent) override;
+ // Properties
+ long GetPropertyCount() override;
+ ITimelineItemProperty *GetProperty(long inIndex) override;
+ // Eye/Lock toggles
+ bool ShowToggleControls() const override;
+ bool IsLockedEnabled() const override;
+ bool IsVisibleEnabled() const override;
+ // Init/Cleanup
+ void Bind(CBaseStateRow *inRow) override;
+ void Release() override;
+ // ContextMenu
+ bool IsValidTransaction(EUserTransaction inTransaction) override;
+ void PerformTransaction(EUserTransaction inTransaction) override;
+ Q3DStudio::CString GetObjectPath() override;
+ // Selected keyframes
+ ITimelineKeyframesManager *GetKeyframesManager() const override;
+ // Properties
+ void RemoveProperty(ITimelineItemProperty *inProperty) override;
+ void LoadProperties() override;
+
+ // ITimelineItemKeyframesHolder
+ void InsertKeyframe() override;
+ void DeleteAllChannelKeyframes() override;
+ long GetKeyframeCount() const override;
+ IKeyframe *GetKeyframeByTime(long inTime) const override;
+ IKeyframe *GetKeyframeByIndex(long inIndex) const override;
+ long OffsetSelectedKeyframes(long inOffset) override;
+ void CommitChangedKeyframes() override;
+ void OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation) override;
+
+ // IKeyframeSelector
+ void SelectKeyframes(bool inSelected, long inTime = -1) override;
+
+ // IUICDMSelectable
+ virtual UICDM::CUICDMInstanceHandle GetInstanceHandle() const;
+
+ // IDragable
+ long GetFlavor() const override;
+
+ void OnBeginDataModelNotifications() override;
+ void OnEndDataModelNotifications() override;
+ void OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance) override;
+ void OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance,
+ long inInstanceCount) override;
+ void RefreshStateRow(bool inRefreshChildren = false);
+
+ virtual void AddPropertyRow(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ bool inAppend = false);
+ virtual void RemovePropertyRow(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ virtual void RefreshPropertyKeyframe(UICDM::CUICDMPropertyHandle inPropertyHandle,
+ UICDM::CUICDMKeyframeHandle,
+ ETimelineKeyframeTransaction inTransaction);
+ virtual void OnPropertyChanged(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ virtual void OnPropertyLinked(UICDM::CUICDMPropertyHandle inPropertyHandle);
+
+ virtual void UIRefreshPropertyKeyframe(long inOffset);
+ // Keyframe manipulation
+ virtual bool HasDynamicKeyframes(long inTime);
+ virtual void SetDynamicKeyframes(long inTime, bool inDynamic);
+ virtual void DoSelectKeyframes(bool inSelected, long inTime, bool inUpdateUI);
+ virtual void OnPropertySelection(long inTime);
+
+ virtual void OnAddChild(UICDM::CUICDMInstanceHandle inInstance);
+ virtual void OnDeleteChild(UICDM::CUICDMInstanceHandle inInstance);
+
+ void UpdateActionStatus();
+
+ Q3DStudio::CId GetGuid() const;
+
+ // Bridge between asset & UICDM. Ideally we should be fully UICDM
+ virtual UICDM::CUICDMInstanceHandle GetInstance() const;
+
+protected:
+ virtual ITimelineTimebar *CreateTimelineTimebar();
+ ITimelineItemProperty *GetPropertyBinding(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ ITimelineItemProperty *GetOrCreatePropertyBinding(UICDM::CUICDMPropertyHandle inPropertyHandle);
+ void RemoveAllPropertyBindings();
+ void AddKeyframes(ITimelineItemProperty *inPropertyBinding);
+ bool
+ DeleteAssetKeyframesWhereApplicable(ITimelineItemProperty *inTriggerPropertyBinding = nullptr);
+ void UpdateKeyframe(IKeyframe *inKeyframe, ETimelineKeyframeTransaction inTransaction);
+
+ // For iterating through children
+ virtual bool AmITimeParent() const { return false; }
+
+ // subclasses can call this method to open referenced files
+ virtual bool OpenSourcePathFile();
+};
+
+#endif // INCLUDED_UICDM_TIMELINEITEM_BINDING_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.cpp
new file mode 100644
index 00000000..c09fca25
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.cpp
@@ -0,0 +1,579 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMTimelineItemProperty.h"
+#include "PropertyRow.h"
+#include "TimelineTranslationManager.h"
+#include "ITimelineItemBinding.h"
+#include "PropertyTimebarRow.h"
+#include "UICDMTimelineItemBinding.h"
+#include "UICDMTimelineKeyframe.h"
+#include "KeyframesManager.h"
+#include "CmdDataModelChangeKeyframe.h"
+#include "CmdDataModelRemoveKeyframe.h"
+#include "StudioApp.h"
+#include "Core.h"
+
+// Link to data model
+#include "TimeEditDlg.h"
+#include "ClientDataModelBridge.h"
+#include "UICDMSlides.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMAnimation.h"
+#include "UICDMMetaData.h"
+#include "UICDMPropertyDefinition.h"
+#include "UICDMDataCore.h"
+#include "StudioFullSystem.h"
+#include <boost/bind.hpp>
+using namespace UICDM;
+
+bool SortKeyframeByTime(const CUICDMTimelineKeyframe *inLHS, const CUICDMTimelineKeyframe *inRHS)
+{
+ return inLHS->GetTime() < inRHS->GetTime();
+}
+
+// UICDM stores it from 0..1, UI expects 0..255
+inline float UICDMToColor(float inValue)
+{
+ return inValue * 255;
+}
+
+CUICDMTimelineItemProperty::CUICDMTimelineItemProperty(CTimelineTranslationManager *inTransMgr,
+ CUICDMPropertyHandle inPropertyHandle,
+ CUICDMInstanceHandle inInstance)
+ : m_Row(nullptr)
+ , m_InstanceHandle(inInstance)
+ , m_PropertyHandle(inPropertyHandle)
+ , m_TransMgr(inTransMgr)
+ , m_SetKeyframeValueCommand(nullptr)
+{
+ // Cache all the animation handles because we need them for any keyframes manipulation.
+ // the assumption is that all associated handles are created all at once (i.e. we do not need to
+ // add or delete from this list )
+ CreateKeyframes();
+ InitializeCachedVariables(inInstance);
+ m_Signals.push_back(
+ m_TransMgr->GetStudioSystem()->GetFullSystem()->GetSignalProvider()->ConnectPropertyLinked(
+ boost::bind(&CUICDMTimelineItemProperty::OnPropertyLinkStatusChanged, this, _1, _2,
+ _3)));
+
+ m_Signals.push_back(
+ m_TransMgr->GetStudioSystem()
+ ->GetFullSystem()
+ ->GetSignalProvider()
+ ->ConnectPropertyUnlinked(boost::bind(
+ &CUICDMTimelineItemProperty::OnPropertyLinkStatusChanged, this, _1, _2, _3)));
+}
+
+CUICDMTimelineItemProperty::~CUICDMTimelineItemProperty()
+{
+ ReleaseKeyframes();
+ Release();
+}
+
+void CUICDMTimelineItemProperty::CreateKeyframes()
+{
+ // Cache all the animation handles because we need them for any keyframes manipulation.
+ // the assumption is that all associated handles are created all at once (i.e. we do not need to
+ // add or delete from this list )
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ DataModelDataType::Value theDataType = thePropertySystem->GetDataType(m_PropertyHandle);
+ IStudioAnimationSystem *theAnimationSystem =
+ m_TransMgr->GetStudioSystem()->GetAnimationSystem();
+ std::tuple<bool, size_t> theArity = GetDatatypeAnimatableAndArity(theDataType);
+ for (size_t i = 0; i < std::get<1>(theArity); ++i) {
+ CUICDMAnimationHandle theAnimationHandle =
+ theAnimationSystem->GetControllingAnimation(m_InstanceHandle, m_PropertyHandle, i);
+ if (theAnimationHandle.Valid())
+ m_AnimationHandles.push_back(theAnimationHandle);
+ }
+ if (!m_AnimationHandles.empty()) { // update wrappers for keyframes
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+ TKeyframeHandleList theKeyframes;
+ // all channels have keyframes at the same time
+ theAnimationCore->GetKeyframes(m_AnimationHandles[0], theKeyframes);
+ for (size_t i = 0; i < theKeyframes.size(); ++i)
+ CreateKeyframeIfNonExistent(theKeyframes[i], m_AnimationHandles[0]);
+ }
+}
+
+void CUICDMTimelineItemProperty::ReleaseKeyframes()
+{
+ // clear any selection from m_TransMgr
+ TKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ m_TransMgr->GetKeyframesManager()->SetKeyframeSelected(*theIter, false);
+
+ SAFE_DELETE(*theIter);
+ }
+ m_Keyframes.clear();
+ m_AnimationHandles.clear();
+}
+
+// Type doesn't change and due to the logic required to figure this out, cache it.
+void CUICDMTimelineItemProperty::InitializeCachedVariables(UICDM::CUICDMInstanceHandle inInstance)
+{
+ using namespace Q3DStudio;
+ UICDM::IPropertySystem *thePropertySystem = m_TransMgr->GetStudioSystem()->GetPropertySystem();
+
+ m_Type.first = thePropertySystem->GetDataType(m_PropertyHandle);
+ m_Type.second = thePropertySystem->GetAdditionalMetaDataType(inInstance, m_PropertyHandle);
+
+ // Name doesn't change either.
+ TCharStr theFormalName = thePropertySystem->GetFormalName(inInstance, m_PropertyHandle);
+
+ if (theFormalName.empty()) // fallback on property name
+ theFormalName = thePropertySystem->GetName(m_PropertyHandle);
+ m_Name = theFormalName.c_str();
+}
+
+Q3DStudio::CString CUICDMTimelineItemProperty::GetName() const
+{
+ return m_Name;
+}
+
+// Helper function to retrieve the parent binding class.
+inline ITimelineItemBinding *GetParentBinding(CPropertyRow *inRow)
+{
+ ITimelineItemBinding *theParentBinding = nullptr;
+ if (inRow) {
+ CBaseStateRow *theParentRow = inRow->GetParentRow();
+ if (theParentRow) {
+ theParentBinding = theParentRow->GetTimelineItemBinding();
+ ASSERT(theParentBinding); // TimelineItemBinding should be set properly during
+ // CBaseStateRow::Initialize
+ }
+ }
+ return theParentBinding;
+}
+
+bool CUICDMTimelineItemProperty::IsMaster() const
+{
+ if (m_Row) {
+ if (CUICDMTimelineItemBinding *theParentBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetParentBinding(m_Row)))
+ return m_TransMgr->GetDoc()->GetDocumentReader().IsPropertyLinked(
+ theParentBinding->GetInstanceHandle(), m_PropertyHandle);
+ }
+ return false;
+}
+
+UICDM::TDataTypePair CUICDMTimelineItemProperty::GetType() const
+{
+ return m_Type;
+}
+
+void CompareAndSet(const CUICDMTimelineKeyframe *inKeyframe, float &outRetValue, bool inGreaterThan)
+{
+ float theValue = (inGreaterThan) ? inKeyframe->GetMaxValue() : inKeyframe->GetMinValue();
+ if ((inGreaterThan && theValue > outRetValue) || (!inGreaterThan && theValue < outRetValue))
+ outRetValue = theValue;
+}
+
+// return the max value of the current set of keyframes
+float CUICDMTimelineItemProperty::GetMaximumValue() const
+{
+ float theRetVal = FLT_MIN;
+ do_all(m_Keyframes, boost::bind(CompareAndSet, _1, boost::ref(theRetVal), true));
+ if (m_Type.first == DataModelDataType::Float3 && m_Type.second == AdditionalMetaDataType::Color)
+ theRetVal = UICDMToColor(theRetVal);
+ return theRetVal;
+}
+
+// return the min value of the current set of keyframes
+float CUICDMTimelineItemProperty::GetMinimumValue() const
+{
+ float theRetVal = FLT_MAX;
+ do_all(m_Keyframes, boost::bind(CompareAndSet, _1, boost::ref(theRetVal), false));
+ if (m_Type.first == DataModelDataType::Float3 && m_Type.second == AdditionalMetaDataType::Color)
+ theRetVal = UICDMToColor(theRetVal);
+ return theRetVal;
+}
+
+void CUICDMTimelineItemProperty::Bind(CPropertyRow *inRow)
+{
+ ASSERT(!m_Row);
+
+ m_Row = inRow;
+}
+
+void CUICDMTimelineItemProperty::Release()
+{
+ m_Row = nullptr;
+}
+
+// Ensures the object that owns this property is selected.
+void CUICDMTimelineItemProperty::SetSelected()
+{
+ if (m_Row) {
+ ITimelineItemBinding *theParentBinding = GetParentBinding(m_Row);
+ if (theParentBinding)
+ theParentBinding->SetSelected(false);
+ }
+}
+
+void CUICDMTimelineItemProperty::ClearKeySelection()
+{
+ m_TransMgr->ClearKeyframeSelection();
+}
+
+void CUICDMTimelineItemProperty::DeleteAllKeys()
+{
+ if (m_Keyframes.empty())
+ return;
+
+ using namespace Q3DStudio;
+
+ ScopedDocumentEditor editor(*m_TransMgr->GetDoc(), L"Delete All Keyframes", __FILE__, __LINE__);
+ for (size_t idx = 0, end = m_AnimationHandles.size(); idx < end; ++idx)
+ editor->DeleteAllKeyframes(m_AnimationHandles[idx]);
+}
+
+ITimelineKeyframesManager *CUICDMTimelineItemProperty::GetKeyframesManager() const
+{
+ return m_TransMgr->GetKeyframesManager();
+}
+
+IKeyframe *CUICDMTimelineItemProperty::GetKeyframeByTime(long inTime) const
+{
+ std::vector<long> theTest;
+ TKeyframeList::const_iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ if ((*theIter)->GetTime() == inTime)
+ return (*theIter);
+
+ theTest.push_back((*theIter)->GetTime());
+ }
+ // if key had been deleted, this returns nullptr
+ return nullptr;
+}
+
+IKeyframe *CUICDMTimelineItemProperty::GetKeyframeByIndex(long inIndex) const
+{
+ if (inIndex >= 0 && inIndex < (long)m_Keyframes.size())
+ return m_Keyframes[inIndex];
+
+ ASSERT(0); // should not happen
+ return nullptr;
+}
+
+long CUICDMTimelineItemProperty::GetKeyframeCount() const
+{
+ // this list is updated in constructor and when keyframes are added or deleted.
+ return (long)m_Keyframes.size();
+}
+
+long CUICDMTimelineItemProperty::GetChannelCount() const
+{
+ return (long)m_AnimationHandles.size();
+}
+
+float CUICDMTimelineItemProperty::GetChannelValueAtTime(long inChannelIndex, long inTime)
+{
+ // if no keyframes, get current property value.
+ if (m_Keyframes.empty()) {
+ CUICDMTimelineItemBinding *theParentBinding =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetParentBinding(m_Row));
+ if (theParentBinding) {
+
+ SValue theValue;
+ UICDM::IPropertySystem *thePropertySystem =
+ m_TransMgr->GetStudioSystem()->GetPropertySystem();
+ thePropertySystem->GetInstancePropertyValue(theParentBinding->GetInstanceHandle(),
+ m_PropertyHandle, theValue);
+ switch (m_Type.first) {
+ case DataModelDataType::Float3: {
+ if (m_Type.second == AdditionalMetaDataType::Color) {
+ SFloat3 theFloat3 = UICDM::get<SFloat3>(theValue);
+ if (inChannelIndex >= 0 && inChannelIndex < 3)
+ return UICDMToColor(theFloat3[inChannelIndex]);
+ } else {
+ SFloat3 theFloat3 = UICDM::get<SFloat3>(theValue);
+ if (inChannelIndex >= 0 && inChannelIndex < 3)
+ return theFloat3[inChannelIndex];
+ }
+ break;
+ }
+ case DataModelDataType::Float2: {
+ SFloat2 theFloat2 = UICDM::get<SFloat2>(theValue);
+ if (inChannelIndex >= 0 && inChannelIndex < 2)
+ return theFloat2[inChannelIndex];
+ break;
+ }
+ case DataModelDataType::Float:
+ return UICDM::get<float>(theValue);
+ break;
+ default: // TODO: handle other types
+ break;
+ }
+ }
+ }
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+ if (!m_AnimationHandles.empty() && inChannelIndex >= 0
+ && inChannelIndex < (long)m_AnimationHandles.size()) {
+ float theValue = theAnimationCore->EvaluateAnimation(
+ m_AnimationHandles[inChannelIndex], CUICDMTimelineKeyframe::GetTimeInSecs(inTime));
+ if (m_Type.first == DataModelDataType::Float3
+ && m_Type.second == AdditionalMetaDataType::Color)
+ theValue = UICDMToColor(theValue);
+
+ return theValue;
+ }
+ return 0.f;
+}
+
+void CUICDMTimelineItemProperty::SetChannelValueAtTime(long inChannelIndex, long inTime,
+ float inValue)
+{
+ using namespace boost;
+ CUICDMTimelineKeyframe *theKeyframeWrapper =
+ dynamic_cast<CUICDMTimelineKeyframe *>(GetKeyframeByTime(inTime));
+ if (theKeyframeWrapper) {
+ CUICDMTimelineKeyframe::TKeyframeHandleList theKeyframes;
+ theKeyframeWrapper->GetKeyframeHandles(theKeyframes);
+ if (!theKeyframes.empty() && inChannelIndex < (long)theKeyframes.size()) {
+ inValue /= 255;
+ if (!m_SetKeyframeValueCommand)
+ m_SetKeyframeValueCommand = new CCmdDataModelSetKeyframeValue(
+ g_StudioApp.GetCore()->GetDoc(), theKeyframes[inChannelIndex], inValue);
+ m_SetKeyframeValueCommand->Update(inValue);
+ }
+ }
+}
+
+long CUICDMTimelineItemProperty::OffsetSelectedKeyframes(long inOffset)
+{
+ long theRetVal = m_TransMgr->GetKeyframesManager()->OffsetSelectedKeyframes(inOffset);
+ if (m_Row) // UI update, since the data model sends no event while the change isn't commited.
+ {
+ m_Row->Refresh();
+ }
+ return theRetVal;
+}
+
+void CUICDMTimelineItemProperty::CommitChangedKeyframes()
+{
+ if (m_SetKeyframeValueCommand) { // if this is moving a keyframe value
+ g_StudioApp.GetCore()->ExecuteCommand(m_SetKeyframeValueCommand, false);
+ m_SetKeyframeValueCommand = nullptr;
+ } else // otherwise its changing keyframe times
+ m_TransMgr->GetKeyframesManager()->CommitChangedKeyframes();
+}
+
+void CUICDMTimelineItemProperty::OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation)
+{
+ (void)inObjectAssociation;
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.SetKeyframesManager(m_TransMgr->GetKeyframesManager());
+ theTimeEditDlg.ShowDialog(inCurrentTime, 0, g_StudioApp.GetCore()->GetDoc(), ASSETKEYFRAME);
+}
+
+void CUICDMTimelineItemProperty::SelectKeyframes(bool inSelected, long inTime /*= -1 */)
+{
+ CUICDMTimelineItemBinding *theParent =
+ dynamic_cast<CUICDMTimelineItemBinding *>(GetParentBinding(m_Row));
+ DoSelectKeyframes(inSelected, inTime, false, theParent);
+}
+
+CPropertyRow *CUICDMTimelineItemProperty::GetRow()
+{
+ return m_Row;
+}
+
+bool CUICDMTimelineItemProperty::IsDynamicAnimation()
+{
+ return m_Keyframes.size() > 0 && m_Keyframes[0]->IsDynamic();
+}
+
+//=============================================================================
+/**
+ * For updating the UI when keyframes are added/updated/deleted.
+ */
+bool CUICDMTimelineItemProperty::RefreshKeyframe(UICDM::CUICDMKeyframeHandle inKeyframe,
+ ETimelineKeyframeTransaction inTransaction)
+{
+ bool theHandled = false;
+ switch (inTransaction) {
+ case ETimelineKeyframeTransaction_Delete: {
+ TKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ CUICDMTimelineKeyframe *theKeyframe = *theIter;
+ if (theKeyframe->HasKeyframeHandle(inKeyframe)) { // clear selection
+ m_TransMgr->GetKeyframesManager()->SetKeyframeSelected(theKeyframe, false);
+ m_Keyframes.erase(theIter);
+
+ theHandled = true;
+ break;
+ }
+ }
+ } break;
+ case ETimelineKeyframeTransaction_Add: {
+ ASSERT(!m_AnimationHandles.empty());
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+ CUICDMAnimationHandle theAnimationHandle =
+ theAnimationCore->GetAnimationForKeyframe(inKeyframe);
+ // only create for the first animation handle.
+ if (theAnimationHandle == m_AnimationHandles[0]) { // for undo/redo, the keyframes can be
+ // added in reverse, hence the need to
+ // sort
+ if (CreateKeyframeIfNonExistent(inKeyframe, theAnimationHandle))
+ std::stable_sort(m_Keyframes.begin(), m_Keyframes.end(), SortKeyframeByTime);
+ theHandled = true;
+ }
+ } break;
+ case ETimelineKeyframeTransaction_Update:
+ case ETimelineKeyframeTransaction_DynamicChanged:
+ theHandled = true;
+ break;
+ default:
+ return false;
+ }
+ if (theHandled && m_Row)
+ m_Row->Refresh();
+
+ return theHandled;
+}
+
+IKeyframe *CUICDMTimelineItemProperty::GetKeyframeByHandle(UICDM::CUICDMKeyframeHandle inKeyframe)
+{
+ TKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ CUICDMTimelineKeyframe *theKeyframe = *theIter;
+ if (theKeyframe->HasKeyframeHandle(inKeyframe))
+ return *theIter;
+ }
+ return nullptr;
+}
+
+// This is either triggered from this property's keyframe selection OR from a parent's keyframe
+// selection.
+void CUICDMTimelineItemProperty::DoSelectKeyframes(bool inSelected, long inTime,
+ bool inParentTriggered,
+ CUICDMTimelineItemBinding *inParent)
+{
+ // this is what it used to do before the refactor. selecting a keyframe always selects the
+ // asset.
+ if (inSelected)
+ SetSelected();
+
+ if (!inParent)
+ return;
+
+ if (inTime == -1) // all keyframes
+ {
+ TKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ (*theIter)->SetSelected(inSelected);
+ m_TransMgr->GetKeyframesManager()->SetKeyframeSelected(*theIter, inSelected, inParent);
+ }
+ } else {
+ CUICDMTimelineKeyframe *theKeyframe =
+ dynamic_cast<CUICDMTimelineKeyframe *>(GetKeyframeByTime(inTime));
+ if (theKeyframe) {
+ theKeyframe->SetSelected(inSelected);
+ m_TransMgr->GetKeyframesManager()->SetKeyframeSelected(theKeyframe, inSelected,
+ inParent);
+ }
+ }
+ // Requires UI to be updated explicitly
+ if (inParentTriggered && m_Row)
+ m_Row->GetTimebar()->SelectKeysByTime(inTime, inSelected);
+
+ // Support existing feature, selection by mouse-drag a rect, when all property keyframes are
+ // selected, the asset keyframe is automatically selected as well.
+ // and the mouse drags 'outside' a keyframe and de-selecting it, the asset keyframe has to be
+ // deselected as well.
+ if (!inParentTriggered && inTime != -1)
+ inParent->OnPropertySelection(inTime);
+}
+
+//=============================================================================
+/**
+ * Create a wrapper for this keyframe if doesn't exists.
+ * @return true if created, false if already exists.
+ */
+bool CUICDMTimelineItemProperty::CreateKeyframeIfNonExistent(
+ UICDM::CUICDMKeyframeHandle inKeyframeHandle, CUICDMAnimationHandle inOwningAnimation)
+{
+ TKeyframeList::iterator theIter = m_Keyframes.begin();
+ for (; theIter != m_Keyframes.end(); ++theIter) {
+ CUICDMTimelineKeyframe *theKeyframe = *theIter;
+ if (theKeyframe->HasKeyframeHandle(inKeyframeHandle))
+ return false;
+ }
+ // check for multiple channels => only create 1 CUICDMTimelineKeyframe
+ CUICDMTimelineKeyframe *theNewKeyframe =
+ new CUICDMTimelineKeyframe(g_StudioApp.GetCore()->GetDoc());
+ theNewKeyframe->AddKeyframeHandle(inKeyframeHandle);
+ if (m_AnimationHandles.size()
+ > 1) { // assert assumption that is only called for the first handle
+ ASSERT(m_AnimationHandles[0] == inOwningAnimation);
+ IAnimationCore *theAnimationCore = m_TransMgr->GetStudioSystem()->GetAnimationCore();
+ float theKeyframeTime = KeyframeTime(theAnimationCore->GetKeyframeData(inKeyframeHandle));
+ for (size_t i = 1; i < m_AnimationHandles.size(); ++i) {
+ TKeyframeHandleList theKeyframes;
+ theAnimationCore->GetKeyframes(m_AnimationHandles[i], theKeyframes);
+ // the data model ensures that there is only 1 keyframe created for a given time
+ for (size_t theKeyIndex = 0; theKeyIndex < theKeyframes.size(); ++theKeyIndex) {
+ float theValue =
+ KeyframeTime(theAnimationCore->GetKeyframeData(theKeyframes[theKeyIndex]));
+ if (theValue == theKeyframeTime) {
+ theNewKeyframe->AddKeyframeHandle(theKeyframes[theKeyIndex]);
+ break;
+ }
+ }
+ }
+ }
+ m_Keyframes.push_back(theNewKeyframe);
+ return true;
+}
+
+void CUICDMTimelineItemProperty::OnPropertyLinkStatusChanged(UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty)
+{
+ if (inInstance == m_InstanceHandle && inProperty == m_PropertyHandle) {
+ // Re-bind to keyframes because the ones we should be pointing to will have changed.
+ ReleaseKeyframes();
+ CreateKeyframes();
+ }
+}
+
+void CUICDMTimelineItemProperty::RefreshKeyFrames(void)
+{
+ std::stable_sort(m_Keyframes.begin(), m_Keyframes.end(), SortKeyframeByTime);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.h
new file mode 100644
index 00000000..ddd4b647
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineItemProperty.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_UICDMTIMELINE_ITEM_PROPERTY_H
+#define INCLUDED_UICDMTIMELINE_ITEM_PROPERTY_H 1
+
+#pragma once
+
+#include "ITimelineItemProperty.h"
+#include "UICDMTimelineKeyframe.h"
+#include "UICDMTimeline.h"
+#include "UICDMPropertyDefinition.h"
+
+class CTimelineTranslationManager;
+class CCmdDataModelSetKeyframeValue;
+class CUICDMTimelineItemBinding;
+
+//=============================================================================
+/**
+ * A data model item's property.
+ * Typically only animated properties show up in the Timeline.
+ */
+//=============================================================================
+class CUICDMTimelineItemProperty : public ITimelineItemProperty
+{
+public:
+ CUICDMTimelineItemProperty(CTimelineTranslationManager *inTransMgr,
+ UICDM::CUICDMPropertyHandle inPropertyHandle,
+ UICDM::CUICDMInstanceHandle inInstance);
+ virtual ~CUICDMTimelineItemProperty();
+
+ // ITimelineProperty
+ Q3DStudio::CString GetName() const override;
+ bool IsMaster() const override;
+ UICDM::TDataTypePair GetType() const override;
+ float GetMaximumValue() const override;
+ float GetMinimumValue() const override;
+ void SetSelected() override;
+ void ClearKeySelection() override;
+ void DeleteAllKeys() override;
+ ITimelineKeyframesManager *GetKeyframesManager() const override;
+ IKeyframe *GetKeyframeByTime(long inTime) const override;
+ IKeyframe *GetKeyframeByIndex(long inIndex) const override;
+ long GetKeyframeCount() const override;
+ long GetChannelCount() const override;
+ float GetChannelValueAtTime(long inChannelIndex, long inTime) override;
+ void SetChannelValueAtTime(long inChannelIndex, long inTime, float inValue) override;
+ long OffsetSelectedKeyframes(long inOffset) override;
+ void CommitChangedKeyframes() override;
+ void OnEditKeyframeTime(long inCurrentTime, long inObjectAssociation) override;
+ bool IsDynamicAnimation() override;
+ // IKeyframeSelector
+ void SelectKeyframes(bool inSelected, long inTime = -1) override;
+
+ void Bind(CPropertyRow *inRow) override;
+ void Release() override;
+ CPropertyRow *GetRow() override;
+
+ bool RefreshKeyframe(UICDM::CUICDMKeyframeHandle inKeyframe,
+ ETimelineKeyframeTransaction inTransaction);
+ IKeyframe *GetKeyframeByHandle(UICDM::CUICDMKeyframeHandle inKeyframe);
+ void DoSelectKeyframes(bool inSelected, long inTime, bool inParentTriggered,
+ CUICDMTimelineItemBinding *inParent);
+
+ void RefreshKeyFrames(void);
+
+protected:
+ void InitializeCachedVariables(UICDM::CUICDMInstanceHandle inInstance);
+ bool CreateKeyframeIfNonExistent(UICDM::CUICDMKeyframeHandle inKeyframe,
+ UICDM::CUICDMAnimationHandle inOwningAnimation);
+ void OnPropertyLinkStatusChanged(UICDM::CUICDMSlideHandle inSlide,
+ UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+ void CreateKeyframes();
+ void ReleaseKeyframes();
+
+protected:
+ typedef std::vector<CUICDMTimelineKeyframe *> TKeyframeList;
+
+ CPropertyRow *m_Row;
+ UICDM::CUICDMInstanceHandle m_InstanceHandle;
+ UICDM::CUICDMPropertyHandle m_PropertyHandle;
+ CTimelineTranslationManager *m_TransMgr;
+ std::vector<UICDM::CUICDMAnimationHandle> m_AnimationHandles;
+ TKeyframeList m_Keyframes;
+ CCmdDataModelSetKeyframeValue
+ *m_SetKeyframeValueCommand; // for merging modifying keyframe values via graph
+ UICDM::TDataTypePair m_Type;
+ Q3DStudio::CString m_Name;
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>> m_Signals;
+};
+
+#endif // INCLUDED_UICDMTIMELINE_ITEM_PROPERTY_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.cpp
new file mode 100644
index 00000000..c29062a2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "UICDMTimelineKeyframe.h"
+#include "UICDMAnimation.h"
+#include "CmdDataModelChangeKeyframe.h"
+#include "CmdBatch.h"
+#include "UICDMStudioSystem.h"
+#include "OffsetKeyframesCommandHelper.h"
+
+#include "Doc.h"
+#include "StudioApp.h"
+#include "Core.h"
+
+using namespace UICDM;
+
+// TODO: figure out if we can just use IDoc instead of CDoc
+CUICDMTimelineKeyframe::CUICDMTimelineKeyframe(IDoc *inDoc)
+ : m_Doc(dynamic_cast<CDoc *>(inDoc))
+ , m_Selected(false)
+{
+}
+
+CUICDMTimelineKeyframe::~CUICDMTimelineKeyframe()
+{
+}
+
+bool CUICDMTimelineKeyframe::IsSelected() const
+{
+ return m_Selected;
+}
+
+float my_roundf(float r)
+{
+ return (r > 0.0f) ? floorf(r + 0.5f) : ceilf(r - 0.5f);
+}
+
+long CUICDMTimelineKeyframe::GetTime() const
+{
+ if (!m_KeyframeHandles.empty()) {
+ IAnimationCore *theAnimationCore = m_Doc->GetStudioSystem()->GetAnimationCore();
+ CUICDMKeyframeHandle theKeyframeHandle = *m_KeyframeHandles.begin();
+ if (theAnimationCore->KeyframeValid(theKeyframeHandle)) {
+ float theTimeinSecs =
+ KeyframeTime(theAnimationCore->GetKeyframeData(theKeyframeHandle));
+ // We always convert back and forth between between long and float.
+ // This causes especially issues when we do comparisons
+ return (long)my_roundf(theTimeinSecs * 1000);
+ }
+ }
+ return -1; // keyframe was deleted, and data cannot be retrieved.
+}
+
+float CUICDMTimelineKeyframe::GetTimeInSecs(long inTime)
+{
+ float theTimeinSecs = static_cast<float>(inTime) / 1000.f;
+ // round off to 4 decimal place to workaround precision issues
+ // TODO: fix this, either all talk float OR long. choose one.
+ theTimeinSecs = (float)(((theTimeinSecs + 0.00005) * 10000.0) / 10000.0f);
+ return theTimeinSecs;
+}
+
+void CUICDMTimelineKeyframe::SetTime(const long inNewTime)
+{
+ float theTimeinSecs = GetTimeInSecs(inNewTime);
+ CCmd *theCmd = nullptr;
+ if (m_KeyframeHandles.size() == 1) {
+ theCmd = new CCmdDataModelSetKeyframeTime(m_Doc, m_KeyframeHandles.front(), theTimeinSecs);
+ } else { // more than 1 channel
+ CCmdBatch *theBatch = new CCmdBatch(m_Doc);
+ TKeyframeHandleList::iterator theIter = m_KeyframeHandles.begin();
+ for (; theIter != m_KeyframeHandles.end(); ++theIter)
+ theBatch->AddCommand(new CCmdDataModelSetKeyframeTime(m_Doc, *theIter, theTimeinSecs));
+ theCmd = theBatch;
+ }
+ if (theCmd)
+ m_Doc->GetCore()->ExecuteCommand(theCmd);
+
+#ifdef _DEBUG
+ // we have a precision issue from converting from long to float..
+ IAnimationCore *theAnimationCore = m_Doc->GetStudioSystem()->GetAnimationCore();
+ long theTest = static_cast<long>(
+ KeyframeTime(theAnimationCore->GetKeyframeData(*m_KeyframeHandles.begin())) * 1000);
+ ASSERT(inNewTime == theTest);
+#endif
+}
+
+inline CUICDMAnimationHandle GetAnimationHandle(UICDM::IAnimationCore *inAnimationCore,
+ const TKeyframeHandleList &inKeyframes)
+{
+ if (!inKeyframes.empty())
+ return inAnimationCore->GetAnimationForKeyframe(inKeyframes[0]);
+ return 0;
+}
+
+void CUICDMTimelineKeyframe::SetDynamic(bool inIsDynamic)
+{
+ if (!m_KeyframeHandles.empty()) {
+ CUICDMAnimationHandle theAnimation =
+ GetAnimationHandle(m_Doc->GetStudioSystem()->GetAnimationCore(), m_KeyframeHandles);
+ if (theAnimation.Valid())
+ m_Doc->GetCore()->ExecuteCommand(
+ new CCmdDataModelChangeDynamicKeyframe(m_Doc, theAnimation, inIsDynamic));
+ }
+}
+
+// Only the first key of a track can be dynamic.
+bool CUICDMTimelineKeyframe::IsDynamic() const
+{
+ UICDM::IAnimationCore *theAnimationCore = m_Doc->GetStudioSystem()->GetAnimationCore();
+ CUICDMAnimationHandle theAnimation = GetAnimationHandle(theAnimationCore, m_KeyframeHandles);
+ if (theAnimation.Valid()) {
+ SAnimationInfo theInfo = theAnimationCore->GetAnimationInfo(theAnimation);
+ if (theInfo.m_DynamicFirstKeyframe) {
+ TKeyframeHandleList theKeyframes;
+ theAnimationCore->GetKeyframes(theAnimation, theKeyframes);
+ if (!theKeyframes.empty()) // only true if track is dynamic and this is the first
+ // keyframe. Might have to optimize because this is so
+ // clunky.
+ return (theKeyframes[0] == m_KeyframeHandles[0]);
+ }
+ }
+ return false;
+}
+
+void CUICDMTimelineKeyframe::AddKeyframeHandle(UICDM::CUICDMKeyframeHandle inHandle)
+{
+ m_KeyframeHandles.push_back(inHandle);
+}
+
+bool CUICDMTimelineKeyframe::HasKeyframeHandle(UICDM::CUICDMKeyframeHandle inHandle) const
+{
+ TKeyframeHandleList::const_iterator theIter = m_KeyframeHandles.begin();
+ for (; theIter != m_KeyframeHandles.end(); ++theIter) {
+ if (*theIter == inHandle)
+ return true;
+ }
+ return false;
+}
+
+void CUICDMTimelineKeyframe::SetSelected(bool inSelected)
+{
+ m_Selected = inSelected;
+}
+
+// For colors, there would be 3 keyframe handles
+void CUICDMTimelineKeyframe::UpdateKeyframesTime(COffsetKeyframesCommandHelper *inCommandHelper,
+ long inTime)
+{
+ for (size_t i = 0; i < m_KeyframeHandles.size(); ++i)
+ inCommandHelper->SetCommandTime(m_KeyframeHandles[i], inTime);
+}
+
+void CUICDMTimelineKeyframe::GetKeyframeHandles(TKeyframeHandleList &outList) const
+{
+ outList = m_KeyframeHandles;
+}
+
+void CompareAndSet(CUICDMKeyframeHandle inKeyframe, IAnimationCore *inAnimationCore,
+ float &outRetValue, bool inGreaterThan)
+{
+ TKeyframe theKeyframeData = inAnimationCore->GetKeyframeData(inKeyframe);
+ float theValue = KeyframeValueValue(theKeyframeData);
+ if ((inGreaterThan && theValue > outRetValue) || (!inGreaterThan && theValue < outRetValue))
+ outRetValue = theValue;
+}
+
+float CUICDMTimelineKeyframe::GetMaxValue() const
+{
+ IAnimationCore *theAnimationCore = m_Doc->GetStudioSystem()->GetAnimationCore();
+ float theRetVal = FLT_MIN;
+ do_all(m_KeyframeHandles,
+ std::bind(CompareAndSet, std::placeholders::_1, theAnimationCore,
+ std::ref(theRetVal), true));
+ return theRetVal;
+}
+
+float CUICDMTimelineKeyframe::GetMinValue() const
+{
+ IAnimationCore *theAnimationCore = m_Doc->GetStudioSystem()->GetAnimationCore();
+ float theRetVal = FLT_MAX;
+ do_all(m_KeyframeHandles,
+ std::bind(CompareAndSet, std::placeholders::_1, theAnimationCore,
+ std::ref(theRetVal), false));
+ return theRetVal;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.h
new file mode 100644
index 00000000..bb615202
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineKeyframe.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef INCLUDED_UICDMKEYFRAME_H
+#define INCLUDED_UICDMKEYFRAME_H 1
+
+#pragma once
+
+#include "IKeyframe.h"
+
+// Data model specific
+#include "UICDMHandles.h"
+
+class IDoc;
+class CDoc;
+class CCmdBatch;
+class COffsetKeyframesCommandHelper;
+
+//==============================================================================
+/**
+ * Wrapper for a keyframe in UICDM.
+ */
+//==============================================================================
+class CUICDMTimelineKeyframe : public IKeyframe
+{
+public:
+ typedef std::vector<UICDM::CUICDMKeyframeHandle> TKeyframeHandleList;
+
+protected:
+ TKeyframeHandleList
+ m_KeyframeHandles; ///< no. corresponds to the channels the animated property has.
+ CDoc *m_Doc;
+ bool m_Selected;
+
+public:
+ CUICDMTimelineKeyframe(IDoc *inDoc);
+ virtual ~CUICDMTimelineKeyframe();
+
+ // IKeyframe
+ bool IsSelected() const override;
+ long GetTime() const override;
+ void SetTime(const long inNewTime) override;
+ void SetDynamic(bool inIsDynamic) override;
+ bool IsDynamic() const override;
+
+ void AddKeyframeHandle(UICDM::CUICDMKeyframeHandle inHandle);
+ bool HasKeyframeHandle(UICDM::CUICDMKeyframeHandle inHandle) const;
+ void SetSelected(bool inSelected);
+ void UpdateKeyframesTime(COffsetKeyframesCommandHelper *inCommandHelper, long inTime);
+ void GetKeyframeHandles(TKeyframeHandleList &outList) const;
+
+ // support drawing graphs
+ float GetMaxValue() const;
+ float GetMinValue() const;
+
+ static float GetTimeInSecs(long inTime);
+};
+
+#endif // INCLUDED_UICDMKEYFRAME_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.cpp
new file mode 100644
index 00000000..2c44e438
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICDMTimelineTimebar.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMDataCore.h"
+#include "UICDMDataTypes.h"
+#include "ClientDataModelBridge.h"
+#include "TimelineTranslationManager.h"
+#include "Doc.h"
+#include "Dispatch.h"
+#include "Core.h"
+#include "TimeEditDlg.h"
+#include "IDocumentEditor.h"
+#include "BaseStateRow.h"
+#include "BaseTimebarlessRow.h"
+#include "StudioFullSystem.h"
+#include "StudioPreferences.h"
+#include "ITimelineItemBinding.h"
+
+CUICDMTimelineTimebar::CUICDMTimelineTimebar(
+ CTimelineTranslationManager *inTimelineTranslationManager,
+ UICDM::CUICDMInstanceHandle inDataHandle)
+ : Q3DStudio::CUpdateableDocumentEditor(*inTimelineTranslationManager->GetDoc())
+ , m_TimelineTranslationManager(inTimelineTranslationManager)
+ , m_PropertySystem(inTimelineTranslationManager->GetStudioSystem()->GetPropertySystem())
+ , m_DataHandle(inDataHandle)
+{
+ CClientDataModelBridge *theClientDataModelBridge =
+ inTimelineTranslationManager->GetStudioSystem()->GetClientDataModelBridge();
+ m_StartTime = theClientDataModelBridge->GetSceneAsset().m_StartTime;
+ m_EndTime = theClientDataModelBridge->GetSceneAsset().m_EndTime;
+ UICDM::SValue theValue;
+ if (m_PropertySystem->GetInstancePropertyValue(
+ m_DataHandle, theClientDataModelBridge->GetSceneAsset().m_TimebarColor, theValue)) {
+ UICDM::SFloat3 theTimebarColor = UICDM::get<UICDM::SFloat3>(theValue);
+
+ m_Color.SetRGB(static_cast<int>(theTimebarColor.m_Floats[0] * 255.0f),
+ static_cast<int>(theTimebarColor.m_Floats[1] * 255.0f),
+ static_cast<int>(theTimebarColor.m_Floats[2] * 255.0f));
+ }
+ UICDM::IStudioFullSystemSignalProvider *theProvider =
+ inTimelineTranslationManager->GetStudioSystem()->GetFullSystem()->GetSignalProvider();
+ m_PropertyChangedSignal = theProvider->ConnectInstancePropertyValue(
+ std::bind(&CUICDMTimelineTimebar::OnPropertyChanged, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ OnPropertyChanged(m_DataHandle, theClientDataModelBridge->GetSceneAsset().m_TimebarColor);
+ OnPropertyChanged(m_DataHandle, theClientDataModelBridge->GetSceneAsset().m_TimebarText);
+}
+
+void CUICDMTimelineTimebar::OnPropertyChanged(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty)
+{
+ if (m_DataHandle == inInstance) {
+ bool needsInvalidate = false;
+ UICDM::SValue theValue;
+ CClientDataModelBridge *theClientDataModelBridge =
+ m_TimelineTranslationManager->GetStudioSystem()->GetClientDataModelBridge();
+ if (inProperty == theClientDataModelBridge->GetSceneAsset().m_TimebarColor) {
+
+ if (m_PropertySystem->GetInstancePropertyValue(
+ m_DataHandle, theClientDataModelBridge->GetSceneAsset().m_TimebarColor,
+ theValue)) {
+ UICDM::SFloat3 theTimebarColor = UICDM::get<UICDM::SFloat3>(theValue);
+
+ m_Color.SetRGB(static_cast<int>(theTimebarColor.m_Floats[0] * 255.0f),
+ static_cast<int>(theTimebarColor.m_Floats[1] * 255.0f),
+ static_cast<int>(theTimebarColor.m_Floats[2] * 255.0f));
+ } else {
+ switch (theClientDataModelBridge->GetObjectType(inInstance)) {
+ case OBJTYPE_LAYER:
+ m_Color = CStudioPreferences::GetLayerTimebarColor();
+ break;
+ case OBJTYPE_BEHAVIOR:
+ m_Color = CStudioPreferences::GetBehaviorTimebarColor();
+ break;
+ case OBJTYPE_CAMERA:
+ m_Color = CStudioPreferences::GetCameraTimebarColor();
+ break;
+ case OBJTYPE_LIGHT:
+ m_Color = CStudioPreferences::GetLightTimebarColor();
+ break;
+ case OBJTYPE_MODEL:
+ m_Color = CStudioPreferences::GetModelTimebarColor();
+ break;
+ case OBJTYPE_GROUP:
+ m_Color = CStudioPreferences::GetGroupTimebarColor();
+ break;
+ case OBJTYPE_COMPONENT:
+ m_Color = CStudioPreferences::GetComponentTimebarColor();
+ break;
+ case OBJTYPE_EFFECT:
+ m_Color = CStudioPreferences::GetEffectTimebarColor();
+ break;
+ default:
+ m_Color = CStudioPreferences::GetObjectTimebarColor();
+ break;
+ }
+ }
+ needsInvalidate = true;
+ } else if (inProperty == theClientDataModelBridge->GetSceneAsset().m_TimebarText) {
+ if (m_PropertySystem->GetInstancePropertyValue(
+ m_DataHandle, theClientDataModelBridge->GetSceneAsset().m_TimebarText,
+ theValue)) {
+ UICDM::SStringRef theTimebarComment = UICDM::get<UICDM::SStringRef>(theValue);
+ m_Comment.Assign(static_cast<const wchar_t *>(theTimebarComment));
+ } else {
+ m_Comment.Assign(L"");
+ }
+ needsInvalidate = true;
+ }
+ if (needsInvalidate) {
+ ITimelineItemBinding *theBinding =
+ m_TimelineTranslationManager->GetOrCreate(inInstance);
+ if (theBinding) {
+ CBaseStateRow *theRow = theBinding->GetRow();
+ if (theRow) {
+ CBaseTimebarlessRow *theTimebar = theRow->GetTimebar();
+ theTimebar->RefreshRowMetaData();
+ }
+ }
+ }
+ }
+}
+
+CUICDMTimelineTimebar::~CUICDMTimelineTimebar()
+{
+}
+
+// TODO: Can we put this on IInstancePropertyCore?
+template <typename T>
+T GetInstancePropertyValue(UICDM::IPropertySystem *inPropertySystem,
+ UICDM::CUICDMInstanceHandle inInstanceHandle,
+ UICDM::CUICDMPropertyHandle inProperty)
+{
+ UICDM::SValue theValue;
+ inPropertySystem->GetInstancePropertyValue(inInstanceHandle, inProperty, theValue);
+ return UICDM::get<T>(theValue);
+}
+
+long CUICDMTimelineTimebar::GetStartTime() const
+{
+ return GetInstancePropertyValue<qt3ds::QT3DSI32>(m_PropertySystem, m_DataHandle, m_StartTime);
+}
+
+long CUICDMTimelineTimebar::GetEndTime() const
+{
+ return GetInstancePropertyValue<qt3ds::QT3DSI32>(m_PropertySystem, m_DataHandle, m_EndTime);
+}
+
+long CUICDMTimelineTimebar::GetDuration() const
+{
+ auto theStartTime = GetInstancePropertyValue<qt3ds::QT3DSI32>(m_PropertySystem, m_DataHandle, m_StartTime);
+ auto theEndTime = GetInstancePropertyValue<qt3ds::QT3DSI32>(m_PropertySystem, m_DataHandle, m_EndTime);
+
+ return theEndTime - theStartTime;
+}
+
+bool CUICDMTimelineTimebar::ShowHandleBars() const
+{
+ return true;
+}
+
+void CUICDMTimelineTimebar::OnBeginDrag()
+{ // Really? TODO: Figure out why this is here.
+ // ASSERT(0);
+}
+
+void CUICDMTimelineTimebar::OffsetTime(long inDiff)
+{
+ if (m_DataHandle.Valid()) {
+ ENSURE_EDITOR(L"Time Bar Move").OffsetTimeRange(m_DataHandle, inDiff);
+ m_TimelineTranslationManager->GetDoc()
+ ->GetCore()
+ ->GetDispatch()
+ ->FireImmediateRefreshInstance(m_DataHandle);
+ }
+}
+
+void CUICDMTimelineTimebar::ChangeTime(long inTime, bool inSetStart)
+{
+ if (m_DataHandle.Valid()) {
+ ENSURE_EDITOR(L"Time Bar Resize").ResizeTimeRange(m_DataHandle, inTime, inSetStart);
+ m_TimelineTranslationManager->GetDoc()
+ ->GetCore()
+ ->GetDispatch()
+ ->FireImmediateRefreshInstance(m_DataHandle);
+ }
+}
+
+void CUICDMTimelineTimebar::CommitTimeChange()
+{
+ CommitEditor();
+}
+
+void CUICDMTimelineTimebar::RollbackTimeChange()
+{
+ RollbackEditor();
+}
+
+void CUICDMTimelineTimebar::SetTimebarColor(const ::CColor &inColor)
+{
+ using namespace Q3DStudio;
+ if (inColor != m_Color) {
+ UICDM::CUICDMInstanceHandle theHandle = m_DataHandle;
+ SCOPED_DOCUMENT_EDITOR(*m_TimelineTranslationManager->GetDoc(), QObject::tr("Set Timebar Color"))
+ ->SetTimebarColor(theHandle, inColor);
+ }
+}
+
+void CUICDMTimelineTimebar::SetTimebarComment(const Q3DStudio::CString &inComment)
+{
+ using namespace Q3DStudio;
+ if (inComment != m_Comment) {
+ UICDM::CUICDMInstanceHandle theHandle = m_DataHandle;
+ SCOPED_DOCUMENT_EDITOR(*m_TimelineTranslationManager->GetDoc(), QObject::tr("Set Timebar Text"))
+ ->SetTimebarText(theHandle, inComment);
+ }
+}
+
+void CUICDMTimelineTimebar::SetTimebarTime(ITimeChangeCallback *inCallback /*= nullptr*/)
+{
+ long theStartTime = GetStartTime();
+ long theEndTime = GetEndTime();
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.ShowDialog(theStartTime, theEndTime, m_TimelineTranslationManager->GetDoc(),
+ TIMEBAR, inCallback);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.h
new file mode 100644
index 00000000..d1441039
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/UICDMTimelineTimebar.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+///////////////////////////////////////////////////////////////////////////////
+// Includes
+#include "ITimelineTimebar.h"
+#include "UICDMHandles.h"
+#include "IDocumentEditor.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Forwards
+class CTimelineTranslationManager;
+
+namespace Q3DStudio {
+class IDocumentEditor;
+}
+
+namespace UICDM {
+class IPropertySystem;
+class ISignalConnection;
+}
+
+//=============================================================================
+/**
+ * General timebar implementation for UICDM objects
+ */
+class CUICDMTimelineTimebar : public ITimelineTimebar, public Q3DStudio::CUpdateableDocumentEditor
+{
+public:
+ CUICDMTimelineTimebar(CTimelineTranslationManager *inTimelineTranslationManager,
+ UICDM::CUICDMInstanceHandle inDataHandle);
+ virtual ~CUICDMTimelineTimebar();
+
+protected:
+ CTimelineTranslationManager *m_TimelineTranslationManager;
+ UICDM::IPropertySystem *m_PropertySystem;
+ UICDM::CUICDMInstanceHandle m_DataHandle; // The Instance Handle for this Timeline Timeber.
+ UICDM::CUICDMPropertyHandle m_StartTime;
+ UICDM::CUICDMPropertyHandle m_EndTime;
+ ::CColor m_Color; // Timebar color
+ Q3DStudio::CString m_Comment; // Timebar comment text
+ std::shared_ptr<UICDM::ISignalConnection> m_PropertyChangedSignal;
+ void OnPropertyChanged(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMPropertyHandle inProperty);
+
+public:
+ // ITimelineTimebar
+ long GetStartTime() const override;
+ long GetEndTime() const override;
+ long GetDuration() const override;
+ bool ShowHandleBars() const override;
+ void OnBeginDrag() override;
+ void OffsetTime(long inDiff) override;
+ void ChangeTime(long inTime, bool inSetStart) override;
+ void CommitTimeChange() override;
+ void RollbackTimeChange() override;
+ ::CColor GetTimebarColor() override { return m_Color; }
+ void SetTimebarColor(const ::CColor &inColor) override;
+ Q3DStudio::CString GetTimebarComment() override { return m_Comment; }
+ void SetTimebarComment(const Q3DStudio::CString &inComment) override;
+ void SetTimebarTime(ITimeChangeCallback *inCallback = nullptr) override;
+};
diff --git a/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.cpp b/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.cpp
new file mode 100644
index 00000000..fb25559a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "BlankToggleControl.h"
+#include "Renderer.h"
+#include "BaseStateRow.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+
+CBlankToggleControl::CBlankToggleControl(CBaseStateRow *inBaseStateRow)
+ : m_StateRow(inBaseStateRow)
+ , m_Selected(false)
+{
+ m_BackgroundColor = m_StateRow->GetTimebarBackgroundColor(inBaseStateRow->GetObjectType());
+}
+
+CBlankToggleControl::~CBlankToggleControl()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the drawing fo rthe toggle control
+ */
+void CBlankToggleControl::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ // Fill in the background
+ if (!m_Selected)
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+ else
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetTimelineSelectColor());
+
+ // Draw the line at the bottom of this control
+ inRenderer->PushPen(CStudioPreferences::GetTreeFloorColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the line on the left side of this control
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the highlight
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(1, 0));
+ inRenderer->LineTo(CPt(1, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the line on the right side of this control
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(CPt(theRect.size.x - 1, 0));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been selected.
+ */
+void CBlankToggleControl::OnSelect()
+{
+ m_Selected = true;
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been deselected.
+ */
+void CBlankToggleControl::OnDeselect()
+{
+ m_Selected = false;
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Handler for the OnMouseDown event
+ *
+ * @param inPoint the point where this event takes place
+ * @param inFlags the state when this event takes place.
+ */
+bool CBlankToggleControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_StateRow->Select(SBaseStateRowSelectionKeyState());
+ }
+ return true;
+}
+
+//==============================================================================
+/**
+ * Sets the background color of this toggle control
+ */
+void CBlankToggleControl::SetBackgroundColor(::CColor inBackgroundColor)
+{
+ if (m_BackgroundColor == inBackgroundColor)
+ return;
+
+ m_BackgroundColor = inBackgroundColor;
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Handler for the OnMouseOver event
+ *
+ * @param inPoint the point where this event takes place
+ * @param inFlags the state when this event takes place.
+ */
+void CBlankToggleControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ m_StateRow->OnMouseOver();
+}
+
+//==============================================================================
+/**
+ * Handler for the OnMouseOut event
+ *
+ * @param inPoint the point where this event takes place
+ * @param inFlags the state when this event takes place.
+ */
+void CBlankToggleControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ m_StateRow->OnMouseOut();
+}
+
+void CBlankToggleControl::Refresh()
+{
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.h b/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.h
new file mode 100644
index 00000000..2fe4ce28
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BlankToggleControl.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BLANK_TOGGLE_CONTROL_H
+#define INCLUDED_BLANK_TOGGLE_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+
+class CBaseStateRow;
+
+class CBlankToggleControl : public CControl
+{
+public:
+ CBlankToggleControl(CBaseStateRow *inStateRow);
+ virtual ~CBlankToggleControl();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ void OnSelect();
+ void OnDeselect();
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void SetBackgroundColor(::CColor inBackgroundColor);
+
+ virtual void Refresh();
+
+protected:
+ CBaseStateRow *m_StateRow;
+ bool m_Selected;
+
+ ::CColor m_BackgroundColor;
+};
+#endif // INCLUDED_BLANK_TOGGLE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.cpp b/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.cpp
new file mode 100644
index 00000000..2564d087
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ColorBlankControl.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CColorBlankControl::CColorBlankControl(CColor inColor)
+ : CBlankControl(inColor)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CColorBlankControl::~CColorBlankControl()
+{
+}
+
+//=============================================================================
+/**
+ * Handles custom drawing of the blank control underneath the tree control
+ * on the timeline palette.
+ */
+void CColorBlankControl::Draw(CRenderer *inRenderer)
+{
+ CBlankControl::Draw(inRenderer);
+
+ // Draw the line on the right side of this control
+ CPt theSize = GetSize();
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(theSize.x - 1, 0);
+ inRenderer->LineTo(theSize.x - 1, theSize.y - 1);
+ inRenderer->PopPen();
+
+ // Draw the line on the left side of this control
+ inRenderer->PushPen(CStudioPreferences::GetRowTopColor());
+ inRenderer->MoveTo(0, 0);
+ inRenderer->LineTo(0, theSize.y - 1);
+ inRenderer->PopPen();
+
+ // Draw the highlight on the left side of this control
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(1, 0));
+ inRenderer->LineTo(CPt(1, theSize.y - 1));
+ inRenderer->PopPen();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.h b/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.h
new file mode 100644
index 00000000..bb85b2aa
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ColorBlankControl.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_COLOR_BLANK_CONTROL_H
+#define INCLUDED_COLOR_BLANK_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//=============================================================================
+/**
+ * Extends the blank control to draw items specific to the tree view side of the
+ * timeline palette.
+ */
+class CColorBlankControl : public CBlankControl
+{
+public:
+ CColorBlankControl(CColor inColor = CStudioPreferences::GetBaseColor());
+ virtual ~CColorBlankControl();
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+};
+
+#endif // INCLUDED_COLOR_BLANK_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ColorControl.cpp b/src/Authoring/Studio/Palettes/Timeline/ColorControl.cpp
new file mode 100644
index 00000000..71d1c392
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ColorControl.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "CColor.h"
+#include "ColorControl.h"
+#include "CoreUtils.h"
+#include "BaseStateRow.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+#include "Dispatch.h"
+#include "ResourceCache.h"
+#include "Bindings/ITimelineItem.h"
+
+//==============================================================================
+/**
+ * The control (of each row) to the left of the tree view, where it indicates if there are
+ *actions associated with this row.
+ */
+CColorControl::CColorControl(CBaseStateRow *inRow)
+ : m_Shaded(true)
+ , m_Selected(false)
+ , m_HasAction(false)
+ , m_HasMasterAction(false)
+ , m_ChildHasAction(false)
+ , m_ChildHasMasterAction(false)
+ , m_ComponentHasAction(false)
+ , m_ComponentHasMasterAction(false)
+{
+ m_ParentRow = inRow;
+ m_BackgroundColor = m_ParentRow->GetTimebarBackgroundColor(m_ParentRow->GetObjectType());
+
+ UpdateIconStatus();
+}
+
+CColorControl::~CColorControl()
+{
+}
+
+void CColorControl::SetShaded(bool inIsShaded)
+{
+ m_Shaded = inIsShaded;
+}
+
+//==============================================================================
+/**
+ * Draw
+ *
+ * draws this object
+ *
+ * @param inRenderer a renderer object
+ */
+void CColorControl::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+ //
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ // bottom
+ inRenderer->MoveTo(CPt(1, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+ // left
+ inRenderer->MoveTo(CPt(0, 1));
+ inRenderer->LineTo(CPt(0, theRect.size.y - 1));
+ // right
+ inRenderer->MoveTo(CPt(theRect.size.x - 1, 1));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+ //
+ inRenderer->PopPen();
+
+ CPt thePos(0, 0);
+ if (m_HasMasterAction) {
+ if (m_ActionImages[IMAGETYPE_MASTERACTION].isNull())
+ m_ActionImages[IMAGETYPE_MASTERACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-MasterAction.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_MASTERACTION]);
+ } else if (m_HasAction) {
+ if (m_ActionImages[IMAGETYPE_ACTION].isNull())
+ m_ActionImages[IMAGETYPE_ACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-Action.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_ACTION]);
+ }
+ if (m_ChildHasMasterAction) {
+ if (m_ActionImages[IMAGETYPE_CHILDMASTERACTION].isNull())
+ m_ActionImages[IMAGETYPE_CHILDMASTERACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-ChildMasterAction.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_CHILDMASTERACTION]);
+ } else if (m_ChildHasAction) {
+ if (m_ActionImages[IMAGETYPE_CHILDACTION].isNull())
+ m_ActionImages[IMAGETYPE_CHILDACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-ChildAction.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_CHILDACTION]);
+ }
+ if (m_ComponentHasMasterAction) {
+ if (m_ActionImages[IMAGETYPE_COMPONENTMASTERACTION].isNull())
+ m_ActionImages[IMAGETYPE_COMPONENTMASTERACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-ComponentMasterAction.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_COMPONENTMASTERACTION]);
+ } else if (m_ComponentHasAction) {
+ if (m_ActionImages[IMAGETYPE_COMPONENTACTION].isNull())
+ m_ActionImages[IMAGETYPE_COMPONENTACTION] =
+ CResourceCache::GetInstance()->GetBitmap("Action-ComponentAction.png");
+
+ inRenderer->DrawBitmap(thePos, m_ActionImages[IMAGETYPE_COMPONENTACTION]);
+ }
+
+ // the old code with selection
+
+ // inRenderer->DrawGradientBitmap( theRect, m_ParentRow->GetAsset( )->GetTimebarColor( ), false
+ // );
+ // if ( m_Selected )
+ //{
+ // long theXSize = theRect.size.x / 2;
+ // long theYSize = theRect.size.y / 2;
+ // inRenderer->FillSolidRect( CRct( CPt( theXSize / 2, theYSize / 2 ), CPt( theXSize, theYSize)
+ //), CalculateSelectedColor( m_ParentRow->GetAsset( )->GetTimebarColor( ) ) );
+ //}
+
+ // if this is selected then do something special
+ // if ( m_Shaded )
+ //{
+ // // This is used for the left line of the color control
+ // CColor theNonGradiantHighlight = m_ParentRow->GetAsset( )->GetTimebarColor( );
+ // float theLuminance = theNonGradiantHighlight.GetLuminance( );
+ // theLuminance = theLuminance * (float)1.15;
+ // if ( theLuminance > 1.0 )
+ // {
+ // theLuminance = (float)1.0;
+ // }
+ //
+ // // Highlight on left edge
+ // theNonGradiantHighlight.SetLuminance( theLuminance );
+ // inRenderer->PushPen( theNonGradiantHighlight );
+ // inRenderer->MoveTo( CPt( 1, 0 ) );
+ // inRenderer->LineTo( CPt( 1, theRect.size.y - 1 ) );
+ // inRenderer->PopPen( );
+
+ // // Dark line on far left edge
+ // inRenderer->PushPen( CStudioPreferences::GetRowTopColor( ) );
+ // inRenderer->MoveTo( CPt( 0, 0 ) );
+ // inRenderer->LineTo( CPt( 0, theRect.size.y - 1 ) );
+ // inRenderer->PopPen( );
+ //
+ // inRenderer->PushPen( CStudioPreferences::GetRowTopColor( ) );
+ // inRenderer->MoveTo( CPt( 0,theRect.size.y - 1 ) );
+ // inRenderer->LineTo( CPt( theRect.size.x, theRect.size.y - 1 ) );
+ // inRenderer->MoveTo( CPt( theRect.size.x - 1, 0 ) );
+ // inRenderer->LineTo( CPt( theRect.size.x - 1, theRect.size.y - 1 ) );
+ // inRenderer->PopPen( );
+ //}
+
+ // inRenderer->Draw3dRect( CRct( theRect.position, CPt( theRect.size.x, theRect.size.y + 1 ) ),
+ // CColor( 0, 255, 0 ), CColor( 0, 255, 0 ) );
+}
+
+//==============================================================================
+/**
+ * CalculateNonGradiantHighlight
+ *
+ * calculates the highlight for this color
+ *
+ * @param inColor color to multiply
+ */
+CColor CColorControl::CalculateNonGradiantHighlight(CColor inColor)
+{
+ double theNonGradiantHighlightR = inColor.GetRed() * 1.15;
+ double theNonGradiantHighlightG = inColor.GetGreen() * 1.15;
+ double theNonGradiantHighlightB = inColor.GetBlue() * 1.15;
+ if (theNonGradiantHighlightR > 255) {
+ theNonGradiantHighlightR = 255;
+ }
+ if (theNonGradiantHighlightG > 255) {
+ theNonGradiantHighlightG = 255;
+ }
+ if (theNonGradiantHighlightB > 255) {
+ theNonGradiantHighlightB = 255;
+ }
+ return CColor(::dtol(theNonGradiantHighlightR), ::dtol(theNonGradiantHighlightG),
+ ::dtol(theNonGradiantHighlightB));
+}
+
+//==============================================================================
+/**
+ * CalculateNonGradiantHighlight
+ *
+ * calculates the highlight for this color
+ *
+ * @param inColor color to multiply
+ */
+CColor CColorControl::CalculateSelectedColor(CColor inColor)
+{
+ double theNonGradiantHighlightR = inColor.GetRed() * 0.65;
+ double theNonGradiantHighlightG = inColor.GetGreen() * 0.65;
+ double theNonGradiantHighlightB = inColor.GetBlue() * 0.65;
+ if (theNonGradiantHighlightR > 255) {
+ theNonGradiantHighlightR = 255;
+ }
+ if (theNonGradiantHighlightG > 255) {
+ theNonGradiantHighlightG = 255;
+ }
+ if (theNonGradiantHighlightB > 255) {
+ theNonGradiantHighlightB = 255;
+ }
+ return CColor(::dtol(theNonGradiantHighlightR), ::dtol(theNonGradiantHighlightG),
+ ::dtol(theNonGradiantHighlightB));
+}
+
+//==============================================================================
+/**
+ * Handles the OnMouseDownEvent
+ */
+bool CColorControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+ return true;
+}
+
+//==============================================================================
+/**
+ * Called when this row becomes selected
+ */
+void CColorControl::OnSelect()
+{
+ m_Selected = true;
+ this->Invalidate();
+}
+
+//==============================================================================
+/**
+ * Called when this row becomes deselected
+ */
+void CColorControl::OnDeselect()
+{
+ m_Selected = false;
+ this->Invalidate();
+}
+
+//==============================================================================
+/**
+ * Updates the icon used for display based on the following logic:
+ * 1. Self has actions
+ * 2. Descendents have actions
+ * Results in 4 states.
+ */
+void CColorControl::UpdateIconStatus()
+{
+ ITimelineItem *theTimelineItem = m_ParentRow->GetTimelineItem();
+
+ if (!theTimelineItem)
+ return;
+
+ // Master 'supersede' non-master
+ m_HasMasterAction = theTimelineItem->HasAction(true);
+ if (!m_HasMasterAction)
+ m_HasAction = theTimelineItem->HasAction(false);
+
+ // no descendent info if current row is expanded
+ if (!m_ParentRow->IsExpanded()) {
+ m_ChildHasMasterAction = theTimelineItem->ChildrenHasAction(true);
+ if (!m_ChildHasMasterAction)
+ m_ChildHasAction = theTimelineItem->ChildrenHasAction(false);
+ } else {
+ m_ChildHasMasterAction = false;
+ m_ChildHasAction = false;
+ }
+
+ m_ComponentHasMasterAction = theTimelineItem->ComponentHasAction(true);
+ if (!m_ComponentHasMasterAction)
+ m_ComponentHasAction = theTimelineItem->ComponentHasAction(false);
+
+ this->Invalidate();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ColorControl.h b/src/Authoring/Studio/Palettes/Timeline/ColorControl.h
new file mode 100644
index 00000000..ec5b156d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ColorControl.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_COLOR_CONTROL_H
+#define INCLUDED_COLOR_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+#include "CmdStack.h"
+
+#include <QPixmap>
+
+class CBaseStateRow;
+
+//==============================================================================
+/**
+ * The control at the left side of the TimeContextRow.
+ * It is currently being used to indicate if the Asset or its descendents have any Action.
+ */
+class CColorControl : public CControl
+{
+protected:
+ enum EImageType {
+ IMAGETYPE_ACTION = 0, ///< Types of action image
+ IMAGETYPE_MASTERACTION,
+ IMAGETYPE_CHILDACTION,
+ IMAGETYPE_CHILDMASTERACTION,
+ IMAGETYPE_COMPONENTACTION,
+ IMAGETYPE_COMPONENTMASTERACTION,
+ IMAGETYPE_MAXCOUNT
+ };
+
+public:
+ CColorControl(CBaseStateRow *inRow);
+ virtual ~CColorControl();
+
+ void SetColor(::CColor inColor);
+
+ void SetShaded(bool inIsShaded);
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void Draw(CRenderer *inRenderer) override;
+ ::CColor CalculateNonGradiantHighlight(::CColor inColor);
+
+ void OnSelect();
+ void OnDeselect();
+
+ static ::CColor CalculateSelectedColor(::CColor inCOLOR);
+
+ void UpdateIconStatus();
+
+protected:
+ ::CColor m_BackgroundColor;
+ bool m_Shaded;
+ bool m_Selected;
+ CBaseStateRow *m_ParentRow;
+
+ ///////////////////////////////////////////////////
+ // Action icons
+ // Action status
+ bool m_HasAction;
+ bool m_HasMasterAction;
+ bool m_ChildHasAction;
+ bool m_ChildHasMasterAction;
+ bool m_ComponentHasAction;
+ bool m_ComponentHasMasterAction;
+ // Action images
+ QPixmap m_ActionImages[IMAGETYPE_MAXCOUNT];
+};
+#endif // INCLUDED_COLOR_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/CommentEdit.cpp b/src/Authoring/Studio/Palettes/Timeline/CommentEdit.cpp
new file mode 100644
index 00000000..d0d5325f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/CommentEdit.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "CommentEdit.h"
+#include "Renderer.h"
+#include "ColorControl.h"
+#include "Bindings/ITimelineTimebar.h"
+#include "CoreUtils.h"
+//=============================================================================
+/**
+ * Constructor
+ */
+CCommentEdit::CCommentEdit(ITimelineTimebar *inTimelineItemTimebar)
+ : m_TimelineItemTimebar(inTimelineItemTimebar)
+ , m_IsSelected(false)
+{
+ AddCommitListener(this);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CCommentEdit::~CCommentEdit()
+{
+ RemoveCommitListener(this);
+}
+
+//=============================================================================
+/**
+ * Called when a property changes.
+ * @param inProperty the property that was changed
+ * @param inIsVisibleChange true if this is a change that effects the property's visibility in the
+ * Timeline
+ */
+void CCommentEdit::OnSetData(CControl *inControl)
+{
+ if (inControl == this && m_TimelineItemTimebar) {
+ m_TimelineItemTimebar->SetTimebarComment(GetString());
+ }
+}
+
+//=============================================================================
+/**
+ * Called when this control receives a double click, edit the comment.
+ *
+ * @param inPoint the point where the double click occured
+ * @param inFlags the flags at the time of the click
+ */
+bool CCommentEdit::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ if (GetDisplayString().Length() > 0) {
+ DoChangeComment();
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Edit the Comment
+ *
+ */
+void CCommentEdit::DoChangeComment()
+{
+ SetEditMode(true);
+ SelectAllText();
+}
+
+//=============================================================================
+/**
+ * Called when this control receives a mouse down, don't do anything unless we're in edit mode
+ *
+ * @param inPoint the point where the click occured
+ * @param inFlags the flags at the time of the click
+ */
+bool CCommentEdit::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+ if (GetEditMode()) {
+ theRetVal = CTextEditInPlace::OnMouseDown(inPoint, inFlags);
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Called when this control loses focus
+ */
+void CCommentEdit::OnLoseFocus()
+{
+ CStringEdit::OnLoseFocus();
+ SetEditMode(false);
+}
+
+//=============================================================================
+/**
+ * Called when this control gains focus, don't do anytihng
+ */
+void CCommentEdit::OnGainFocus()
+{
+}
+
+//=============================================================================
+/**
+ * Override for the draw, don't set the size of this control here since we are in
+ * a comment and wouldn't want the size changing
+ */
+void CCommentEdit::Draw(CRenderer *inRenderer)
+{
+ CPt theRectPoint = CPt(::dtol(CalculateCharWidths(inRenderer) + GetRightBuffer()), GetSize().y);
+ CPt theSize = GetSize();
+ if (theSize.x < theRectPoint.x)
+ theRectPoint.x = theSize.x;
+ CRct theRect = CRct(theRectPoint);
+ ::CColor theOutlineColor = ::CColor(0, 0, 0);
+ bool theFillFlag = m_FillBackground;
+
+ if (theFillFlag && !GetEditMode())
+ SetFillBackground(false);
+
+ if (GetEditMode())
+ SetTextColor(::CColor(0, 0, 0));
+
+ inRenderer->PushClippingRect(theRect);
+ CStringEdit::Draw(inRenderer);
+ inRenderer->PopClippingRect();
+
+ if (!GetEditMode())
+ SetFillBackground(theFillFlag);
+
+ if (GetEditMode())
+ inRenderer->DrawRectOutline(theRect, theOutlineColor, theOutlineColor, theOutlineColor,
+ theOutlineColor);
+}
+
+//=============================================================================
+/**
+ * Called when the timebar comment or the time bar color changes on an asset
+ */
+void CCommentEdit::RefreshMetaData()
+{
+ m_Color = ::CColor(m_TimelineItemTimebar->GetTimebarColor());
+ CalculateTextColor();
+ SetData(m_TimelineItemTimebar->GetTimebarComment());
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Calculates the text color based on whether the object is selected or not
+ */
+void CCommentEdit::CalculateTextColor()
+{
+ ::CColor theColor = m_Color;
+ float theLuminance = theColor.GetLuminance();
+ if (m_IsSelected) {
+ theLuminance = theLuminance * 0.8f;
+ }
+ // Duplicated Code to check luminance when the timebar changes color
+
+ if (theLuminance < 0.5) {
+ SetTextColor(::CColor(255, 255, 255));
+ } else {
+ SetTextColor(::CColor(0, 0, 0));
+ }
+}
+
+//=============================================================================
+/**
+ * Sets this object as selected
+ * @param inPoint the point to check
+ */
+void CCommentEdit::SetSelected(bool inState)
+{
+ m_IsSelected = inState;
+ CalculateTextColor();
+}
+
+//=============================================================================
+/**
+ * Overrides the hit test for the case when the click occurs in the object, but outside the text
+ * @param inPoint the point to check
+ */
+bool CCommentEdit::HitTest(const CPt &inPoint) const
+{
+ // rp this seems wrong but apparently it magically works
+ bool theRetVal = false;
+ if (inPoint.x
+ < m_TotalCharWidth + GetRightBuffer() + 2 * CStudioPreferences::GetTimebarTipSize())
+ theRetVal = CTextEditInPlace::HitTest(inPoint);
+ return theRetVal;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/CommentEdit.h b/src/Authoring/Studio/Palettes/Timeline/CommentEdit.h
new file mode 100644
index 00000000..8ae37ad9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/CommentEdit.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_COMMENT_EDIT
+#define INCLUDED_COMMENT_EDIT 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TextEditInPlace.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class ITimelineTimebar;
+
+//=============================================================================
+/**
+ * @class CCommentEdit this class handles changing the comments for a timebar in
+ * the timeline. This class seems necessary only to change the color of the text
+ * depending on the timebar color
+ */
+class CCommentEdit : public CTextEditInPlace, public CCommitDataListener
+{
+public:
+ CCommentEdit(ITimelineTimebar *inTimelineItemTimebar);
+ virtual ~CCommentEdit();
+ void OnSetData(CControl *inControl) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void Draw(CRenderer *inRenderer) override;
+ void RefreshMetaData();
+ void CalculateTextColor();
+ bool HitTest(const CPt &inPoint) const override;
+ void SetSelected(bool inState);
+
+ void OnLoseFocus() override;
+ void OnGainFocus() override;
+
+ void DoChangeComment();
+
+protected:
+ ITimelineTimebar *m_TimelineItemTimebar;
+ ::CColor m_Color;
+ bool m_IsSelected;
+};
+
+#endif // INCLUDED_COMMENT_EDIT
diff --git a/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.cpp b/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.cpp
new file mode 100644
index 00000000..8909ab7e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ComponentContextMenu.h"
+#include "TimelineControl.h"
+#include "StudioUtils.h"
+#include "StudioClipboard.h"
+#include "Dialogs.h"
+#include "BaseTimelineTreeControl.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "RelativePathTools.h"
+
+CComponentContextMenu::CComponentContextMenu(CBaseTimelineTreeControl *inTreeControl,
+ ITimelineItemBinding *inTimelineItemBinding,
+ QWidget *parent)
+ : QMenu(parent)
+ , m_TreeControl(inTreeControl)
+ , m_TimelineItemBinding(inTimelineItemBinding)
+{
+ Initialize();
+}
+
+void CComponentContextMenu::Initialize()
+{
+ m_renameAction = new QAction(tr("Rename Object"), this);
+ connect(m_renameAction, &QAction::triggered, this, &CComponentContextMenu::RenameObject);
+ addAction(m_renameAction);
+
+ m_duplicateAction = new QAction(tr("Duplicate Object"), this);
+ connect(m_duplicateAction, &QAction::triggered, this, &CComponentContextMenu::DuplicateObject);
+ addAction(m_duplicateAction);
+
+ m_deleteAction = new QAction(tr("Delete Object"), this);
+ connect(m_deleteAction, &QAction::triggered, this, &CComponentContextMenu::DeleteObject);
+ addAction(m_deleteAction);
+
+ addSeparator();
+
+ m_copyAction = new QAction(tr("Copy"), this);
+ connect(m_copyAction, &QAction::triggered, this, &CComponentContextMenu::CopyObject);
+ addAction(m_copyAction);
+
+ m_pasteAction = new QAction(tr("Paste"), this);
+ connect(m_pasteAction, &QAction::triggered, this, &CComponentContextMenu::PasteObject);
+ addAction(m_pasteAction);
+
+ m_cutAction = new QAction(tr("Cut"), this);
+ connect(m_cutAction, &QAction::triggered, this, &CComponentContextMenu::CutObject);
+ addAction(m_cutAction);
+ addSeparator();
+
+ m_makeAction = new QAction(tr("Make Component"), this);
+ connect(m_makeAction, &QAction::triggered, this, &CComponentContextMenu::MakeComponent);
+ addAction(m_makeAction);
+
+ if (CanInspectComponent()) {
+ m_inspectAction = new QAction(tr("Edit Component"), this);
+ connect(m_inspectAction, &QAction::triggered, this, &CComponentContextMenu::InspectComponent);
+ addAction(m_inspectAction);
+ }
+
+ if (m_TimelineItemBinding->IsExternalizeable()) {
+ addSeparator();
+ m_externalizeAction = new QAction(tr("Externalize Buffer"), this);
+ connect(m_externalizeAction, &QAction::triggered, this, &CComponentContextMenu::Externalize);
+ addAction(m_externalizeAction);
+ } else if (m_TimelineItemBinding->IsInternalizeable()) {
+ addSeparator();
+ m_internalizeAction = new QAction(tr("Internalize Buffer"), this);
+ connect(m_internalizeAction, &QAction::triggered, this, &CComponentContextMenu::Internalize);
+ addAction(m_internalizeAction);
+ }
+
+ addSeparator();
+
+ m_copyPathAction = new QAction(tr("Copy Object Path"), this);
+ connect(m_copyPathAction, &QAction::triggered, this, &CComponentContextMenu::CopyObjectPath);
+ addAction(m_copyPathAction);
+}
+
+void CComponentContextMenu::showEvent(QShowEvent *event)
+{
+ m_renameAction->setEnabled(CanRenameObject());
+ m_duplicateAction->setEnabled(CanDuplicateObject());
+ m_deleteAction->setEnabled(CanDeleteObject());
+
+ m_cutAction->setEnabled(CanCutObject());
+ m_copyAction->setEnabled(CanCopyObject());
+ m_pasteAction->setEnabled(CanPasteObject());
+
+ m_makeAction->setEnabled(CanMakeComponent());
+
+ QMenu::showEvent(event);
+}
+
+
+CComponentContextMenu::~CComponentContextMenu()
+{
+ // This will result in a double deletion.
+ // DeletePerformers( );
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be renamed.
+ * @return true if the object can be renamed.
+ */
+bool CComponentContextMenu::CanRenameObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(ITimelineItemBinding::EUserTransaction_Rename);
+}
+
+//=============================================================================
+/**
+ * Rename the object.
+ */
+void CComponentContextMenu::RenameObject()
+{
+ m_TreeControl->DoRename();
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be duplicated.
+ * @return true if the object can be duplicated.
+ */
+bool CComponentContextMenu::CanDuplicateObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(
+ ITimelineItemBinding::EUserTransaction_Duplicate);
+}
+
+//=============================================================================
+/**
+ * Duplicate the object.
+ */
+void CComponentContextMenu::DuplicateObject()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_Duplicate);
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be deleted.
+ * @return true if the object can be deleted.
+ */
+bool CComponentContextMenu::CanDeleteObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(ITimelineItemBinding::EUserTransaction_Delete);
+}
+
+//=============================================================================
+/**
+ * Deletes the object from the scene graph.
+ */
+void CComponentContextMenu::DeleteObject()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_Delete);
+}
+
+//=============================================================================
+/**
+ * Checks to see if the State is a component and can be inspected.
+ * @return true is the state is a component and can be inspected.
+ */
+bool CComponentContextMenu::CanInspectComponent()
+{
+ return m_TimelineItemBinding->IsValidTransaction(
+ ITimelineItemBinding::EUserTransaction_EditComponent);
+}
+
+//=============================================================================
+/**
+ * Inspect the State (Component).
+ * This will make the component the top level item of the timelineview.
+ */
+void CComponentContextMenu::InspectComponent()
+{
+ m_TimelineItemBinding->OpenAssociatedEditor();
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be wrapped in a component.
+ * @return true if the object is allowed to be wrapped in a component.
+ */
+bool CComponentContextMenu::CanMakeComponent()
+{
+ return m_TimelineItemBinding->IsValidTransaction(
+ ITimelineItemBinding::EUserTransaction_MakeComponent);
+}
+
+//=============================================================================
+/**
+ * Wraps the specified asset hierarchy under a component.
+ */
+void CComponentContextMenu::MakeComponent()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_MakeComponent);
+}
+
+//=============================================================================
+/**
+ * Get the full Scripting path of the object and copy it to the clipboard.
+ * This will figure out the proper way to address the object via scripting
+ * and put that path into the clipboard.
+ */
+void CComponentContextMenu::CopyObjectPath()
+{
+ CStudioClipboard::CopyTextToClipboard(m_TimelineItemBinding->GetObjectPath().toQString());
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be copied
+ * @return true if the object can be copied
+ */
+bool CComponentContextMenu::CanCopyObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(ITimelineItemBinding::EUserTransaction_Copy);
+}
+
+//=============================================================================
+/**
+ * Copy the object.
+ */
+void CComponentContextMenu::CopyObject()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_Copy);
+}
+
+bool CComponentContextMenu::CanCutObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(ITimelineItemBinding::EUserTransaction_Cut);
+}
+
+void CComponentContextMenu::CutObject()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_Cut);
+}
+
+//=============================================================================
+/**
+ * Checks to see if the object can be pasted
+ * @return true if the object can be pasted
+ */
+bool CComponentContextMenu::CanPasteObject()
+{
+ return m_TimelineItemBinding->IsValidTransaction(ITimelineItemBinding::EUserTransaction_Paste);
+}
+
+//=============================================================================
+/**
+ * Paste the object.
+ */
+void CComponentContextMenu::PasteObject()
+{
+ m_TimelineItemBinding->PerformTransaction(ITimelineItemBinding::EUserTransaction_Paste);
+}
+
+ITimelineItem *CComponentContextMenu::GetTimelineItem() const
+{
+ return m_TimelineItemBinding->GetTimelineItem();
+}
+
+void CComponentContextMenu::Externalize()
+{
+ m_TimelineItemBinding->Externalize();
+}
+
+void CComponentContextMenu::Internalize()
+{
+ m_TimelineItemBinding->Internalize();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.h b/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.h
new file mode 100644
index 00000000..cf1573e6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ComponentContextMenu.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_COMPONENT_CONTEXT_MENU_H
+#define INCLUDED_COMPONENT_CONTEXT_MENU_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <QMenu>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CBaseTimelineTreeControl;
+class ITimelineItem;
+class ITimelineItemBinding;
+
+class CComponentContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ CComponentContextMenu(CBaseTimelineTreeControl *inTreeControl,
+ ITimelineItemBinding *inTimelineItemBinding,
+ QWidget *parent = nullptr);
+ virtual ~CComponentContextMenu();
+
+protected Q_SLOTS:
+ void RenameObject();
+ void DuplicateObject();
+ void DeleteObject();
+ void InspectComponent();
+ void MakeComponent();
+ void CopyObjectPath();
+ void CopyObject();
+ void PasteObject();
+ void CutObject();
+ void Externalize();
+ void Internalize();
+
+protected:
+ void showEvent(QShowEvent *event) override;
+
+ bool CanRenameObject();
+ bool CanDuplicateObject();
+ bool CanDeleteObject();
+ bool CanInspectComponent();
+ bool CanMakeComponent();
+ bool CanCopyObject();
+ bool CanPasteObject();
+ bool CanCutObject();
+ void Import();
+ void RefreshImport();
+ bool CanImport();
+ bool CanRefreshImport();
+ bool CanExportComponent();
+
+ void Initialize();
+
+ ITimelineItem *GetTimelineItem() const;
+
+ CBaseTimelineTreeControl *m_TreeControl;
+ ITimelineItemBinding *m_TimelineItemBinding;
+ QAction *m_renameAction;
+ QAction *m_duplicateAction;
+ QAction *m_deleteAction;
+ QAction *m_inspectAction;
+ QAction *m_makeAction;
+ QAction *m_copyPathAction;
+ QAction *m_cutAction;
+ QAction *m_copyAction;
+ QAction *m_pasteAction;
+ QAction *m_externalizeAction;
+ QAction *m_internalizeAction;
+};
+#endif // INCLDUED_STATE_CONTEXT_MENU_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.cpp b/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.cpp
new file mode 100644
index 00000000..ce2b296c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FilterToolbar.h"
+#include "ButtonControl.h"
+#include "SystemPreferences.h"
+#include "Renderer.h"
+#include "TimelineTreeLayout.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CFilterToolbar::CFilterToolbar(CTimelineTreeLayout *inTreeLayout)
+ : CFlowLayout(nullptr, false)
+{
+ m_TreeLayout = inTreeLayout;
+
+ SetFlowDirection(FLOW_HORIZONTAL);
+ SetAlignment(ALIGN_TOP, ALIGN_LEFT);
+ SetLeftMargin(1);
+
+ // Create the buttons
+ m_FltrBehaviorsBtn = new CProceduralButton<CToggleButton>();
+ m_FltrPropertiesBtn = new CProceduralButton<CToggleButton>();
+ m_FltrMaterialsBtn = new CProceduralButton<CToggleButton>();
+ m_FltrShyBtn = new CProceduralButton<CToggleButton>();
+ m_FltrVisibleBtn = new CProceduralButton<CToggleButton>();
+
+ // Load the bitmaps
+ m_FltrBehaviorsBtn->SetUpImage("obsolete_placeholder.png");
+ m_FltrBehaviorsBtn->SetDownImage("obsolete_placeholder.png");
+
+ m_FltrPropertiesBtn->SetUpImage("obsolete_placeholder.png");
+ m_FltrPropertiesBtn->SetDownImage("obsolete_placeholder.png");
+
+ m_FltrMaterialsBtn->SetUpImage("obsolete_placeholder.png");
+ m_FltrMaterialsBtn->SetDownImage("obsolete_placeholder.png");
+
+ m_FltrShyBtn->SetUpImage("Toggle-Shy.png");
+ m_FltrShyBtn->SetDownImage("Toggle-Shy.png");
+
+ m_FltrVisibleBtn->SetUpImage("Toggle-HideShow.png");
+ m_FltrVisibleBtn->SetDownImage("Toggle-HideShow.png");
+
+ // Turn off the left border of each button since they are all next to each other, otherwise,
+ // you'll get a double line effect
+ CProceduralButton<CToggleButton>::SBorderOptions theBorderOptions(false, true, true, true);
+ m_FltrBehaviorsBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrPropertiesBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrMaterialsBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrShyBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrVisibleBtn->SetBorderVisibilityAll(theBorderOptions);
+ CProceduralButton<CButtonControl>::SBorderOptions theBorderOptions2(false, true, true, true);
+
+ // Set the max sizes for the buttons
+ m_FltrBehaviorsBtn->SetAbsoluteSize(m_FltrBehaviorsBtn->GetSize());
+ m_FltrPropertiesBtn->SetAbsoluteSize(m_FltrPropertiesBtn->GetSize());
+ m_FltrMaterialsBtn->SetAbsoluteSize(m_FltrMaterialsBtn->GetSize());
+ m_FltrShyBtn->SetAbsoluteSize(m_FltrShyBtn->GetSize());
+ m_FltrVisibleBtn->SetAbsoluteSize(m_FltrShyBtn->GetSize());
+
+ // Tooltips
+ m_FltrBehaviorsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_BEHAVIOR2));
+ m_FltrPropertiesBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_PROPERTIES2));
+ m_FltrMaterialsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_MATERIALS2));
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY2));
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE2));
+
+ // Callback for one of the Filter buttons being clicked on
+ m_FltrBehaviorsBtn->SigToggle.connect(std::bind(&CFilterToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_FltrPropertiesBtn->SigToggle.connect(std::bind(&CFilterToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_FltrMaterialsBtn->SigToggle.connect(std::bind(&CFilterToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_FltrShyBtn->SigToggle.connect(std::bind(&CFilterToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_FltrVisibleBtn->SigToggle.connect(std::bind(&CFilterToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ // Add the buttons to this layout
+ AddChild(m_FltrMaterialsBtn);
+ AddChild(m_FltrPropertiesBtn);
+ AddChild(m_FltrBehaviorsBtn);
+ AddChild(m_FltrShyBtn);
+ AddChild(m_FltrVisibleBtn);
+
+ m_FltrBehaviorsBtn->SetToggleState(false);
+ m_FltrPropertiesBtn->SetToggleState(false);
+ m_FltrMaterialsBtn->SetToggleState(false);
+ m_FltrShyBtn->SetToggleState(false);
+ m_FltrVisibleBtn->SetToggleState(false);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CFilterToolbar::~CFilterToolbar()
+{
+ delete m_FltrBehaviorsBtn;
+ delete m_FltrPropertiesBtn;
+ delete m_FltrMaterialsBtn;
+ delete m_FltrShyBtn;
+ delete m_FltrVisibleBtn;
+}
+
+//=============================================================================
+/**
+ * Overriden to draw some highlighting.
+ */
+void CFilterToolbar::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+ // Draw the highlight at the bottom
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the line on the left side
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(0, 0);
+ inRenderer->LineTo(0, theRect.size.y - 1);
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for behavior objects in the timeline.
+ * @param inFilter true to filter behaviors out of the timeline, false to show
+ * behaviors in the timeline.
+ */
+void CFilterToolbar::FilterBehaviors(bool inFilter)
+{
+ if (inFilter)
+ m_FltrBehaviorsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_BEHAVIOR1));
+ else
+ m_FltrBehaviorsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_BEHAVIOR2));
+
+ m_TreeLayout->GetFilter()->SetBehaviors(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for properties on objects in the timeline.
+ * @param inFilter true to filter properties out of the timeline, false to show
+ * properties in the timeline.
+ */
+void CFilterToolbar::FilterProperties(bool inFilter)
+{
+ if (inFilter)
+ m_FltrPropertiesBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_PROPERTIES1));
+ else
+ m_FltrPropertiesBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_PROPERTIES2));
+
+ m_TreeLayout->GetFilter()->SetProperties(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for material objects.
+ * @param inFilter true to filter material objects out of the timeline, false to show
+ * material objects in the timeline.
+ */
+void CFilterToolbar::FilterMaterials(bool inFilter)
+{
+ if (inFilter)
+ m_FltrMaterialsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_MATERIALS1));
+ else
+ m_FltrMaterialsBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_MATERIALS2));
+
+ m_TreeLayout->GetFilter()->SetMaterials(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for shy objects.
+ * @param inFilter true to filter shy objects out of the timeline, false to show
+ * shy objects in the timeline.
+ */
+void CFilterToolbar::FilterShy(bool inFilter)
+{
+ if (inFilter)
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY1));
+ else
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY2));
+
+ m_TreeLayout->GetFilter()->SetShy(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for visible objects.
+ * @param inFilter true to filter visible objects out of the timeline, false to show
+ * shy objects in the timeline.
+ */
+void CFilterToolbar::FilterVisible(bool inFilter)
+{
+ if (inFilter)
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE1));
+ else
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE2));
+
+ m_TreeLayout->GetFilter()->SetVisible(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Handles turning a filter on or off in response to a button being pressed.
+ * @param inButton button that generated the event
+ * @param inState new state of the button after being toggled
+ */
+void CFilterToolbar::OnButtonToggled(CToggleButton *inButton, CButtonControl::EButtonState inState)
+{
+ bool theFilterNeedsApplied = (inState == CButtonControl::EBUTTONSTATE_UP);
+
+ if (inButton == m_FltrBehaviorsBtn)
+ FilterBehaviors(theFilterNeedsApplied);
+ else if (inButton == m_FltrPropertiesBtn)
+ FilterProperties(theFilterNeedsApplied);
+ else if (inButton == m_FltrMaterialsBtn)
+ FilterMaterials(theFilterNeedsApplied);
+ else if (inButton == m_FltrShyBtn)
+ FilterShy(theFilterNeedsApplied);
+ else if (inButton == m_FltrVisibleBtn)
+ FilterVisible(theFilterNeedsApplied);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.h b/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.h
new file mode 100644
index 00000000..8148fff3
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/FilterToolbar.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_FILTER_TOOLBAR_H
+#define INCLUDED_FILTER_TOOLBAR_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "FlowLayout.h"
+#include "ProceduralButton.h"
+#include "ToggleButton.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CButtonControl;
+class CRenderer;
+class CTimelineTreeLayout;
+
+//=============================================================================
+/**
+ * Control at the top of the timeline containing filter buttons.
+ */
+class CFilterToolbar : public CFlowLayout
+{
+public:
+ CFilterToolbar(CTimelineTreeLayout *inTreeLayout);
+ virtual ~CFilterToolbar();
+ void Draw(CRenderer *inRenderer) override;
+
+ void FilterBehaviors(bool inFilter);
+ void FilterProperties(bool inFilter);
+ void FilterMaterials(bool inFilter);
+ void FilterShy(bool inFilter);
+ void FilterVisible(bool inFilter);
+
+ void OnButtonToggled(CToggleButton *inButton, CToggleButton::EButtonState inState);
+
+protected:
+ CProceduralButton<CToggleButton> *m_FltrBehaviorsBtn;
+ CProceduralButton<CToggleButton> *m_FltrPropertiesBtn;
+ CProceduralButton<CToggleButton> *m_FltrMaterialsBtn;
+ CProceduralButton<CToggleButton> *m_FltrShyBtn;
+ CProceduralButton<CToggleButton> *m_FltrVisibleBtn;
+ CTimelineTreeLayout *m_TreeLayout;
+};
+
+#endif // INCLUDED_FILTER_TOOLBAR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/IBreadCrumbProvider.h b/src/Authoring/Studio/Palettes/Timeline/IBreadCrumbProvider.h
new file mode 100644
index 00000000..18aa31ac
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/IBreadCrumbProvider.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_IBREADCRUMBPROVIDER_H
+#define INCLUDED_IBREADCRUMBPROVIDER_H 1
+
+#pragma once
+
+#include <QColor>
+#include <QString>
+
+#include <boost/signals.hpp>
+
+class QPixmap;
+
+struct SBreadCrumb
+{
+ QColor m_Color; /// Color for text of the bread crumb
+ QString m_String; /// Text to be displayed for the bread crumb
+};
+
+//=============================================================================
+/**
+ * A interface class for the breadcrumb control, to walk down the breadcrumb trail, without having
+ * to know any underlying implementations.
+ */
+class IBreadCrumbProvider
+{
+public:
+ typedef std::vector<SBreadCrumb> TTrailList;
+
+public:
+ virtual ~IBreadCrumbProvider() {}
+
+ virtual TTrailList GetTrail(bool inRefresh = true) = 0;
+ virtual void OnBreadCrumbClicked(long inTrailIndex) = 0;
+
+ virtual QPixmap GetRootImage() const = 0;
+ virtual QPixmap GetBreadCrumbImage() const = 0;
+ virtual QPixmap GetSeparatorImage() const = 0;
+ virtual QPixmap GetActiveBreadCrumbImage() const = 0;
+
+ boost::signal0<void> SigBreadCrumbUpdate;
+};
+
+#endif // INCLUDED_IBREADCRUMBPROVIDER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ITimelineControl.h b/src/Authoring/Studio/Palettes/Timeline/ITimelineControl.h
new file mode 100644
index 00000000..05af776e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ITimelineControl.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_ITIMELINE_CONTROL_H
+#define INCLUDED_ITIMELINE_CONTROL_H 1
+
+#pragma once
+
+#include "Rct.h"
+
+class ISnappingListProvider;
+
+class ITimelineControl
+{
+public:
+ virtual ~ITimelineControl() {}
+
+ virtual void OnLayoutChanged() = 0;
+ virtual CRct GetBounds() const = 0;
+ virtual void HideTimelineMoveableTooltip() = 0;
+ virtual ISnappingListProvider *GetSnappingListProvider() const = 0;
+};
+
+#endif // INCLUDED_TIMELINE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.cpp b/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.cpp
new file mode 100644
index 00000000..78a2e4cb
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.cpp
@@ -0,0 +1,405 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "KeyframeContextMenu.h"
+#include "IDoc.h"
+#include "CColor.h"
+#include "Preferences.h"
+#include "Dialogs.h"
+#include "TimebarControl.h"
+#include "Bindings/ITimelineTimebar.h"
+#include "Bindings/ITimelineKeyframesManager.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "IKeyframe.h"
+
+#include <QColorDialog>
+
+#define IDC_KEYFRAME_COLOR_BOX 1002
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CBaseKeyframeContextMenu::CBaseKeyframeContextMenu(QWidget *parent)
+ : QMenu(parent)
+ , m_HasDynamicSelectedKeyframes(false)
+ , m_KeyframesManager(nullptr)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CBaseKeyframeContextMenu::~CBaseKeyframeContextMenu()
+{
+}
+
+void CBaseKeyframeContextMenu::Initialize(ITimelineKeyframesManager *inKeyframesManager)
+{
+ m_KeyframesManager = inKeyframesManager;
+ ASSERT(m_KeyframesManager);
+
+ //"Insert Keyframe"
+ ITimelineItemKeyframesHolder *theKeyframesHolder = GetKeyframesHolder();
+ if (theKeyframesHolder) {
+ m_insertAction = new QAction(tr("Insert Keyframe"));
+ connect(m_insertAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::InsertKeyframe);
+ addAction(m_insertAction);
+ }
+
+ bool theHasKeysSelected = m_KeyframesManager->HasSelectedKeyframes();
+ bool theCanCopyKeys = m_KeyframesManager->CanPerformKeyframeCopy();
+ bool theCanPasteKeys = m_KeyframesManager->CanPerformKeyframePaste();
+
+ m_cutAction = new QAction(tr("Cut Selected Keyframes"));
+ connect(m_cutAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::CutSelectedKeys);
+ m_cutAction->setEnabled(theCanCopyKeys);
+ addAction(m_cutAction);
+
+ m_copyAction = new QAction(tr("Copy Selected Keyframes"));
+ connect(m_copyAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::CopySelectedKeys);
+ m_copyAction->setEnabled(theCanCopyKeys);
+ addAction(m_copyAction);
+
+ m_pasteAction = new QAction(tr("Paste Keyframes"));
+ connect(m_pasteAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::PasteSelectedKeys);
+ m_pasteAction->setEnabled(theCanPasteKeys);
+ addAction(m_pasteAction);
+
+ m_deleteSelectedAction = new QAction(tr("Delete Selected Keyframes"));
+ connect(m_deleteSelectedAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::DeleteSelectedKeys);
+ m_deleteSelectedAction->setEnabled(theHasKeysSelected);
+ addAction(m_deleteSelectedAction);
+
+ //"Delete All Channel Keyframes"
+ if (theKeyframesHolder) {
+ m_deleteChannelKeysAction = new QAction(tr("Delete All Channel Keyframes"));
+ connect(m_deleteChannelKeysAction, &QAction::triggered, this, &CBaseKeyframeContextMenu::DeleteChannelKeys);
+ addAction(m_deleteChannelKeysAction);
+ }
+}
+
+//=============================================================================
+/**
+ * Called when the cut selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::InsertKeyframe()
+{
+ GetKeyframesHolder()->InsertKeyframe();
+}
+
+//=============================================================================
+/**
+ * Called when the cut selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::DeleteChannelKeys()
+{
+ GetKeyframesHolder()->DeleteAllChannelKeyframes();
+}
+
+//=============================================================================
+/**
+ * Called when the cut selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::CutSelectedKeys()
+{
+ m_KeyframesManager->RemoveKeyframes(true);
+}
+
+//=============================================================================
+/**
+ * Called when the copy selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::CopySelectedKeys()
+{
+ m_KeyframesManager->CopyKeyframes();
+}
+
+//=============================================================================
+/**
+ * Called when the paste selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::PasteSelectedKeys()
+{
+ m_KeyframesManager->PasteKeyframes();
+}
+
+//=============================================================================
+/**
+ * Called when the delete selected keys option is chosen by the user. Makes a call
+ * to the doc to handle the request
+ */
+void CBaseKeyframeContextMenu::DeleteSelectedKeys()
+{
+ m_KeyframesManager->RemoveKeyframes(false);
+}
+
+//=============================================================================
+/**
+ * Toggle if the selected keyframe(s) is dynamic
+ */
+void CBaseKeyframeContextMenu::MakeDynamic()
+{
+ m_KeyframesManager->SetKeyframesDynamic(!m_HasDynamicSelectedKeyframes); // toggle
+}
+
+//==============================================================================
+// CTimebarKeyframeContextMenu
+//==============================================================================
+CTimebarKeyframeContextMenu::CTimebarKeyframeContextMenu(ITimebarControl *inTimebarControl, ITimelineKeyframesManager *inKeyframesManager,
+ bool inShowTimebarPropertiesOptions /*= true */, QWidget *parent)
+ : CBaseKeyframeContextMenu(parent)
+ , m_TimebarControl(inTimebarControl)
+ , m_ShowTimebarPropertiesOptions(inShowTimebarPropertiesOptions)
+{
+ Initialize(inKeyframesManager);
+}
+
+CTimebarKeyframeContextMenu::~CTimebarKeyframeContextMenu()
+{
+}
+
+void CTimebarKeyframeContextMenu::Initialize(ITimelineKeyframesManager *inKeyframesManager)
+{
+ CBaseKeyframeContextMenu::Initialize(inKeyframesManager);
+
+ // Dynamic keyframes ( the way it was set up before was, this option is still shown, but grayed
+ // out even if this context menu isn't triggered from right-clicking on a timebar )
+ QString theMakeDynamicOption = tr("Make Animations Static");
+
+ if (m_TimebarControl->GetKeyframesHolder()->GetKeyframeCount() > 0) {
+ m_HasDynamicSelectedKeyframes = m_KeyframesManager->HasDynamicKeyframes();
+
+ if (!m_HasDynamicSelectedKeyframes) {
+ theMakeDynamicOption = tr("Make Animations Dynamic");
+ }
+
+ m_makeDynamicAction = new QAction(theMakeDynamicOption);
+ connect(m_makeDynamicAction, &QAction::triggered, this, &CTimebarKeyframeContextMenu::MakeDynamic);
+ addAction(m_makeDynamicAction);
+ }
+
+ if (m_ShowTimebarPropertiesOptions) {
+ // Timebar specific actions
+ addSeparator();
+ m_timeBarColorAction = new QAction(tr("Change Time Bar Color"));
+ connect(m_timeBarColorAction, &QAction::triggered, this, &CTimebarKeyframeContextMenu::ChangeTimebarColor);
+
+ m_timeBarTextAction = new QAction(tr("Change Time Bar Text"));
+ connect(m_timeBarTextAction, &QAction::triggered, this, &CTimebarKeyframeContextMenu::ChangeTimebarText);
+
+ // Change the text for the timebar option depending on whether they are currently being
+ // shown or not
+ QString theTimebarHandleTextID = tr("Show Time Bar Handles");
+ if (CPreferences::GetUserPreferences("Timeline").GetValue("ShowTimebarHandles", false))
+ theTimebarHandleTextID = tr("Hide Time Bar Handles");
+
+ m_timeBarHandlesAction = new QAction(theTimebarHandleTextID);
+ connect(m_timeBarHandlesAction, &QAction::triggered, this, &CTimebarKeyframeContextMenu::ToggleTimebarHandles);
+
+ m_timeBarTimeAction = new QAction(tr("Set Timebar Time"));
+ connect(m_timeBarTimeAction, &QAction::triggered, this, &CTimebarKeyframeContextMenu::SetTimebarTime);
+ }
+}
+
+void CTimebarKeyframeContextMenu::MakeDynamic()
+{
+ m_KeyframesManager->SelectAllKeyframes();
+ CBaseKeyframeContextMenu::MakeDynamic();
+}
+
+//=============================================================================
+/**
+ * To show "Insert Keyframe" and "Delete All Channel Keyframes"
+ */
+ITimelineItemKeyframesHolder *CTimebarKeyframeContextMenu::GetKeyframesHolder()
+{
+ return m_TimebarControl->GetKeyframesHolder();
+}
+
+//=============================================================================
+/**
+ * Called when the copy selected keys option is chosen by the user.
+ */
+void CTimebarKeyframeContextMenu::ChangeTimebarColor()
+{
+ const auto color = QColorDialog::getColor(m_TimebarControl->GetTimebarColor());
+ if (color.isValid())
+ m_TimebarControl->SetTimebarColor(color);
+}
+
+//=============================================================================
+/**
+ * Called when the copy selected keys option is chosen by the user.
+ */
+void CTimebarKeyframeContextMenu::ChangeTimebarText()
+{
+ m_TimebarControl->OnEditTimeComment();
+}
+
+//=============================================================================
+/**
+ * Shows or hides timebar handles in the timeline. If timebar handles are
+ * currently being shown, they are hidden, and the preference is stored. If
+ * timebar handles are being hidden, they are shown.
+ */
+void CTimebarKeyframeContextMenu::ToggleTimebarHandles()
+{
+ // Get the current timebar handle preference
+ bool theHandlesAreShowing =
+ CPreferences::GetUserPreferences("Timeline").GetValue("ShowTimebarHandles", false);
+ // Switch the preference.
+ CPreferences::GetUserPreferences("Timeline")
+ .SetValue("ShowTimebarHandles", !theHandlesAreShowing);
+ if (m_TimebarControl)
+ m_TimebarControl->OnToggleTimebarHandles();
+}
+
+//=============================================================================
+/**
+ * SetTimebarTime: This is the event handler that will be called when the user
+ * chooses set timebar time from a pop up menu. This pop up
+ *menu
+ * is triggered when the user right click on the selected
+ *timebar.
+ * It displays a time edit dialog to allow the user to set the
+ * start and end time of the timebar time.
+ * @param NONE
+ * @return NONE
+ */
+void CTimebarKeyframeContextMenu::SetTimebarTime()
+{
+ m_TimebarControl->SetTimebarTime();
+}
+
+//==============================================================================
+// CKeyframeContextMenu
+//==============================================================================
+CKeyframeContextMenu::CKeyframeContextMenu(ITimelineKeyframesManager *inKeyframesManager,
+ ITimelineItemProperty *inTimelineItemProperty, QWidget *parent)
+ : CBaseKeyframeContextMenu(parent)
+ , m_TimelineItemProperty(inTimelineItemProperty)
+{
+ Initialize(inKeyframesManager);
+}
+
+CKeyframeContextMenu::~CKeyframeContextMenu()
+{
+}
+
+void CKeyframeContextMenu::Initialize(ITimelineKeyframesManager *inKeyframesManager)
+{
+ CBaseKeyframeContextMenu::Initialize(inKeyframesManager);
+
+ // Dynamic keyframes ( the way it was set up before was, this option is still shown, but grayed
+ // out even if this context menu isn't triggered from right-clicking on a timebar )
+ QString theMakeDynamicOption = tr("Make Animations Static");
+ m_HasDynamicSelectedKeyframes =
+ m_TimelineItemProperty && m_TimelineItemProperty->IsDynamicAnimation();
+
+ if (!m_HasDynamicSelectedKeyframes) {
+ theMakeDynamicOption = tr("Make Animations Dynamic");
+ }
+
+ m_makeDynamicAction = new QAction(theMakeDynamicOption);
+ connect(m_makeDynamicAction, &QAction::triggered, this, &CKeyframeContextMenu::MakeDynamic);
+ addAction(m_makeDynamicAction);
+
+ addSeparator();
+ bool theHasKeysSelected = m_KeyframesManager->HasSelectedKeyframes();
+ m_setInterpolationAction = new QAction(tr("Set Interpolation"));
+ connect(m_setInterpolationAction, &QAction::triggered, this, &CKeyframeContextMenu::SetInterpolation);
+ addAction(m_setInterpolationAction);
+
+ m_setKeyframeTimeAction = new QAction(tr("Set Keyframe Time"));
+ connect(m_setKeyframeTimeAction, &QAction::triggered, this, &CKeyframeContextMenu::SetKeyframeTime);
+ m_setKeyframeTimeAction->setEnabled(theHasKeysSelected);
+ addAction(m_setKeyframeTimeAction);
+}
+
+void CKeyframeContextMenu::MakeDynamic()
+{
+ if (m_TimelineItemProperty != nullptr && m_TimelineItemProperty->GetKeyframeCount() > 0) {
+ m_TimelineItemProperty->SelectKeyframes(
+ true, m_TimelineItemProperty->GetKeyframeByIndex(0)->GetTime());
+ }
+
+ CBaseKeyframeContextMenu::MakeDynamic();
+}
+
+//=============================================================================
+/**
+ * SetTime: Saves the keyframe time
+ * @param inTime is the keyframe time
+ */
+void CKeyframeContextMenu::SetTime(long inTime)
+{
+ m_Time = inTime;
+}
+
+//=============================================================================
+/**
+ * Called when the set interpolation option is taken by the user. Uses the left most
+ * selected key for the base interpolation in the dialog box. User can then set the interpolation
+ * and it is propagated to all teh keys
+ */
+void CKeyframeContextMenu::SetInterpolation()
+{
+ m_KeyframesManager->SetKeyframeInterpolation();
+}
+
+//=============================================================================
+/**
+ * SetKeyframeTime: This is the event handler that will be called when the user
+ * chooses set keyframe time from a pop up menu. This pop up
+ *menu
+ * is triggered when the user right click on the selected
+ *keyframe.
+ * It displays a time edit dialog to allow the user to set the
+ * keyframe time.
+ * @param NONE
+ * @return NONE
+ */
+void CKeyframeContextMenu::SetKeyframeTime()
+{
+ m_KeyframesManager->SetKeyframeTime(m_Time);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.h b/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.h
new file mode 100644
index 00000000..b11ccb2f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/KeyframeContextMenu.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_KEYFRAME_CONTEXT_MENU
+#define INCLUDED_KEYFRAME_CONTEXT_MENU 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <QMenu>
+
+class ITimelineKeyframesManager;
+class ITimelineItemProperty;
+class ITimelineTimebar;
+class ITimeChangeCallback;
+class ITimelineItemKeyframesHolder;
+class ITimebarControl;
+
+class QAction;
+
+//=============================================================================
+/**
+ * Abstract class that contain the common items for the keyframes-related context menu.
+ */
+class CBaseKeyframeContextMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ CBaseKeyframeContextMenu(QWidget *parent = nullptr);
+ virtual ~CBaseKeyframeContextMenu();
+
+protected:
+ virtual void Initialize(ITimelineKeyframesManager *inKeyframesManager);
+ virtual void MakeDynamic();
+ virtual ITimelineItemKeyframesHolder *GetKeyframesHolder() { return nullptr; }
+
+protected Q_SLOTS:
+ void CutSelectedKeys();
+ void CopySelectedKeys();
+ void PasteSelectedKeys();
+ void DeleteSelectedKeys();
+ void InsertKeyframe();
+ void DeleteChannelKeys();
+
+protected:
+ bool m_HasDynamicSelectedKeyframes;
+ ITimelineKeyframesManager *m_KeyframesManager;
+ QAction *m_insertAction;
+ QAction *m_cutAction;
+ QAction *m_copyAction;
+ QAction *m_pasteAction;
+ QAction *m_deleteSelectedAction;
+ QAction *m_deleteChannelKeysAction;
+};
+
+//=============================================================================
+/**
+ * Context menu right-clicking on a timebar,
+ */
+class CTimebarKeyframeContextMenu : public CBaseKeyframeContextMenu
+{
+ Q_OBJECT
+public:
+ CTimebarKeyframeContextMenu(ITimebarControl *inTimebarControl,
+ ITimelineKeyframesManager *inKeyframesManager,
+ bool inShowTimebarPropertiesOptions = true,
+ QWidget *parent = nullptr);
+ ~CTimebarKeyframeContextMenu();
+
+protected:
+ void Initialize(ITimelineKeyframesManager *inKeyframesManager) override;
+ ITimelineItemKeyframesHolder *GetKeyframesHolder() override;
+ void MakeDynamic() override;
+
+ void ChangeTimebarColor();
+ void ChangeTimebarText();
+ void ToggleTimebarHandles();
+ void SetTimebarTime();
+
+protected:
+ ITimebarControl *m_TimebarControl;
+ bool m_ShowTimebarPropertiesOptions;
+
+ QAction *m_makeDynamicAction;
+ QAction *m_timeBarColorAction;
+ QAction *m_timeBarTextAction;
+ QAction *m_timeBarHandlesAction;
+ QAction *m_timeBarTimeAction;
+};
+
+//=============================================================================
+/**
+ * Context menu for right-clicking on selected keyframe(s)
+ * so even though the assoicated action isn't limited to the keyframe that was right-clicked on,
+ * this is still a different one from right-clicking on outside the keyframe.
+ * This is how the old system used to be.
+ */
+class CKeyframeContextMenu : public CBaseKeyframeContextMenu
+{
+ Q_OBJECT
+public:
+ CKeyframeContextMenu(ITimelineKeyframesManager *inKeyframesManager,
+ ITimelineItemProperty *inTimelineItemProperty = nullptr,
+ QWidget *parent = nullptr);
+ ~CKeyframeContextMenu();
+
+ void SetTime(long inTime);
+
+protected:
+ void Initialize(ITimelineKeyframesManager *inKeyframesManager) override;
+ void MakeDynamic() override;
+
+protected Q_SLOTS:
+ void SetInterpolation();
+ void SetKeyframeTime();
+
+protected:
+ long m_Time;
+
+private:
+ ITimelineItemProperty *m_TimelineItemProperty;
+ QAction *m_makeDynamicAction;
+ QAction *m_setKeyframeTimeAction;
+ QAction *m_setInterpolationAction;
+};
+#endif // INCLUDED_KEYFRAME_CONTEXT_MENU
diff --git a/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.cpp b/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.cpp
new file mode 100644
index 00000000..676a5d2d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.cpp
@@ -0,0 +1,30 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "MultiSelectAspect.h"
diff --git a/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.h b/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.h
new file mode 100644
index 00000000..0c19b2b2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/MultiSelectAspect.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_MULTISELECT_ASPECT_H
+#define INCLUDED_MULTISELECT_ASPECT_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StateRow.h"
+#include "TimelineTimelineLayout.h"
+#include "Bindings/IKeyframeSelector.h"
+
+//==============================================================================
+/**
+ * This class handles the multi selection aspect for keyframes in the timeline.
+ * It toggles each keyframe's select state on or off, depending on whether the
+ * mouse rectangle is over them and if the modifier key is down.
+ */
+template <typename TSTLKeyFrameList>
+class CMultiSelectAspect
+{
+
+public:
+ typedef typename TSTLKeyFrameList::iterator TSTLKeyframesItr;
+
+protected:
+ TSTLKeyFrameList &m_STLKeyframes; ///< stores list of keyframes in a vector (STL)
+ IKeyframeSelector *m_KeyframeSelector; ///< the interface that performs the keyframes selection
+
+public:
+ //=============================================================================
+ /**
+ * Constructor
+ * @param inSTLKeyframes stores a list of keyframes in a vector
+ * @param inDoc stores the studio document
+ */
+ CMultiSelectAspect(TSTLKeyFrameList &inSTLKeyframes, IKeyframeSelector *inKeyframeSelector)
+ : m_STLKeyframes(inSTLKeyframes)
+ , m_KeyframeSelector(inKeyframeSelector)
+ {
+ }
+
+ //=============================================================================
+ /**
+ * Destructor
+ */
+ ~CMultiSelectAspect() {}
+
+ //=============================================================================
+ /**
+ * CommitSelections: Overwrites all previous keyframe states with the current keyframe states.
+ * This will prevent the keyframes in the current selection
+*from
+* switching states as we select other keyframes.
+ */
+ void CommitSelections()
+ {
+ // Iterates each keyframe and set the previous state to the current one.
+ TSTLKeyframesItr thePos = m_STLKeyframes.begin();
+ for (; thePos != m_STLKeyframes.end(); ++thePos) {
+ (*thePos)->SetPreviousSelectState((*thePos)->IsSelected());
+ }
+ }
+
+ //=============================================================================
+ /**
+ * MultiSelect: Handles the selection of keyframes in a given rect.
+ * @param inRect stores the rectangle that will be used to select the keyframes within it.
+ * @param inModifierKeyDown indicates if thte modifier key is pressed.
+ */
+ void MultiSelect(CRct inRect, bool inModifierKeyDown)
+ {
+ // Iterates through the keyframes and checks if the keys are in the rect and
+ // perform the necessary selection operations on each keyframe.
+ TSTLKeyframesItr thePos = m_STLKeyframes.begin();
+ for (; thePos != m_STLKeyframes.end(); ++thePos) {
+ bool isInRect = (*thePos)->IsInRect(inRect);
+ if ((*thePos)->IsEnabled()) {
+ if (inModifierKeyDown) {
+ if (isInRect) {
+ if (!(*thePos)->GetRectOverHandled()) {
+ bool theSelectState = (*thePos)->IsSelected();
+
+ // Update the previous select state
+ (*thePos)->SetPreviousSelectState(theSelectState);
+ // Negate the keyframe state when it is in the rectangle
+ theSelectState = !theSelectState;
+ (*thePos)->Select(theSelectState);
+ m_KeyframeSelector->SelectKeyframes(theSelectState,
+ (*thePos)->GetTime());
+
+ // Set the RectOverFlag to true, so that we will not repeat the negation
+ // in indefinitely.
+ (*thePos)->SetRectOverHandled(true);
+ }
+ } else {
+ // When the rectangle is not over the current keyframe, revert its state to
+ // the previous one
+ if ((*thePos)->IsSelected() != (*thePos)->GetPreviousSelectState()) {
+ (*thePos)->Select((*thePos)->GetPreviousSelectState());
+ m_KeyframeSelector->SelectKeyframes((*thePos)->GetPreviousSelectState(),
+ (*thePos)->GetTime());
+ }
+ (*thePos)->SetRectOverHandled(false);
+ }
+ } else {
+ // When modifier is not pressed we will just select the keyframes if it is over
+ // in the rectangle and deselect it when it isn't.
+ if ((*thePos)->IsSelected() != isInRect) {
+ (*thePos)->SetPreviousSelectState((*thePos)->IsSelected());
+ m_KeyframeSelector->SelectKeyframes(isInRect, (*thePos)->GetTime());
+ (*thePos)->Select(isInRect);
+ }
+ (*thePos)->SetRectOverHandled(false);
+ }
+ }
+ }
+ }
+};
+#endif // INCLUDED_MULTISELECT_ASPECT_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Playhead.cpp b/src/Authoring/Studio/Palettes/Timeline/Playhead.cpp
new file mode 100644
index 00000000..c010a854
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Playhead.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "Playhead.h"
+#include "Renderer.h"
+#include "TimelineTimelineLayout.h"
+#include "IDoc.h"
+#include "HotKeys.h"
+#include "ResourceCache.h"
+#include "TimeEditDlg.h"
+#include "TimeMeasure.h"
+
+//=============================================================================
+/**
+ * Create a new Playhead.
+ * The timeline is used for notifying of time changes and for scrolling.
+ * @param inTimeline the timeline that this is a part of.
+ */
+CPlayhead::CPlayhead(CTimelineTimelineLayout *inTimeline, IDoc *inDoc)
+ : m_IsMouseDown(false)
+ , m_MinimumPosition(0)
+ , m_MaximumPosition(LONG_MAX)
+ , m_InitialOffset(0)
+ , m_Doc(inDoc)
+{
+ m_Timeline = inTimeline;
+
+ m_PlayheadImage = CResourceCache::GetInstance()->GetBitmap("PlaybackHead.png");
+ SetName("Playhead");
+
+ // Set the line to the middle of this.
+ m_LinePos = GetSize().x / 2;
+ SetAlpha(128);
+}
+
+CPlayhead::~CPlayhead()
+{
+}
+
+//=============================================================================
+/**
+ * Call from the OverlayControl to perform the draw.
+ * Wish this didn't have to be a special function but...
+ * @param inRenderer the renderer to draw to.
+ */
+void CPlayhead::Draw(CRenderer *inRenderer)
+{
+ // If this goes before position then clip it at 0.
+ if (GetPosition().x < 0) {
+ CRct theClipRect(GetSize());
+ theClipRect.Offset(CPt(-GetPosition().x, 0));
+ inRenderer->PushClippingRect(theClipRect);
+ }
+
+ // Draw the playhead
+ inRenderer->DrawBitmap(CPt(0, 2), m_PlayheadImage);
+
+ // Draw the line
+ inRenderer->PushPen(CColor(255, 0, 0));
+ inRenderer->MoveTo(m_LinePos, 20);
+ inRenderer->LineTo(m_LinePos, GetSize().y - 1);
+ inRenderer->PopPen();
+
+ // If we added an extra clipping rect then remove it.
+ if (GetPosition().x < 0) {
+ inRenderer->PopClippingRect();
+ }
+}
+
+//=============================================================================
+/**
+ * Handles mouse down messages.
+ * @param inPoint where the mouse was clicked.
+ * @param inFlags the state of the mouse.
+ */
+bool CPlayhead::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // If no one else processed it then process the command.
+ if (!COverlayControl::OnMouseDown(inPoint, inFlags)) {
+ m_Snapper.Clear();
+ m_Snapper.SetSource(this);
+ m_Timeline->PopulateSnappingList(&m_Snapper);
+
+ // m_Snapper.SetTimeOffset( m_Timeline->GetViewTimeOffset( ) );
+ m_Snapper.SetSnappingKeyframes(true);
+ m_Snapper.BeginDrag(inPoint.x, GetSize().x / 2);
+
+ m_InitialOffset = inPoint.x;
+ m_IsMouseDown = true;
+
+ m_Timeline->RecalcTime((inFlags & CHotKeys::MODIFIER_CONTROL) == 0, 0);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles mouse double click messages. Pops up a time edit dialog box for
+ * modifying playhead time.
+ * @param inPoint the location of the mouse in local coordinates.
+ * @param inFlags the state of the mouse.
+ */
+bool CPlayhead::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.ShowDialog(GetCurrentTime(), 0, m_Doc, PLAYHEAD);
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handles mouse move messages.
+ * @param inPoint the location of the mouse in local coordinates.
+ * @param inFlags the state of the mouse.
+ */
+void CPlayhead::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ COverlayControl::OnMouseMove(inPoint, inFlags);
+
+ // If we are down then move the playhead
+ if (m_IsMouseDown) {
+ long theTime = m_Snapper.ProcessDrag(GetCurrentTime(), inPoint.x, inFlags);
+ bool theUpdateClientTimeFlag = (inFlags & CHotKeys::MODIFIER_CONTROL) == 0;
+
+ UpdateTime(theTime, theUpdateClientTimeFlag);
+ }
+}
+
+//=============================================================================
+/**
+ * Updates the time of the active context
+ * @param inTime the new time
+ * @param inUpdateClient true if scene is to be redrawn
+ */
+void CPlayhead::UpdateTime(long inTime, bool /*inUpdateClient*/)
+{
+ // true to "check bounds" to ensure playhead is within valid range.
+ m_Doc->NotifyTimeChanged(inTime);
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse was let go.
+ * @param inPoint the location of the mouse in local coordinates.
+ * @param inFlags the state of the mouse.
+ */
+void CPlayhead::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ COverlayControl::OnMouseUp(inPoint, inFlags);
+
+ m_IsMouseDown = false;
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * @param inSize the size to be set.
+ */
+void CPlayhead::SetSize(CPt inSize)
+{
+ COverlayControl::SetSize(inSize);
+ // Update the line position.
+ m_LinePos = inSize.x / 2;
+}
+
+//=============================================================================
+/**
+ * Set the minimum and maximum allowed positions of the line.
+ * @param inMinimumPosition the minimum allowed position.
+ * @param inMaximumPosition the maximum allowed position.
+ */
+void CPlayhead::SetMinMaxPosition(long inMinimumPosition, long inMaximumPosition)
+{
+ m_MinimumPosition = inMinimumPosition;
+ m_MaximumPosition = inMaximumPosition;
+}
+
+long CPlayhead::GetCurrentTime()
+{
+ return m_Doc->GetCurrentViewTime();
+}
+
+//=============================================================================
+/**
+ * Check to see if inPoint is over this control or not.
+ * This overrides COverlayControl::HitTest so that the playhead line can be
+ * exempt from mouse hits.
+ * @param inPoint the location of the mouse in local coordinates.
+ */
+bool CPlayhead::HitTest(const CPt &inPoint) const
+{
+ if (inPoint.y < m_PlayheadImage.height()) {
+ return COverlayControl::HitTest(inPoint);
+ }
+ return false;
+}
+
+void CPlayhead::StepRightSmall()
+{
+ long theInterval = m_Timeline->GetTimeMeasure()->GetSmallHashInterval();
+
+ long theCurrentTime = GetCurrentTime();
+ long theTime = theCurrentTime / theInterval * theInterval + theInterval;
+ UpdateTime(theTime, true);
+}
+
+void CPlayhead::StepRightLarge()
+{
+ long theInterval = m_Timeline->GetTimeMeasure()->GetLargeHashInterval();
+
+ long theCurrentTime = GetCurrentTime();
+ long theTime = theCurrentTime / theInterval * theInterval + theInterval;
+ UpdateTime(theTime, true);
+}
+
+void CPlayhead::StepLeftSmall()
+{
+ long theInterval = m_Timeline->GetTimeMeasure()->GetSmallHashInterval();
+
+ long theCurrentTime = GetCurrentTime();
+ long theTime = theCurrentTime / theInterval * theInterval - theInterval;
+
+ UpdateTime(theTime, true);
+}
+
+void CPlayhead::StepLeftLarge()
+{
+ long theInterval = m_Timeline->GetTimeMeasure()->GetLargeHashInterval();
+
+ long theCurrentTime = GetCurrentTime();
+ long theTime = theCurrentTime / theInterval * theInterval - theInterval;
+
+ UpdateTime(theTime, true);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Playhead.h b/src/Authoring/Studio/Palettes/Timeline/Playhead.h
new file mode 100644
index 00000000..21d21041
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Playhead.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PLAYHEAD_H
+#define INCLUDED_PLAYHEAD_H 1
+
+#pragma once
+
+#include "OverlayControl.h"
+#include "Snapper.h"
+
+#include <QPixmap>
+
+class CTimelineTimelineLayout;
+class IDoc;
+
+class CPlayhead : public COverlayControl
+{
+public:
+ CPlayhead(CTimelineTimelineLayout *inTimeline, IDoc *inDoc);
+ virtual ~CPlayhead();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void SetSize(CPt inSize) override;
+ void Draw(CRenderer *inRenderer) override;
+ void SetMinMaxPosition(long inMinimumPosition, long inMaximumPosition);
+ long GetCurrentTime();
+ bool HitTest(const CPt &inPoint) const override;
+ void StepRightSmall();
+ void StepRightLarge();
+ void StepLeftSmall();
+ void StepLeftLarge();
+
+ long GetCenterOffset() const { return m_LinePos; }
+ bool IsMouseDown() const { return m_IsMouseDown; }
+
+ void UpdateTime(long inTime, bool inUpdateClient);
+
+protected:
+ QPixmap m_PlayheadImage;
+ bool m_IsMouseDown;
+ long m_InitialOffset;
+
+ long m_MinimumPosition;
+ long m_MaximumPosition;
+ long m_LinePos;
+
+ CTimelineTimelineLayout *m_Timeline;
+
+ CSnapper m_Snapper;
+ IDoc *m_Doc;
+};
+#endif // INCLUDED_PLAYHEAD_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.cpp
new file mode 100644
index 00000000..3cffd417
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "PropertyColorControl.h"
+#include "Renderer.h"
+#include "PropertyRow.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+
+CPropertyColorControl::CPropertyColorControl(CPropertyRow *inPropertyRow)
+{
+ m_ParentRow = inPropertyRow;
+}
+
+CPropertyColorControl::~CPropertyColorControl()
+{
+}
+
+//==============================================================================
+/**
+ * Draw
+ *
+ * draws this object
+ *
+ * @param inRenderer a renderer object
+ */
+void CPropertyColorControl::Draw(CRenderer *inRenderer)
+{
+ // Fill the control with the solid color
+ CRct theRect(GetSize());
+ CColor theBgColor(CStudioPreferences::GetPropertyBackgroundColor());
+ inRenderer->FillSolidRect(theRect, theBgColor);
+
+ // Define the colors for the side and bottom outline
+ CColor theBorderColor(CStudioPreferences::GetPropertyFloorColor());
+
+ // Right
+ inRenderer->PushPen(theBorderColor);
+ inRenderer->MoveTo(CPt(theRect.size.x - 1, 0));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+
+ // Left
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theRect.size.y - 1));
+
+ // Bottom
+ inRenderer->MoveTo(CPt(1, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x - 2, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//==============================================================================
+/**
+ * Handles the OnMouseDownEvent
+ */
+bool CPropertyColorControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ m_ParentRow->Select((CHotKeys::MODIFIER_SHIFT & inFlags) == CHotKeys::MODIFIER_SHIFT);
+ return true;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.h b/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.h
new file mode 100644
index 00000000..cc3263b8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyColorControl.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTYCOLOR_CONTROL_H
+#define INCLUDED_PROPERTYCOLOR_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+
+class CRenderer;
+class CPropertyRow;
+
+class CPropertyColorControl : public CControl
+{
+public:
+ CPropertyColorControl(CPropertyRow *inPropertyRow);
+ virtual ~CPropertyColorControl();
+
+ void SetColor(::CColor inColor);
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+ CPropertyRow *m_ParentRow;
+};
+#endif // INCLUDED_
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.cpp
new file mode 100644
index 00000000..fd7585ac
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "PropertyGraphKeyframe.h"
+#include "Renderer.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "StudioUtils.h"
+#include "CColor.h"
+
+CPropertyGraphKeyframe::CPropertyGraphKeyframe(ITimelineItemProperty *inProperty,
+ long inChannelIndex, long inKeyframeTime,
+ double inTimeRatio, long inMinY, long inMaxY,
+ float inMinVal, float inMaxVal)
+ : m_Property(inProperty)
+ , m_ChannelIndex(inChannelIndex)
+ , m_KeyframeTime(inKeyframeTime)
+ , m_TimeRatio(inTimeRatio)
+ , m_MinVal(inMinVal)
+ , m_MaxVal(inMaxVal)
+ , m_MinY(inMinY)
+ , m_MaxY(inMaxY)
+ , m_IsMouseDown(false)
+{
+ SetSize(CPt(4, 4));
+ PositionKeyframe();
+}
+
+CPropertyGraphKeyframe::~CPropertyGraphKeyframe()
+{
+}
+
+long CPropertyGraphKeyframe::GetTime()
+{
+ return m_KeyframeTime;
+}
+
+long CPropertyGraphKeyframe::GetChannelIndex() const
+{
+ return m_ChannelIndex;
+}
+
+void CPropertyGraphKeyframe::PositionKeyframe()
+{
+ long theXPos = ::TimeToPos(m_KeyframeTime, m_TimeRatio);
+ float theValue = GetKeyframeValue();
+ long theYPos = (long)((1.0 - (theValue - m_MinVal) / (m_MaxVal - m_MinVal)) * (m_MaxY - m_MinY)
+ + m_MinY - GetSize().y / 2 + .5);
+
+ if (theYPos < m_MinY)
+ theYPos = m_MinY;
+ else if (theYPos > m_MaxY)
+ theYPos = m_MaxY;
+
+ SetPosition(CPt(theXPos, theYPos));
+}
+
+void CPropertyGraphKeyframe::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ PositionKeyframe();
+}
+
+float CPropertyGraphKeyframe::GetKeyframeValue()
+{
+ return m_Property->GetChannelValueAtTime(m_ChannelIndex, m_KeyframeTime);
+}
+
+void CPropertyGraphKeyframe::SetKeyframeValue(float inValue)
+{
+ m_Property->SetChannelValueAtTime(m_ChannelIndex, m_KeyframeTime, inValue);
+}
+
+void CPropertyGraphKeyframe::Draw(CRenderer *inRenderer)
+{
+ CPt mySize = GetSize();
+ inRenderer->MoveTo(0, 0);
+ inRenderer->LineTo(mySize.x, 0);
+ inRenderer->LineTo(mySize.x, mySize.y);
+ inRenderer->LineTo(0, mySize.y);
+ inRenderer->LineTo(0, 0);
+
+ if (m_IsMouseDown) {
+ inRenderer->FillSolidRect(CRct(GetSize()), CColor::black);
+ }
+}
+
+bool CPropertyGraphKeyframe::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_IsMouseDown = true;
+ m_MouseDownLoc = inPoint;
+ }
+ return true;
+}
+
+void CPropertyGraphKeyframe::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_IsMouseDown = false;
+ m_Property->CommitChangedKeyframes();
+ GetParent()->Invalidate();
+}
+
+void CPropertyGraphKeyframe::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ if (m_IsMouseDown) {
+ long theDiff = inPoint.y - m_MouseDownLoc.y;
+ long theYPos = GetPosition().y + theDiff;
+ if (theYPos < m_MinY)
+ theYPos = m_MinY;
+ if (theYPos > m_MaxY)
+ theYPos = m_MaxY;
+ SetPosition(GetPosition().x, theYPos);
+
+ float theValue = (m_MaxVal - m_MinVal)
+ * (((m_MaxY + m_MinY) - theYPos - GetSize().y / 2) - m_MinY) / (m_MaxY - m_MinY)
+ + m_MinVal;
+ SetKeyframeValue(theValue);
+
+ GetParent()->Invalidate();
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.h
new file mode 100644
index 00000000..daffc8ab
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyGraphKeyframe.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_GRAPH_KEYFRAME_H
+#define INCLUDED_PROPERTY_GRAPH_KEYFRAME_H 1
+
+#pragma once
+
+#include "Control.h"
+
+class ITimelineItemProperty;
+
+class CPropertyGraphKeyframe : public CControl
+{
+public:
+ CPropertyGraphKeyframe(ITimelineItemProperty *inProperty, long inChannelIndex,
+ long inKeyframeTime, double inTimeRatio, long inHeight, long inOffset,
+ float inMinVal, float inMaxVal);
+ virtual ~CPropertyGraphKeyframe();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ long GetTime();
+ long GetChannelIndex() const;
+ void SetTimeRatio(double inTimeRatio);
+ void PositionKeyframe();
+
+protected:
+ float GetKeyframeValue();
+ void SetKeyframeValue(float inValue);
+
+ ITimelineItemProperty *m_Property;
+ long m_ChannelIndex; // index that identifies the channel for a animated property
+ long m_KeyframeTime;
+ double m_TimeRatio;
+ float m_MinVal;
+ float m_MaxVal;
+ long m_MinY;
+ long m_MaxY;
+ CPt m_MouseDownLoc;
+ bool m_IsMouseDown;
+};
+#endif // INCLUDED_PROPERTY_GRAPH_KEYFRAME_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyRow.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyRow.cpp
new file mode 100644
index 00000000..986d8326
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyRow.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PropertyRow.h"
+#include "TimelineControl.h"
+#include "PropertyColorControl.h"
+#include "PropertyToggleControl.h"
+#include "PropertyTreeControl.h"
+#include "PropertyTimebarRow.h"
+#include "BlankControl.h"
+#include "StudioPreferences.h"
+#include "StateRow.h"
+#include "Bindings/ITimelineItemProperty.h"
+
+//=============================================================================
+/**
+ * Create a property row for inProperty.
+ * @param inProperty the property that this is displaying.
+ */
+CPropertyRow::CPropertyRow(ITimelineItemProperty *inProperty)
+ : m_Highlighted(false)
+ , m_DetailedView(false)
+ , m_TimeRatio(0.0f)
+ , m_Property(inProperty)
+{
+ m_IsViewable = true;
+
+ m_TreeControl = new CPropertyTreeControl(this);
+ m_ToggleControl = new CPropertyToggleControl(this);
+ m_TimebarRow = new CPropertyTimebarRow(this);
+
+ m_PropertyColorControl = new CPropertyColorControl(this);
+ long theTimebarHeight = CStudioPreferences::GetRowSize();
+
+ // do not set absolute size because it messes up the timeline
+ m_TreeControl->SetSize(CPt(500, theTimebarHeight));
+ m_PropertyColorControl->SetAbsoluteSize(CPt(theTimebarHeight, theTimebarHeight));
+ m_ToggleControl->SetAbsoluteSize(CPt(57, theTimebarHeight));
+ m_TimebarRow->SetSize(CPt(0, theTimebarHeight));
+
+ // sk: setting controls names' seem to be only useful for debugging.
+ // Q3DStudio::CString thePropName( inProperty->GetName( ) );
+ // m_TreeControl->SetName( thePropName );
+ // m_TimebarRow->SetName( thePropName + "TimebarRow" );
+}
+
+CPropertyRow::~CPropertyRow()
+{
+ delete m_TreeControl;
+ delete m_ToggleControl;
+ delete m_PropertyColorControl;
+ delete m_TimebarRow;
+}
+
+//=============================================================================
+/**
+ * Get the left hand color control for this row.
+ * @return the color control for this row.
+ */
+CControl *CPropertyRow::GetColorControl()
+{
+ return m_PropertyColorControl;
+}
+
+//=============================================================================
+/**
+ * Get the tree control object for this row.
+ * @return the tree control object for this row.
+ */
+CControl *CPropertyRow::GetTreeControl()
+{
+ return m_TreeControl;
+}
+
+//=============================================================================
+/**
+ * Get the toggle control object for this row.
+ * @return the toggle control object for this row.
+ */
+CControl *CPropertyRow::GetToggleControl()
+{
+ return m_ToggleControl;
+}
+
+//=============================================================================
+/**
+ * Get the timebar control for this row.
+ * @return the timebar control for this row.
+ */
+CControl *CPropertyRow::GetTimebarControl()
+{
+ return m_TimebarRow;
+}
+
+//=============================================================================
+/**
+ * Set the amount that this row is indented.
+ * This handles the tree indenting.
+ * @param inIndent the amount of indent for this row.
+ */
+void CPropertyRow::SetIndent(long inIndent)
+{
+ m_TreeControl->SetIndent(inIndent);
+}
+
+//=============================================================================
+/**
+ * Notification from one of the child controls that the mouse just entered.
+ * This is used to handle the highlighting of the entire row.
+ */
+void CPropertyRow::OnMouseOver()
+{
+ // Only change if change is needed
+ if (!m_Highlighted) {
+ m_TreeControl->SetHighlighted(true);
+ m_ToggleControl->SetHighlighted(true);
+ m_TimebarRow->SetHighlighted(true);
+
+ m_Highlighted = true;
+ }
+}
+
+//=============================================================================
+/**
+ * Notification from one of the child controls that the mouse just left.
+ * This is used to handle the highlighting of the entire row.
+ */
+void CPropertyRow::OnMouseOut()
+{
+ // Only change is change is needed.
+ if (m_Highlighted) {
+ m_TreeControl->SetHighlighted(false);
+ m_ToggleControl->SetHighlighted(false);
+ m_TimebarRow->SetHighlighted(false);
+
+ m_Highlighted = false;
+ }
+}
+
+//=============================================================================
+/**
+ * Double click handler for the property row
+ */
+void CPropertyRow::OnMouseDoubleClick()
+{
+ SetDetailedView(!m_DetailedView);
+}
+
+//=============================================================================
+/**
+ * Expands the row out so a more detailed view of the animation tracks can be seen
+ *
+ * @param inIsInDetailedView true if we are switching to a detailed view
+ */
+void CPropertyRow::SetDetailedView(bool inIsDetailedView)
+{
+ m_DetailedView = inIsDetailedView;
+
+ if (m_DetailedView) {
+ long theHeight = 100;
+ m_PropertyColorControl->SetAbsoluteSize(
+ CPt(m_PropertyColorControl->GetSize().x, theHeight));
+ m_TreeControl->SetSize(CPt(m_TreeControl->GetSize().x, theHeight));
+ m_ToggleControl->SetAbsoluteSize(CPt(m_ToggleControl->GetSize().x, theHeight));
+ m_TimebarRow->SetAbsoluteSize(CPt(m_TimebarRow->GetSize().x, theHeight));
+
+ m_TimebarRow->SetDetailedView(true);
+ } else {
+ long theDefaultHeight = CStudioPreferences::GetRowSize();
+
+ m_PropertyColorControl->SetAbsoluteSize(
+ CPt(m_PropertyColorControl->GetSize().x, theDefaultHeight));
+ m_TreeControl->SetSize(CPt(m_TreeControl->GetSize().x, theDefaultHeight));
+ m_ToggleControl->SetAbsoluteSize(CPt(m_ToggleControl->GetSize().x, theDefaultHeight));
+ m_TimebarRow->SetAbsoluteSize(CPt(m_TimebarRow->GetSize().x, theDefaultHeight));
+
+ m_TimebarRow->SetDetailedView(false);
+ }
+
+ GetTopControl()->OnLayoutChanged();
+}
+
+//=============================================================================
+/**
+ * Filter this property.
+ * This controls whether or not this row should be displayed. The filter will
+ * be stored and used for future operations that may change whether or not
+ * this should be visible.
+ * @param inFilter the filter to filter on.
+ * @param inFilterChildren true if the call is recursive.
+ */
+void CPropertyRow::Filter(const CFilter &inFilter, bool inFilterChildren /*= true*/)
+{
+ Q_UNUSED(inFilterChildren);
+
+ m_Filter = inFilter;
+
+ bool theVisibleFlag = inFilter.Filter(m_Property);
+ m_TreeControl->SetVisible(theVisibleFlag);
+ m_TimebarRow->SetVisible(theVisibleFlag);
+ m_ToggleControl->SetVisible(theVisibleFlag);
+ m_PropertyColorControl->SetVisible(theVisibleFlag);
+ m_IsViewable = theVisibleFlag;
+}
+
+//=============================================================================
+/**
+ * @return true if this property is animated and is not currently being filtered out
+ */
+bool CPropertyRow::IsViewable() const
+{
+ return m_Filter.GetProperties();
+}
+
+//=============================================================================
+/**
+ * Call from one of the child controls to select this object.
+ * Currently this just causes the Asset to be selected.
+ */
+void CPropertyRow::Select(bool inIsShiftKeyPressed /*= false */)
+{
+ m_Property->SetSelected();
+
+ if (inIsShiftKeyPressed)
+ m_TimebarRow->SelectAllKeys();
+ else
+ m_Property->ClearKeySelection();
+}
+
+ISnappingListProvider *CPropertyRow::GetSnappingListProvider() const
+{
+ return &m_TimebarRow->GetSnappingListProvider();
+}
+
+void CPropertyRow::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ m_TimebarRow->SetSnappingListProvider(inProvider);
+}
+
+//=============================================================================
+/**
+ * Retrieves the background color for the row based upon the type of asset
+ * passed in. Overridden so that properties can have a different background
+ * color than their parent asset does.
+ * @return background color to use for this row
+ */
+::CColor CPropertyRow::GetTimebarBackgroundColor()
+{
+ return CStudioPreferences::GetPropertyBackgroundColor();
+}
+
+//=============================================================================
+/**
+ * Retrieves the background color for the row when the mouse is over the row
+ * based upon the type of asset passed in. Overridden so that properties can
+ * have a different highlight background color than their parent asset does.
+ * @return background color to use for this row when the mouse is over it
+ */
+::CColor CPropertyRow::GetTimebarHighlightBackgroundColor()
+{
+ return CStudioPreferences::GetPropertyMouseOverColor();
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is represented by a pixel.
+ * This modifies the length of this control.
+ * @param inTimePerPixel the time per pixel.
+ */
+void CPropertyRow::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ m_TimebarRow->SetTimeRatio(inTimeRatio);
+}
+
+//=============================================================================
+/**
+ * Selects all keyframes of this property that are inside inRect
+ *
+ * @param inRect theRect to check
+ */
+void CPropertyRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown)
+{
+ CRct theOffsetRect = inRect;
+ CRct myRect(CPt(0, 0), m_TimebarRow->GetSize());
+ theOffsetRect.Offset(-m_TimebarRow->GetPosition());
+ m_TimebarRow->SelectKeysInRect(theOffsetRect, inModifierKeyDown);
+}
+
+//=============================================================================
+/**
+ * CommitSelections: commits all the property keyframe selections by setting their
+ * previous selection state to the current selection state.
+ * This will prevent the keyframes in the current selection
+ *from
+ * switching states as we select other keyframes.
+ *
+ * @param NONE
+ * @return NONE
+ */
+void CPropertyRow::CommitSelections()
+{
+ m_TimebarRow->CommitSelections();
+}
+
+//=============================================================================
+/**
+ * Selects all keyframes
+ */
+void CPropertyRow::SelectAllKeys()
+{
+ m_TimebarRow->SelectAllKeys();
+}
+
+//=============================================================================
+/**
+ * Deletes keyframes on this property row
+ */
+void CPropertyRow::DeleteAllKeys()
+{
+ m_Property->DeleteAllKeys();
+ m_TimebarRow->Invalidate();
+}
+
+//=============================================================================
+/**
+ * Sets all the child control enable states
+ * @param inEnabled the state to set the controls to
+ */
+void CPropertyRow::SetEnabled(bool inEnabled)
+{
+ m_TreeControl->SetEnabled(inEnabled);
+ m_ToggleControl->SetEnabled(inEnabled);
+ m_PropertyColorControl->SetEnabled(inEnabled);
+ m_TimebarRow->SetEnabled(inEnabled);
+}
+
+//=============================================================================
+/**
+ * Callback from the ITimelineProperty.
+ */
+void CPropertyRow::Refresh()
+{
+ if (m_TimebarRow->IsVisible())
+ m_TimebarRow->SetDirty(true);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyRow.h b/src/Authoring/Studio/Palettes/Timeline/PropertyRow.h
new file mode 100644
index 00000000..49ca60a7
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyRow.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_ROW_H
+#define INCLUDED_PROPERTY_ROW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TimelineRow.h"
+#include "StateRow.h"
+#include "Rct.h"
+#include <vector>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CPropertyColorControl;
+class CBlankControl;
+class CPropertyTreeControl;
+class CPropertyToggleControl;
+class CPropertyTimebarRow;
+class ITimelineItemProperty;
+
+class CPropertyRow : public CTimelineRow
+{
+public:
+ CPropertyRow(ITimelineItemProperty *inProperty);
+ virtual ~CPropertyRow();
+ CControl *GetColorControl() override;
+ CControl *GetTreeControl() override;
+ CControl *GetToggleControl() override;
+ CControl *GetTimebarControl() override;
+
+ void SetTimeRatio(double inTimePerPixel) override;
+ void SetIndent(long inIndent) override;
+ void Filter(const CFilter &inFilter, bool inFilterChildren = true) override;
+ void OnMouseOver();
+ void OnMouseOut();
+ void OnMouseDoubleClick();
+ void Select(bool inIsShiftKeyPressed = false);
+ void SetDetailedView(bool inIsInDetailedView);
+ void CommitSelections();
+ void SelectKeysInRect(CRct inRect, bool inModifierKeyDown);
+ void SelectAllKeys();
+ void DeleteAllKeys();
+ bool IsViewable() const override;
+ void SetEnabled(bool inEnabled);
+
+ ISnappingListProvider *GetSnappingListProvider() const override;
+ void SetSnappingListProvider(ISnappingListProvider *inProvider) override;
+ using CTimelineRow::GetTimebarBackgroundColor;
+ virtual ::CColor GetTimebarBackgroundColor();
+ using CTimelineRow::GetTimebarHighlightBackgroundColor;
+ virtual ::CColor GetTimebarHighlightBackgroundColor();
+
+ void Refresh();
+ ITimelineItemProperty *GetProperty() const { return m_Property; }
+
+ CPropertyTimebarRow *GetTimebar() { return m_TimebarRow; }
+
+protected:
+ CPropertyTreeControl *m_TreeControl;
+ CPropertyToggleControl *m_ToggleControl;
+ CPropertyColorControl *m_PropertyColorControl;
+ CPropertyTimebarRow *m_TimebarRow;
+ bool m_Highlighted;
+ bool m_DetailedView;
+ double m_TimeRatio;
+ CFilter m_Filter;
+ ITimelineItemProperty *m_Property;
+};
+#endif // INCLUDED_PROPERTY_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.cpp
new file mode 100644
index 00000000..ecd3b5df
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "PropertyTimebarGraph.h"
+#include "IKeyframe.h"
+#include "Renderer.h"
+#include "PropertyGraphKeyframe.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "StudioUtils.h"
+
+//=============================================================================
+/**
+ * Create a graph for the specified property.
+ * @param inProperty the property this is graphing.
+ */
+CPropertyTimebarGraph::CPropertyTimebarGraph(ITimelineItemProperty *inProperty)
+ : m_Property(inProperty)
+ , m_TimeRatio(0.0f)
+ , m_MaxVal(0.0f)
+ , m_MinVal(0.0f)
+ , m_MinY(0)
+ , m_MaxY(0)
+{
+}
+
+CPropertyTimebarGraph::~CPropertyTimebarGraph()
+{
+ TKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos)
+ delete (*thePos);
+}
+
+//=============================================================================
+/**
+ * Toggle whether this is visible or not.
+ * Overrides CControl::SetVisible. If this is not visible then it removes
+ * a bunch of the refresh logic to speed stuff up.
+ * @param inIsVisible true if this control is to be visible.
+ */
+void CPropertyTimebarGraph::SetVisible(bool inIsVisible)
+{
+ CControl::SetVisible(inIsVisible);
+
+ // If this is visible then add the appropriate keyframes and listeners.
+ if (inIsVisible) {
+ m_MaxVal = m_Property->GetMaximumValue();
+ m_MinVal = m_Property->GetMinimumValue();
+
+ m_MinY = 10;
+ m_MaxY = GetSize().y - m_MinY;
+
+ RefreshKeyframes();
+ } else
+ RemoveKeyframes();
+}
+
+//=============================================================================
+/**
+ * Set the time ratio for this display.
+ * The time ratio controls how much time is displayed in how much space.
+ * @param inTimeRatio the time ratio.
+ */
+void CPropertyTimebarGraph::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+}
+
+//=============================================================================
+/**
+ * Draw this to the specified renderer.
+ * This will perform the graphing of all the channels on the property.
+ * @param inRenderer the renderer to draw to.
+ */
+void CPropertyTimebarGraph::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(CPt(0, 0), GetSize());
+ inRenderer->PushClippingRect(theRect);
+
+ // the available line colors, tried to use Rainbow.bvs code to do it dynamically but didn't
+ // quite work.
+ ::CColor theColors[6] = { ::CColor(255, 0, 0), ::CColor(0, 255, 0), ::CColor(0, 0, 255),
+ ::CColor(255, 255, 0), ::CColor(255, 0, 255), ::CColor(0, 255, 255) };
+
+ long theChannelCount = m_Property->GetChannelCount();
+ // Don't want to overflow the color array
+ if (theChannelCount <= 6) {
+ // For each channel graph it.
+ for (long theIndex = 0; theIndex < theChannelCount; ++theIndex)
+ DrawDetailedChannel(inRenderer, theIndex, theColors[theIndex]);
+ }
+
+ inRenderer->PopClippingRect();
+}
+
+void CPropertyTimebarGraph::DrawDetailedChannel(CRenderer *inRenderer, long inChannelIndex,
+ ::CColor inColor)
+{
+ inRenderer->PushPen(inColor, 2);
+
+ CRct theClipRect = inRenderer->GetClippingRect();
+ float theValue = m_Property->GetChannelValueAtTime(inChannelIndex, 0);
+ long theYPos = (long)((1.0 - (theValue - m_MinVal) / (m_MaxVal - m_MinVal)) * (m_MaxY - m_MinY)
+ + m_MinY + .5);
+
+ inRenderer->MoveTo(CPt(0, theYPos));
+
+ long theInterval = 5;
+ long theSize = theClipRect.position.x + theClipRect.size.x + theInterval;
+
+ for (long thePixel = theClipRect.position.x; thePixel < theSize; thePixel += theInterval) {
+ long theTime = ::PosToTime(thePixel, m_TimeRatio); //(long)( thePixel / m_TimeRatio + .5 );
+ theValue = m_Property->GetChannelValueAtTime(inChannelIndex, theTime);
+ theYPos = (long)((1.0 - (theValue - m_MinVal) / (m_MaxVal - m_MinVal)) * (m_MaxY - m_MinY)
+ + m_MinY + .5);
+
+ inRenderer->LineTo(CPt(thePixel, theYPos));
+ }
+
+ inRenderer->PopPen();
+}
+
+void CPropertyTimebarGraph::AddKeyframes()
+{
+ long theChannelCount = m_Property->GetChannelCount();
+ long theKeyframeCount =
+ m_Property->GetKeyframeCount(); // the way it works now (and hence the assumption is), the
+ // number of keyframes for all the channels is the same.
+ for (long theIndex = 0; theIndex < theChannelCount; ++theIndex) {
+ for (long theKeyIndex = 0; theKeyIndex < theKeyframeCount; ++theKeyIndex) {
+ CPropertyGraphKeyframe *theGraphKeyframe = new CPropertyGraphKeyframe(
+ m_Property, theIndex, m_Property->GetKeyframeByIndex(theKeyIndex)->GetTime(),
+ m_TimeRatio, m_MinY, m_MaxY, m_MinVal, m_MaxVal);
+ AddChild(theGraphKeyframe);
+ m_Keyframes.push_back(theGraphKeyframe);
+ }
+ }
+}
+
+void CPropertyTimebarGraph::Invalidate(bool inIsInvalidated)
+{
+ CControl::Invalidate(inIsInvalidated);
+
+ if (inIsInvalidated && GetParent() != nullptr) {
+ GetParent()->Invalidate(inIsInvalidated);
+ }
+}
+
+void CPropertyTimebarGraph::RemoveKeyframes()
+{
+ TKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ CPropertyGraphKeyframe *theKeyframe = (*thePos);
+ RemoveChild(theKeyframe);
+ delete theKeyframe;
+ }
+ m_Keyframes.clear();
+}
+
+void CPropertyTimebarGraph::RefreshKeyframes()
+{
+ TKeyframeList::iterator thePos = m_Keyframes.begin();
+ while (thePos != m_Keyframes.end()) {
+ CPropertyGraphKeyframe *theKeyframe = (*thePos);
+ if (m_Property->GetKeyframeByTime(theKeyframe->GetTime())) {
+ theKeyframe->PositionKeyframe();
+ ++thePos;
+ } else {
+ RemoveChild(theKeyframe);
+ delete theKeyframe;
+ thePos = m_Keyframes.erase(thePos);
+ }
+ }
+ long theChannelCount = m_Property->GetChannelCount();
+ long theKeyframeCount =
+ m_Property->GetKeyframeCount(); // the way it works now (and hence the assumption is), the
+ // number of keyframes for all the channels is the same.
+ for (long theIndex = 0; theIndex < theChannelCount; ++theIndex) {
+ for (long theKeyIndex = 0; theKeyIndex < theKeyframeCount; ++theKeyIndex) {
+ IKeyframe *theKeyframe = m_Property->GetKeyframeByIndex(theKeyIndex);
+ CPropertyGraphKeyframe *theExistingKeyframe =
+ GetKeyframe(theKeyframe->GetTime(), theIndex);
+ if (!theExistingKeyframe) {
+ CPropertyGraphKeyframe *theGraphKeyframe =
+ new CPropertyGraphKeyframe(m_Property, theIndex, theKeyframe->GetTime(),
+ m_TimeRatio, m_MinY, m_MaxY, m_MinVal, m_MaxVal);
+ AddChild(theGraphKeyframe);
+
+ m_Keyframes.push_back(theGraphKeyframe);
+ }
+ }
+ }
+
+ Invalidate();
+}
+
+CPropertyGraphKeyframe *CPropertyTimebarGraph::GetKeyframe(long inTime, long inChannelIndex)
+{
+ TKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ CPropertyGraphKeyframe *theKeyframe = (*thePos);
+ if (theKeyframe->GetChannelIndex() == inChannelIndex && theKeyframe->GetTime() == inTime) {
+ return theKeyframe;
+ }
+ }
+ return nullptr;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.h b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.h
new file mode 100644
index 00000000..76379bcd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarGraph.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_TIMEBAR_GRAPH_H
+#define INCLUDED_PROPERTY_TIMEBAR_GRAPH_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+
+class CPropertyGraphKeyframe;
+class ITimelineItemProperty;
+
+class CPropertyTimebarGraph : public CControl
+{
+ typedef std::vector<CPropertyGraphKeyframe *> TKeyframeList;
+
+public:
+ CPropertyTimebarGraph(ITimelineItemProperty *inProperty);
+ virtual ~CPropertyTimebarGraph();
+ void SetVisible(bool inIsVisible) override;
+ void Draw(CRenderer *inRenderer) override;
+ void SetTimeRatio(double inTimeRatio);
+ void Invalidate(bool inIsInvalidated = true) override;
+ void RefreshKeyframes();
+
+protected:
+ void DrawDetailedChannel(CRenderer *inRenderer, long inChannelIndex, ::CColor inColor);
+ void AddKeyframes();
+ void RemoveKeyframes();
+
+ CPropertyGraphKeyframe *GetKeyframe(long inTime, long inChannelIndex);
+
+protected:
+ ITimelineItemProperty *m_Property;
+ double m_TimeRatio;
+ TKeyframeList m_Keyframes;
+ float m_MaxVal;
+ float m_MinVal;
+ long m_MinY;
+ long m_MaxY;
+};
+#endif // INCLUDED_PROPERTY_TIMEBAR_GRAPH_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.cpp
new file mode 100644
index 00000000..428777a8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.cpp
@@ -0,0 +1,508 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PropertyTimebarRow.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+#include "PropertyRow.h"
+#include "MasterP.h"
+#include "KeyframeContextMenu.h"
+#include "PropertyTimelineKeyframe.h"
+#include "HotKeys.h"
+#include "MultiSelectAspect.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "IKeyframe.h"
+#include "CoreUtils.h"
+
+//=============================================================================
+/**
+ */
+CPropertyTimebarRow::CPropertyTimebarRow(CPropertyRow *inPropertyRow)
+ : m_PropertyRow(inPropertyRow)
+ , m_DetailedView(inPropertyRow->GetProperty())
+ , m_DirtyFlag(false)
+ , m_Refreshing(false)
+ , m_SnappingListProvider(nullptr)
+{
+ m_DetailedView.SetVisible(false);
+
+ m_BackgroundColor = m_PropertyRow->GetTimebarBackgroundColor();
+
+ AddChild(&m_DetailedView);
+ m_Refreshing = false;
+ RefreshKeyframes();
+}
+
+CPropertyTimebarRow::~CPropertyTimebarRow()
+{
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ CPropertyTimelineKeyframe *theDeletedKey = (*thePos);
+ RemoveChild(theDeletedKey);
+ delete theDeletedKey;
+ }
+}
+
+//=============================================================================
+/**
+ * Draws the this row background.
+ * @param inRenderer the renderer to draw to.
+ */
+void CPropertyTimebarRow::Draw(CRenderer *inRenderer)
+{
+ if (m_DirtyFlag) {
+ RefreshKeyframes();
+ m_DirtyFlag = false;
+ }
+
+ CRct theRect(GetSize());
+ UICDM::TDataTypePair theType = m_PropertyRow->GetProperty()->GetType();
+ if (theType.first == UICDM::DataModelDataType::Float3
+ && theType.second == UICDM::AdditionalMetaDataType::Color) {
+ inRenderer->FillSolidRect(CRct(0, 0, theRect.size.x, theRect.size.y - 1),
+ m_BackgroundColor);
+ DrawColor(inRenderer);
+ } else {
+ inRenderer->FillSolidRect(CRct(0, 0, theRect.size.x, theRect.size.y - 1),
+ m_BackgroundColor);
+ }
+
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Draw a colorized background for this property row.
+ * If this is a color object then this will draw the background of this control
+ * as the color that the property is. This will draw a gradient between the
+ * colors over time to show somewhat what the colors look like.
+ * @param inRenderer the renderer to draw to.
+ */
+void CPropertyTimebarRow::DrawColor(CRenderer *inRenderer)
+{
+ UICPROFILE(DrawColor);
+
+ ITimelineItemProperty *theProperty = m_PropertyRow->GetProperty();
+ ASSERT(theProperty->GetChannelCount() == 3); // sanity check
+
+ CColor thePreviousColor;
+ thePreviousColor.SetRed(::dtol(theProperty->GetChannelValueAtTime(0, 0)));
+ thePreviousColor.SetGreen(::dtol(theProperty->GetChannelValueAtTime(1, 0)));
+ thePreviousColor.SetBlue(::dtol(theProperty->GetChannelValueAtTime(2, 0)));
+
+ long thePreviousTime = 0;
+ long theLastPosition = 0;
+ CColor theCurrentColor;
+
+ long theKeyframeCount = theProperty->GetKeyframeCount();
+
+ // Go through all the keyframes and draw a gradient from the previous key to the current key.
+ // Only use the first channel for the keyframes, assume they are all at the same time.
+ for (long theIndex = 0; theIndex < theKeyframeCount; ++theIndex) {
+ long theCurrentTime = theProperty->GetKeyframeByIndex(theIndex)->GetTime();
+ // Get the color at the specified time.
+ theCurrentColor.SetRed(::dtol(theProperty->GetChannelValueAtTime(0, theCurrentTime)));
+ theCurrentColor.SetGreen(::dtol(theProperty->GetChannelValueAtTime(1, theCurrentTime)));
+ theCurrentColor.SetBlue(::dtol(theProperty->GetChannelValueAtTime(2, theCurrentTime)));
+
+ long thePreviousPixel = ::TimeToPos(thePreviousTime, m_TimeRatio);
+ long theCurrentPixel = ::TimeToPos(theCurrentTime, m_TimeRatio);
+
+ // Draw a gradient from the previous keyframe position to the current one.
+ inRenderer->DrawGradient(
+ CRct(thePreviousPixel, 0, theCurrentPixel - thePreviousPixel, GetSize().y),
+ thePreviousColor, theCurrentColor);
+ thePreviousTime = theCurrentTime;
+ thePreviousColor = theCurrentColor;
+ theLastPosition = theCurrentPixel;
+ }
+ // Fill in from the last keyframe to the end of the bar.
+ inRenderer->DrawGradient(CRct(theLastPosition, 0, GetSize().x - theLastPosition, GetSize().y),
+ thePreviousColor, thePreviousColor);
+}
+
+//=============================================================================
+/**
+ * OnMouseOver event, handles the highlighting of the row.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+void CPropertyTimebarRow::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ m_PropertyRow->OnMouseOver();
+}
+
+//=============================================================================
+/**
+ * OnMouseOut event, handles the de-highlighting of this row.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+void CPropertyTimebarRow::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ m_PropertyRow->OnMouseOut();
+}
+
+//=============================================================================
+/**
+ * OnMouseRDown event, pops up a context menu
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the modifier key states (shift, alt, and ctrl).
+ */
+bool CPropertyTimebarRow::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Only do it if a child has not handled the mouse down.
+ bool theRetVal = CControl::OnMouseRDown(inPoint, inFlags);
+ if (!theRetVal) {
+ m_PropertyRow->Select();
+ CKeyframeContextMenu theMenu(m_PropertyRow->GetProperty()->GetKeyframesManager(),
+ m_PropertyRow->GetProperty());
+ DoPopup(&theMenu, inPoint);
+ theRetVal = true;
+ }
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * OnMouseOut event, handles the de-highlighting of this row.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+void CPropertyTimebarRow::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ UICPROFILE(OnMouseMove);
+ CControl::OnMouseMove(inPoint, inFlags);
+}
+//=============================================================================
+/**
+ * Set this control to being highlighted or not.
+ * @param inIsHighlighted true if this is to be highlighted.
+ */
+void CPropertyTimebarRow::SetHighlighted(bool inIsHighlighted)
+{
+ if (inIsHighlighted)
+ m_BackgroundColor = m_PropertyRow->GetTimebarHighlightBackgroundColor();
+ else
+ m_BackgroundColor = m_PropertyRow->GetTimebarBackgroundColor();
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Get the state row that this belongs to.
+ */
+CPropertyRow *CPropertyTimebarRow::GetPropertyRow()
+{
+ return m_PropertyRow;
+}
+
+//=============================================================================
+/**
+ * Sets teh time ratio
+ *
+ * @param inTimeRatio the new ratio
+ */
+void CPropertyTimebarRow::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ (*thePos)->SetTimeRatio(inTimeRatio);
+ }
+ m_DetailedView.SetTimeRatio(inTimeRatio);
+
+ Invalidate();
+}
+
+void CPropertyTimebarRow::SetDetailedView(bool inDetailedView)
+{
+ m_DetailedView.SetVisible(inDetailedView);
+}
+
+//=============================================================================
+/**
+ * Sets teh size of this control.
+ * @param inSize size to set the row
+ */
+void CPropertyTimebarRow::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+
+ m_DetailedView.SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ * called when keyframes need to be updated, this funciton has two loops:
+ * the first loops through and deletes any keys no longer in the sskf list. the
+ * second adds any keys in the sskf list that are not already in the list
+ *
+ */
+void CPropertyTimebarRow::RefreshKeyframes()
+{
+ m_Refreshing = true;
+ UICPROFILE(RefreshKeyframes);
+
+ // First Loop clears any keys that do not correlate to a supersetkey
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ while (thePos != m_Keyframes.end()) {
+ CPropertyTimelineKeyframe *theTimelineKey = (*thePos);
+ CPt theSize = theTimelineKey->GetSize();
+ IKeyframe *theTempKey = nullptr;
+ theTempKey = m_PropertyRow->GetProperty()->GetKeyframeByTime(theTimelineKey->GetTime());
+
+ // If we find a key at this time, then the timeline key doesn't need to be deleted
+ if (!theTempKey) {
+ RemoveChild(theTimelineKey);
+ delete theTimelineKey;
+ thePos = m_Keyframes.erase(thePos);
+ } else if (theTempKey->IsDynamic() != theTimelineKey->IsDynamic()) {
+ theTimelineKey->SetDynamic(theTempKey->IsDynamic());
+ } else {
+ // Set the position
+ theTimelineKey->SetPosition(
+ ::TimeToPos(theTempKey->GetTime(), m_TimeRatio) - (theSize.x / 2), 0);
+ ++thePos;
+ }
+ }
+
+ // Second Loop adds teh remaining keys
+ long theKeyframeCount = m_PropertyRow->GetProperty()->GetKeyframeCount();
+ for (long theKey = 0; theKey < theKeyframeCount; ++theKey) {
+ bool theFoundFlag = false;
+ IKeyframe *theTempKey = m_PropertyRow->GetProperty()->GetKeyframeByIndex(theKey);
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+
+ long theKeyframeTime = theTempKey->GetTime();
+ // each key needs to be compared to all the keys in the sskf list to see if it has to be
+ // added.
+ for (; thePos != m_Keyframes.end() && !theFoundFlag; ++thePos) {
+ CPropertyTimelineKeyframe *theCurrentKey = (*thePos);
+ if (theCurrentKey->GetTime() == theKeyframeTime) {
+ theFoundFlag = true;
+ }
+ }
+ if (!theFoundFlag) {
+ // If we don't have a timeline key, then we have to make a new one
+ CPropertyTimelineKeyframe *thePropertyTimelineKey =
+ new CPropertyTimelineKeyframe(this, m_TimeRatio);
+ thePropertyTimelineKey->SetTime(theKeyframeTime);
+ thePropertyTimelineKey->Select(theTempKey->IsSelected());
+ thePropertyTimelineKey->SetDynamic(theTempKey->IsDynamic());
+ thePropertyTimelineKey->SetSize(CPt(15, 16));
+ CPt theSize = thePropertyTimelineKey->GetSize();
+ long theXPosition = ::TimeToPos(theKeyframeTime, m_TimeRatio) - (theSize.x / 2);
+ thePropertyTimelineKey->SetPosition(theXPosition, 0);
+ AddChild(thePropertyTimelineKey);
+ m_Keyframes.push_back(thePropertyTimelineKey);
+ }
+ }
+
+ if (m_DetailedView.IsVisible())
+ m_DetailedView.RefreshKeyframes();
+
+ m_Refreshing = false;
+}
+
+void CPropertyTimebarRow::Invalidate(bool inInvalidate)
+{
+ if (!m_Refreshing) {
+ CControl::Invalidate(inInvalidate);
+ }
+}
+
+//=============================================================================
+/**
+ * Handler for when a child key is selected
+ *
+ * @param inTime time of the key
+ */
+void CPropertyTimebarRow::OnKeySelected(long inTime, bool inSelected)
+{
+ if (inTime >= 0) {
+ m_PropertyRow->GetProperty()->SelectKeyframes(inSelected, inTime);
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Deselects all keyframes
+ */
+void CPropertyTimebarRow::DeselectAllKeyframes()
+{
+ m_PropertyRow->GetProperty()->SelectKeyframes(false);
+}
+
+//=============================================================================
+/**
+ * called when a child changes and the keyframes need to be refreshed
+ *
+ * @param inDirtyFlag true if this object is now dirty
+ *
+ */
+void CPropertyTimebarRow::SetDirty(bool inDirtyFlag)
+{
+ if (m_DirtyFlag == inDirtyFlag)
+ return;
+
+ m_DirtyFlag = inDirtyFlag;
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * SelectKeysInRect: selects any keyframes inside the rect
+ *
+ * @param inRect is used to select keyframes
+ * @param inModifierKeyDown indicates if the control modifier is down
+ */
+void CPropertyTimebarRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown)
+{
+ CMultiSelectAspect<TTimelineKeyframeList> theMultiSelectAspect(m_Keyframes,
+ m_PropertyRow->GetProperty());
+ theMultiSelectAspect.MultiSelect(inRect, inModifierKeyDown);
+}
+
+//=============================================================================
+/**
+ * CommitSelections: commits all the master keyframe selections by setting their
+ * previous selection state to the current selection state.
+ * This will prevent the keyframes in the current selection
+ *from
+ * switching states as we select other keyframes.
+ *
+ * @param NONE
+ * @return NONE
+ */
+
+void CPropertyTimebarRow::CommitSelections()
+{
+ CMultiSelectAspect<TTimelineKeyframeList> theMultiSelectAspect(m_Keyframes,
+ m_PropertyRow->GetProperty());
+ theMultiSelectAspect.CommitSelections();
+}
+
+//=============================================================================
+/**
+ * true if there are selected keys on this object
+ */
+bool CPropertyTimebarRow::HasSelectedKeys()
+{
+ bool theRetVal = false;
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end() && !theRetVal; ++thePos) {
+ if ((*thePos)->IsSelected()) {
+ theRetVal = true;
+ }
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * selects all keys for this timebar row
+ */
+void CPropertyTimebarRow::SelectAllKeys()
+{
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ m_PropertyRow->GetProperty()->SelectKeyframes(true, (*thePos)->GetTime());
+ (*thePos)->Select(true);
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Set this control as being visible or not.
+ * If the control is not visible then it will not be drawn and will not
+ * get mouse clicks.
+ * @param inIsVisible true if this control is to be visible.
+ */
+void CPropertyTimebarRow::SetVisible(bool inIsVisible)
+{
+ if (inIsVisible != IsVisible()) {
+ CControl::SetVisible(inIsVisible);
+ SetDirty(true);
+ }
+}
+
+bool CPropertyTimebarRow::HasKeyframe(long inTime) const
+{
+ return m_PropertyRow->GetProperty()->GetKeyframeByTime(inTime) != nullptr;
+}
+
+//=============================================================================
+/**
+ * @param inTime -1 for all keyframes
+ */
+void CPropertyTimebarRow::SelectKeysByTime(long inTime, bool inSelected)
+{
+ TTimelineKeyframeList::iterator thePos = m_Keyframes.begin();
+ bool theFoundFlag = false;
+ for (; thePos != m_Keyframes.end() && !theFoundFlag; ++thePos) {
+ CPropertyTimelineKeyframe *theKey = (*thePos);
+ if (inTime == -1 || theKey->GetTime() == inTime) {
+ theKey->Select(inSelected);
+ theFoundFlag = (inTime != -1);
+ }
+ }
+ Invalidate();
+}
+
+void CPropertyTimebarRow::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ m_SnappingListProvider = inProvider;
+}
+
+ISnappingListProvider &CPropertyTimebarRow::GetSnappingListProvider() const
+{
+ // sk - If you hit this, it means the setup is incorrect. e.g. the parent row (which is most
+ // probably a staterow) isn't pass 'down' this info.
+ ASSERT(m_SnappingListProvider);
+ return *m_SnappingListProvider;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.h b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.h
new file mode 100644
index 00000000..a07118f9
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimebarRow.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_TIMEBAR_ROW_H
+#define INCLUDED_PROPERTY_TIMEBAR_ROW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "PropertyTimebarGraph.h"
+#include "StateRow.h"
+
+//==============================================================================
+// forwards
+//==============================================================================
+class CPropertyRow;
+class CPropertyTimelineKeyframe;
+class ISnappingListProvider;
+
+class CPropertyTimebarRow : public CControl
+{
+ typedef std::vector<CPropertyTimelineKeyframe *> TTimelineKeyframeList;
+
+public:
+ CPropertyTimebarRow(CPropertyRow *inPropertyRow);
+ virtual ~CPropertyTimebarRow();
+
+ void Draw(CRenderer *inRenderer) override;
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void SetHighlighted(bool inIsHighlighted);
+ void RefreshKeyframes();
+
+ void SetSize(CPt inSize) override;
+
+ CPropertyRow *GetPropertyRow();
+
+ void SetTimeRatio(double inTimeRatio);
+
+ void SetDetailedView(bool inDetailedView);
+ void OnKeySelected(long inTime, bool inSelected);
+ void DeselectAllKeyframes();
+ void SetDirty(bool inDirtyFlag);
+ void CommitSelections();
+ void SelectKeysInRect(CRct inRect, bool inModifierKeyDown);
+ void SelectAllKeys();
+ bool HasSelectedKeys();
+ void Invalidate(bool inInvalidate = true) override;
+ void SetVisible(bool inIsVisible) override;
+
+ bool HasKeyframe(long inTime) const;
+ void SelectKeysByTime(long inTime, bool inSelected);
+
+ void SetSnappingListProvider(ISnappingListProvider *inProvider);
+ ISnappingListProvider &GetSnappingListProvider() const;
+
+protected:
+ void DrawColor(CRenderer *inRenderer);
+
+protected:
+ TTimelineKeyframeList m_Keyframes; ///< Properties Keyframe List (STL)
+ CPropertyRow *m_PropertyRow;
+ CPropertyTimebarGraph m_DetailedView;
+ ::CColor m_BackgroundColor;
+ double m_TimeRatio;
+
+ bool m_DirtyFlag;
+ bool m_Refreshing;
+ ISnappingListProvider *m_SnappingListProvider;
+};
+#endif // INCLUDED_STATE_TIMEBAR_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.cpp
new file mode 100644
index 00000000..c3a83e09
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.cpp
@@ -0,0 +1,374 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Dialogs.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PropertyTimelineKeyframe.h"
+#include "PropertyTimebarRow.h"
+#include "Renderer.h"
+#include "PropertyRow.h"
+#include "KeyframeContextMenu.h"
+#include "HotKeys.h"
+#include "ResourceCache.h"
+#include "TimelineControl.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "StudioUtils.h"
+#include "TimeEditDlg.h"
+
+CPropertyTimelineKeyframe::CPropertyTimelineKeyframe(CPropertyTimebarRow *inParentRow,
+ double inTimeRatio)
+ : m_Selected(false)
+ , m_IsMouseDown(false)
+ , m_IsDragging(false)
+{
+ m_ParentRow = inParentRow;
+ m_TimeRatio = inTimeRatio;
+ CResourceCache *theCache = CResourceCache::GetInstance();
+ m_Icon = theCache->GetBitmap("Keyframe-Property-Normal.png");
+ m_DisabledIcon = theCache->GetBitmap("Keyframe-Property-Disabled.png");
+ m_SelectedIcon = theCache->GetBitmap("Keyframe-Property-Selected.png");
+ m_DynamicIcon = theCache->GetBitmap("Keyframe-PropertyDynamic-Normal.png");
+ m_DynamicSelectedIcon = theCache->GetBitmap("Keyframe-PropertyDynamic-Selected.png");
+ m_RectOverHandled = false;
+ m_PreviousSelectState = false;
+}
+
+CPropertyTimelineKeyframe::~CPropertyTimelineKeyframe()
+{
+}
+
+//=============================================================================
+/**
+ * SetRectOverHandled: Sets if mouse rectangle has been handled
+ * param@ inState indicates if the rectangle over has been handled.
+ * return@ NONE
+ */
+void CPropertyTimelineKeyframe::SetRectOverHandled(bool inState)
+{
+ m_RectOverHandled = inState;
+}
+
+//=============================================================================
+/**
+ * GetRectOverHandled: GetRectOverHandled
+ * param@ NONE
+ * return@ m_RectOverHandled, which indicates if the rectangle over has been handled
+ */
+bool CPropertyTimelineKeyframe::GetRectOverHandled()
+{
+ return m_RectOverHandled;
+}
+
+//=============================================================================
+/**
+ * SetPreviousSelectState: Sets if the current keyframe was previously selected
+ * param@ inState is used to set m_PreviousSelectState.
+ * return@ NONE
+ */
+void CPropertyTimelineKeyframe::SetPreviousSelectState(bool inState)
+{
+ m_PreviousSelectState = inState;
+}
+
+//=============================================================================
+/**
+ * GetPreviousSelectState: Returns the keyframe's previous select state
+ * param@ NONE
+ * return@ m_PreviousSelectState that stores the select state for the keyframe
+ */
+bool CPropertyTimelineKeyframe::GetPreviousSelectState()
+{
+ return m_PreviousSelectState;
+}
+
+//=============================================================================
+/**
+ * Gets teh correct image and draws
+ */
+void CPropertyTimelineKeyframe::Draw(CRenderer *inRenderer)
+{
+ if (m_Selected) {
+ inRenderer->DrawBitmap(CPt(0, 0), GetImage());
+ } else {
+ inRenderer->DrawBitmap(CPt(2, 0), GetImage());
+ }
+}
+
+//=============================================================================
+/**
+ * Gets the current bitmap depending on the state of the button, postion of the
+ * mouse, etc. Returns the image for the up state by default.
+ */
+QPixmap CPropertyTimelineKeyframe::GetImage()
+{
+ if (!IsEnabled())
+ return m_DisabledIcon;
+ else if (m_IsDynamic) {
+ if (m_Selected)
+ return m_DynamicSelectedIcon;
+ else
+ return m_DynamicIcon;
+ } else if (m_Selected)
+ return m_SelectedIcon;
+ else
+ return m_Icon;
+}
+
+//=============================================================================
+/**
+ * Handler for left-click events. Allows keyframe dragging. Children are
+ * allowed to handle this event before the parent does.
+ * @param inPoint the point where the mouse is
+ * @param inFlags the state of modifier keys when the event occurred
+ * @return true
+ */
+bool CPropertyTimelineKeyframe::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // Store the mouse down location in screen coordinates so that we can check the dragging buffer
+ // in OnMouseMove
+ m_MouseDownLoc = inPoint;
+
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ // If the control key is down then we change state, otherwise
+ if (!((CHotKeys::MODIFIER_CONTROL & inFlags) == CHotKeys::MODIFIER_CONTROL)) {
+ if (!m_Selected)
+ GetProperty()->ClearKeySelection();
+
+ m_Selected = true;
+ } else {
+ m_Selected = !m_Selected;
+ }
+ m_ParentRow->OnKeySelected(m_Time, m_Selected);
+ // TODO : sk - Remove this when we are clear on the fact that UpdateClientScene &
+ // FireChangeEvent do nothing useful here.
+ // This call makes no sense. However, if you run into some wierd
+ //behavior related to mouse down events, this might be a clue.
+ // I am leaving this just in case this speeds up debugging for anyone
+ //else looking at this.
+ // CAssetTimelineKeyframe::OnMouseDown does similar things.
+ // m_Doc->UpdateClientScene( true );
+ // m_ParentRow->GetPropertyRow()->GetProperty()->FireChangeEvent( false );
+ m_IsMouseDown = true;
+
+ m_Snapper.Clear();
+ m_Snapper.SetSource(this);
+ m_Snapper.SetSnappingSelectedKeyframes(false);
+ m_ParentRow->GetSnappingListProvider().PopulateSnappingList(&m_Snapper);
+ m_Snapper.BeginDrag(inPoint.x);
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Handler for right-click events. Pops up a context menu. Children are
+ * allowed to handle this event before the parent does.
+ * @param inPoint the point where the mouse is
+ * @param inFlags the state of modifier keys when the event occurred
+ * @return true
+ */
+bool CPropertyTimelineKeyframe::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseRDown(inPoint, inFlags)) {
+ if (!m_Selected) {
+ GetProperty()->ClearKeySelection();
+ m_Selected = true;
+ m_ParentRow->OnKeySelected(m_Time, m_Selected);
+ }
+
+ CKeyframeContextMenu theMenu(GetProperty()->GetKeyframesManager(), GetProperty());
+ theMenu.SetTime(GetTime());
+ DoPopup(&theMenu, inPoint);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * called when this key is selected
+ * @param inState the state this key is selected to
+ */
+void CPropertyTimelineKeyframe::Select(bool inState)
+{
+ if (m_Selected != inState) {
+ m_Selected = inState;
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * handler for the mouse up event
+ * @param inFlags the state of things when the mouse button was released
+ * @param inPoint the point where the mouse is
+ */
+void CPropertyTimelineKeyframe::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ // If this key is selected, then we need to make sure that it moved all the way to the
+ // mouse up location
+ if (m_Selected && m_IsMouseDown && m_IsDragging) {
+ long theNewTime = m_Snapper.ProcessDrag(m_Time, inPoint.x, inFlags);
+ long theDiffTime = theNewTime - m_Time;
+
+ // theDiffTime can get updated if its invalid.
+ theDiffTime = GetProperty()->OffsetSelectedKeyframes(theDiffTime);
+ // Set this key's time so it won't be recalced in Refresh keyframes in the row
+ SetTime(m_Time + theDiffTime);
+ }
+
+ m_IsMouseDown = false;
+ m_IsDragging = false;
+
+ GetProperty()->CommitChangedKeyframes();
+ HideMoveableWindow();
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * handler for the onMouse Move event. Offsets selected keys.
+ *
+ * @param inFlags the state of things when the mouse was moved
+ * @param inPoint the point where the mouse is
+ */
+void CPropertyTimelineKeyframe::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ // If the mouse is down and this is slected, then offst the keys
+ if (m_IsMouseDown && m_Selected) {
+ // If we are not yet dragging the keyframe
+ if (!m_IsDragging) {
+ long theDiff = ::abs(inPoint.x) - m_MouseDownLoc.x;
+ // Figure out if the mouse has moved far enough to start the drag, and readjust the drag
+ // postion on the snapper
+ m_IsDragging = (::abs(theDiff) > DRAGBUFFER);
+ if (m_IsDragging && (::abs(theDiff) - DRAGBUFFER) > 2) {
+ m_Snapper.BeginDrag(m_MouseDownLoc.x);
+ } else
+ m_Snapper.BeginDrag(inPoint.x);
+ }
+
+ // If we are already dragging the keyframe, procceed as normal
+ if (m_IsDragging) {
+ long theNewTime = m_Snapper.ProcessDrag(m_Time, inPoint.x, inFlags);
+ long theDiffTime = theNewTime - m_Time;
+
+ if (theDiffTime != 0) {
+ // theDiffTime can get updated if its invalid.
+ theDiffTime = GetProperty()->OffsetSelectedKeyframes(theDiffTime);
+ // Set this key's time so it won't be recalced in Refresh keyframes in the row
+ SetTime(m_Time + theDiffTime);
+ Invalidate();
+ }
+ }
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+}
+
+//=============================================================================
+/**
+ * Sets teh time ratio
+ *
+ * @param inTimeRatio the new ratio
+ */
+void CPropertyTimelineKeyframe::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ CPt theSize = GetSize();
+ SetPosition(::TimeToPos(GetTime(), m_TimeRatio) - (theSize.x / 2), 0);
+}
+
+//=============================================================================
+/**
+ * Pass the double click notification on to the row and have it process it.
+ * The row will do object-specific actions on doubleclicks.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ * @return true stating that the event was processed.
+ */
+bool CPropertyTimelineKeyframe::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ GetProperty()->OnEditKeyframeTime(m_Time, ASSETKEYFRAME);
+ return true;
+}
+
+//=============================================================================
+/**
+ * @return true if selected
+ */
+bool CPropertyTimelineKeyframe::IsSelected()
+{
+ return m_Selected;
+}
+
+//=============================================================================
+/**
+* Updates the ToolTip and moves it to the correct place on screen.
+* @param inPoint the point that the tooltip is supposed to be placed.
+*/
+void CPropertyTimelineKeyframe::RefreshToolTip(CPt inPoint)
+{
+ CRct theTimelineBounds(m_ParentRow->GetPropertyRow()->GetTopControl()->GetBounds());
+
+ // format label
+ Q3DStudio::CString theCommentText = " " + ::FormatTimeString(GetTime());
+
+ inPoint.y = GetPosition().y - GetSize().y;
+ inPoint.x = GetSize().x / 2;
+ ShowMoveableWindow(inPoint, theCommentText, theTimelineBounds);
+}
+
+//=============================================================================
+/**
+* Helper function to retrieve the ITimelineItemProperty
+*/
+ITimelineItemProperty *CPropertyTimelineKeyframe::GetProperty() const
+{
+ return m_ParentRow->GetPropertyRow()->GetProperty();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.h
new file mode 100644
index 00000000..40c7f05b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTimelineKeyframe.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_TIMELINE_KEYFRAME
+#define INCLUDED_PROPERTY_TIMELINE_KEYFRAME 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "TimelineKeyframe.h"
+#include "Snapper.h"
+
+#include <QPixmap>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class CPropertyTimebarRow;
+class ITimelineItemProperty;
+
+class CPropertyTimelineKeyframe : public CControl, public CTimelineKeyframe
+{
+
+public:
+ CPropertyTimelineKeyframe(CPropertyTimebarRow *inParentRow, double inTimeRatio);
+ ~CPropertyTimelineKeyframe();
+ void Draw(CRenderer *inRenderer) override;
+ QPixmap GetImage();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void Select(bool inState);
+ void SetTimeRatio(double inTimeRatio);
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool IsSelected();
+ void SetRectOverHandled(bool inState);
+ bool GetRectOverHandled();
+ void SetPreviousSelectState(bool inState);
+ bool GetPreviousSelectState();
+
+protected:
+ void RefreshToolTip(CPt inPoint);
+ ITimelineItemProperty *GetProperty() const;
+
+protected:
+ bool m_Selected;
+ bool m_RectOverHandled; ///< Indicates if the mouse rect over has been handled.
+ bool m_PreviousSelectState; ///< Stores the previous select state for the keyframe.
+ CPropertyTimebarRow *m_ParentRow;
+ bool m_IsMouseDown;
+ CPt m_MouseDownLoc; ///< Location of the mouse after an OnMouseDownEvent, in client coordinates
+ bool m_IsDragging; ///< Indicates whether or not the keyframe is currently being dragged,
+ ///determined by the pixel buffer
+ double m_TimeRatio;
+ CSnapper m_Snapper;
+ QPixmap m_Icon;
+ QPixmap m_DisabledIcon;
+ QPixmap m_SelectedIcon;
+ QPixmap m_DynamicIcon;
+ QPixmap m_DynamicSelectedIcon;
+};
+
+#endif // INCLUDED_PROPERTY_TIMELINE_KEYFRAME
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.cpp
new file mode 100644
index 00000000..994ba2fe
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "PropertyToggleControl.h"
+#include "TimelineRow.h"
+#include "PropertyRow.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+
+CPropertyToggleControl::CPropertyToggleControl(CPropertyRow *inPropertyRow)
+ : m_PropertyRow(inPropertyRow)
+{
+ m_BackgroundColor = m_PropertyRow->GetTimebarBackgroundColor();
+}
+
+CPropertyToggleControl::~CPropertyToggleControl()
+{
+}
+
+void CPropertyToggleControl::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+ inRenderer->FillSolidRect(QRect(0, 0, theRect.size.x + 1, theRect.size.y), m_BackgroundColor);
+
+ // Draw the line at the bottom of this control
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+
+ // Draw the line on the left side of this control
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theRect.size.y - 1));
+
+ // Draw the highlight
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(1, 0));
+ inRenderer->LineTo(CPt(1, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the line on the right side of this control
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(CPt(theRect.size.x - 1, 0));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+void CPropertyToggleControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ m_PropertyRow->OnMouseOver();
+}
+
+void CPropertyToggleControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ m_PropertyRow->OnMouseOut();
+}
+
+void CPropertyToggleControl::SetHighlighted(bool inIsHighlighted)
+{
+ if (inIsHighlighted)
+ m_BackgroundColor = m_PropertyRow->GetTimebarHighlightBackgroundColor();
+ else
+ m_BackgroundColor = m_PropertyRow->GetTimebarBackgroundColor();
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * Handles the OnMouseDownEvent
+ */
+bool CPropertyToggleControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags))
+ m_PropertyRow->Select((CHotKeys::MODIFIER_SHIFT & inFlags) == CHotKeys::MODIFIER_SHIFT);
+
+ return true;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.h b/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.h
new file mode 100644
index 00000000..81435cc6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyToggleControl.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_TOGGLE_CONTROL_H
+#define INCLUDED_PROPERTY_TOGGLE_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "CColor.h"
+
+class CPropertyRow;
+
+class CPropertyToggleControl : public CControl
+{
+public:
+ CPropertyToggleControl(CPropertyRow *inPropertyRow);
+ virtual ~CPropertyToggleControl();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void SetHighlighted(bool inIsHightlighted);
+
+protected:
+ CPropertyRow *m_PropertyRow;
+ ::CColor m_BackgroundColor;
+};
+#endif // INCLUDED_PROPERTY_TOGGLE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.cpp b/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.cpp
new file mode 100644
index 00000000..813f04f3
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PropertyTreeControl.h"
+#include "PropertyRow.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+#include "HotKeys.h"
+#include "ResourceCache.h"
+#include "BaseTimelineTreeControl.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "CoreUtils.h"
+
+CPropertyTreeControl::CPropertyTreeControl(CPropertyRow *inPropRow)
+ : m_Icon(CResourceCache::GetInstance()->GetBitmap("Objects-Property-Normal.png"),
+ CResourceCache::GetInstance()->GetBitmap("Objects-Property-Disabled.png"))
+{
+ m_PropRow = inPropRow;
+
+ CBaseStateRow *theParentRow = m_PropRow->GetParentRow();
+ if (theParentRow) // property row typically should never exists on its own, but to be safe.
+ m_BackgroundColor = m_PropRow->GetTimebarBackgroundColor();
+ else
+ m_BackgroundColor = CStudioPreferences::GetPropertyBackgroundColor();
+
+ AddChild(&m_Icon);
+
+ m_Text.SetData(inPropRow->GetProperty()->GetName());
+ m_Text.SetAlignment(CTextEdit::LEFT);
+ m_Text.SetReadOnly(true);
+
+ AddChild(&m_Text);
+
+ SetIndent(0);
+}
+
+CPropertyTreeControl::~CPropertyTreeControl()
+{
+}
+
+void CPropertyTreeControl::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(GetSize());
+
+ inRenderer->FillSolidRect(theRect, m_BackgroundColor);
+
+ // Draw the line at the bottom of this control
+ CColor theShadowColor = CStudioPreferences::GetPropertyFloorColor();
+ inRenderer->PushPen(theShadowColor);
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+
+ ::CColor theTextColor = CStudioPreferences::GetNormalColor();
+ if (m_PropRow->GetProperty()->IsMaster())
+ theTextColor = CStudioPreferences::GetMasterColor();
+ m_Text.SetTextColor(theTextColor);
+}
+
+void CPropertyTreeControl::SetIndent(long inIndent)
+{
+ m_Indent = inIndent;
+
+ // place it it's size x2 to the right since we don't have a toggle.
+ m_Icon.SetPosition(CPt(m_Indent + m_Icon.GetSize().x, 0));
+
+ m_Text.SetPosition(CPt(m_Icon.GetPosition().x + m_Icon.GetSize().x + 5, 0));
+ m_Text.SetSize(CPt(200, m_Icon.GetSize().y - 1));
+}
+
+long CPropertyTreeControl::GetIndent()
+{
+ return m_Indent;
+}
+
+void CPropertyTreeControl::SetHighlighted(bool inIsHighlighted)
+{
+ CBaseStateRow *theParentRow = m_PropRow->GetParentRow();
+ if (theParentRow) // property row typically should never exists on its own, but to be safe.
+ m_BackgroundColor = (inIsHighlighted) ? m_PropRow->GetTimebarHighlightBackgroundColor()
+ : m_PropRow->GetTimebarBackgroundColor();
+ else
+ m_BackgroundColor = (inIsHighlighted) ? CStudioPreferences::GetPropertyMouseOverColor()
+ : CStudioPreferences::GetPropertyBackgroundColor();
+
+ Invalidate();
+}
+
+//==============================================================================
+/**
+ * HACK: Trying to scroll the timeline during a drag and drop operation.
+ *
+ * The Property Tree Control will never have a drop candidate, so this function
+ * returns nullptr. However, we still need to scroll the timeline in case we are
+ * at the top or bottom of the window. This is hackish for two reasons. 1) It
+ * points out that CPropertyTreeControl and CStateTreeControl should have a
+ * common base class that handles similar code. 2) The TreeControls should not
+ * have to scroll the timeline automatically. It should be handled through a
+ * timer on the scroller when the mouse is being dragged and hovering near the
+ * border of the scroller. But all of that will be saved for another day.
+ * @param inMousePoint location of the mouse
+ * @param inFlags not used (modifier key flags)
+ * @return nullptr (no drop candidate is ever found)
+ */
+CDropTarget *CPropertyTreeControl::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ CPt theSize(GetSize());
+
+ // If the mouse is towards the top of this row, make sure that this row and the
+ // one above it are visible (scroll upwards)
+ if (inMousePoint.y <= ::dtol(theSize.y / 2.0f))
+ EnsureVisible(CRct(CPt(0, -theSize.y), theSize));
+ // Otherwise, the mouse is towards the bottom of this row, so make sure this row
+ // and the one below it are visible (scroll downwards)
+ else
+ EnsureVisible(CRct(CPt(theSize.x, theSize.y * 2)));
+
+ return nullptr;
+}
+
+void CPropertyTreeControl::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ m_PropRow->OnMouseOver();
+}
+
+void CPropertyTreeControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ m_PropRow->OnMouseOut();
+}
+
+//==============================================================================
+/**
+ * Handles the OnMouseDownEvent
+ */
+bool CPropertyTreeControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags))
+ m_PropRow->Select((CHotKeys::MODIFIER_SHIFT & inFlags) == CHotKeys::MODIFIER_SHIFT);
+
+ return true;
+}
+
+bool CPropertyTreeControl::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDoubleClick(inPoint, inFlags)) {
+ m_PropRow->OnMouseDoubleClick();
+ }
+ return true;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.h b/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.h
new file mode 100644
index 00000000..5bc61c23
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/PropertyTreeControl.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PROPERTY_TREE_CONTROL_H
+#define INCLUDED_PROPERTY_TREE_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "SIcon.h"
+#include "StringEdit.h"
+
+class CPropertyRow;
+
+class CPropertyTreeControl : public CControl
+{
+public:
+ CPropertyTreeControl(CPropertyRow *inPropRow);
+ virtual ~CPropertyTreeControl();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ void SetIndent(long inIndent);
+ long GetIndent();
+
+ void SetHighlighted(bool inIsHighlighted);
+
+ CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags) override;
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ long m_Indent;
+ CPropertyRow *m_PropRow;
+ CSIcon m_Icon;
+ CStringEdit m_Text;
+ ::CColor m_BackgroundColor;
+};
+#endif // INCLUDED_PROPERTY_TREE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.cpp b/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.cpp
new file mode 100644
index 00000000..b445db83
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "ScalableScroller.h"
+#include "ScalableScrollerBar.h"
+
+CScalableScroller::CScalableScroller()
+ : CScroller(false)
+ , m_ScalableBar(nullptr)
+ , m_ScalingListener(nullptr)
+{
+ Initialize();
+}
+
+CScalableScroller::~CScalableScroller()
+{
+}
+
+CScrollerBar *CScalableScroller::CreateHorizontalBar()
+{
+ if (m_ScalableBar == nullptr) {
+ m_ScalableBar = new CScalableBar(this);
+ m_ScalableBar->SetOrientation(CScrollerBar::HORIZONTAL);
+ m_ScalableBar->SetScalingListener(m_ScalingListener);
+ }
+ return m_ScalableBar;
+}
+
+void CScalableScroller::SetScalingListener(CScalingListener *inListener)
+{
+ m_ScalingListener = inListener;
+
+ if (m_ScalableBar != nullptr) {
+ m_ScalableBar->SetScalingListener(inListener);
+ }
+}
+
+//====================================================================
+/**
+ * Protected function for seting the display size of the scroller.
+ * Overridden because the scaleable scroller thumb at the bottom of
+ * the timeline was getting messed up. Yes, it's a hack, but I couldn't
+ * fix it any other way.
+ */
+void CScalableScroller::SetVisibleSize(CPt inSize)
+{
+ m_VisibleSize = inSize;
+
+ // Don't call the base class because it messes things up
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.h b/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.h
new file mode 100644
index 00000000..9fd6df3b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ScalableScroller.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SCALABLE_SCROLLER_H
+#define INCLUDED_SCALABLE_SCROLLER_H 1
+
+#pragma once
+
+#include "Scroller.h"
+
+class CScalingListener;
+class CScalableBar;
+
+class CScalableScroller : public CScroller
+{
+public:
+ CScalableScroller();
+ virtual ~CScalableScroller();
+
+ void SetScalingListener(CScalingListener *inScalingListener);
+
+ void SetVisibleSize(CPt inSize) override;
+
+protected:
+ CScrollerBar *CreateHorizontalBar() override;
+
+ CScalableBar *m_ScalableBar;
+
+ CScalingListener *m_ScalingListener;
+};
+#endif // INCLUDED_SCALABLE_SCROLLER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.cpp b/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.cpp
new file mode 100644
index 00000000..77c63292
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.cpp
@@ -0,0 +1,359 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "ScrollerBackground.h"
+#include "ScalableScrollerBar.h"
+#include "ScalableScroller.h"
+#include "Renderer.h"
+#include "MouseCursor.h"
+#include "ResourceCache.h"
+
+#include <QApplication>
+
+//=============================================================================
+/**
+ * Creates a new ThumbTab (the scalable ends of the thumb).
+ * @param inThumb the thumb this belongs to.
+ * @param inIsRight true if this is the right side, false for left.
+ * @param inBar the ScalableBar this belongs to.
+ */
+CScalableThumbTab::CScalableThumbTab(CScalableThumb *inThumb, bool inIsRightTab,
+ CScalableBar *inBar)
+ : m_Bar(inBar)
+ , m_IsMouseDown(false)
+ , m_IsRightTab(inIsRightTab)
+{
+ m_Thumb = inThumb;
+}
+
+CScalableThumbTab::~CScalableThumbTab()
+{
+}
+
+//=============================================================================
+/**
+ * MouseOver handler, modifies the cursor.
+ * @param inPoint the mouse location.
+ * @param inFlags the mouse state.
+ */
+void CScalableThumbTab::OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOver(inPoint, inFlags);
+
+ setCursorIfNotSet(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Mouse out handler, invalidates the control to clear the mouse over drawing.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CScalableThumbTab::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ if (!m_IsMouseDown)
+ resetCursor();
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Mouse move handlers, if this was clicked on then drags the control.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CScalableThumbTab::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ // Only drag if this was clicked on
+ if (m_IsMouseDown) {
+ long theDiff = inPoint.x - m_MouseDownLoc.x;
+ // Fire the scaling event for the delta size.
+ if (m_IsRightTab)
+ m_Bar->OnScalingRight(theDiff);
+ else
+ m_Bar->OnScalingLeft(theDiff);
+ }
+
+ CRct theRect(GetSize());
+ if (theRect.IsInRect(inPoint))
+ setCursorIfNotSet(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+}
+
+//=============================================================================
+/**
+ * Mouse click handler, starts resizing the control.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+bool CScalableThumbTab::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseDown(inPoint, inFlags);
+
+ setCursorIfNotSet(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+ m_IsMouseDown = true;
+ m_MouseDownLoc = inPoint;
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Mouse up handler, ends resizing this control.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CScalableThumbTab::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ resetCursor();
+
+ m_IsMouseDown = false;
+}
+
+//=============================================================================
+/**
+ * Draw this control.
+ * @param inRenderer the renderer to draw to.
+ */
+void CScalableThumbTab::Draw(CRenderer *inRenderer)
+{
+ CPt theSize = GetSize();
+ CRct theRect(theSize);
+
+ inRenderer->FillSolidRect(theRect, CStudioPreferences::GetScrollThumbBGColor());
+
+ // Draw the light colored highlight
+ inRenderer->PushPen(CStudioPreferences::GetScrollThumbHighlightColor());
+ inRenderer->MoveTo(CPt(1, theSize.y - 1));
+ inRenderer->LineTo(CPt(1, 1));
+ inRenderer->LineTo(CPt(theSize.x - 1, 1));
+ inRenderer->PopPen();
+
+ // Draw the dark bounding rectangle
+ CColor theGripBoundColor = CStudioPreferences::GetScrollThumbShadowColor();
+ inRenderer->Draw3dRect(theRect, theGripBoundColor, theGripBoundColor);
+}
+
+//=============================================================================
+/**
+ * Create a scalable thumb, this is the dragging component of the bar.
+ * @param inScrollerBar the bar this belongs to.
+ */
+CScalableThumb::CScalableThumb(CScalableBar *inScrollerBar)
+ : CScrollerThumb(inScrollerBar)
+ , m_LeftTab(this, false, inScrollerBar)
+ , m_RightTab(this, true, inScrollerBar)
+{
+ m_ScrollerBar = inScrollerBar;
+
+ m_LeftTab.SetPosition(CPt(0, 0));
+
+ AddChild(&m_LeftTab);
+ AddChild(&m_RightTab);
+}
+
+CScalableThumb::~CScalableThumb()
+{
+}
+
+//=============================================================================
+/**
+ * Set the size of this component, overrides to update the location of the tabs.
+ * @param inSize the new size of this control.
+ */
+void CScalableThumb::SetSize(CPt inSize)
+{
+ CScrollerThumb::SetSize(inSize);
+ m_LeftTab.SetSize(CPt(7, inSize.y));
+ m_RightTab.SetSize(CPt(7, inSize.y));
+ m_RightTab.SetPosition(CPt(inSize.x - m_RightTab.GetSize().x, 0));
+}
+
+//=============================================================================
+/**
+ * On double click this sends off a reset scaling notification.
+ * @param inPoint the mouse location.
+ * @param inFlags the state of the mouse.
+ */
+bool CScalableThumb::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDoubleClick(inPoint, inFlags))
+ m_ScrollerBar->OnScalingReset();
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Creates a new scalable scroller bar.
+ * This object can only be used for horizontal scrolling.
+ * @param inScroller the scalable scroller this is operating on.
+ */
+CScalableBar::CScalableBar(CScalableScroller *inScroller)
+ : CScrollerBar(inScroller, false)
+ , m_ScalableScroller(inScroller)
+ , m_Listener(nullptr)
+ , m_Thumb(nullptr)
+{
+ Initialize();
+}
+
+CScalableBar::~CScalableBar()
+{
+}
+
+//=============================================================================
+/**
+ * Create a new thumb control.
+ * This method is here so the ScalableBar can override it and return the Scalable
+ * Thumb instead of a normal thumb.
+ */
+CControl *CScalableBar::CreateThumb()
+{
+ if (m_Thumb == nullptr)
+ m_Thumb = new CScalableThumb(this);
+ return m_Thumb;
+}
+
+//=============================================================================
+/**
+ * This control is not allowed to become disabled (it should always allow scaling).
+ * @param inIsEnabled ignored.
+ */
+void CScalableBar::SetEnabled(bool inIsEnabled)
+{
+ Q_UNUSED(inIsEnabled);
+ CControl::SetEnabled(true);
+}
+
+//=============================================================================
+/**
+ * Set the single scaling listener that is to carry out the actual scaling work.
+ * @param inListener the scaling listener, there is only one.
+ */
+void CScalableBar::SetScalingListener(CScalingListener *inListener)
+{
+ m_Listener = inListener;
+}
+
+//=============================================================================
+/**
+ * Event from the left ThumbTab that it is scaling.
+ * @param inAmount the amount that the left ThumbTab is being scaled by.
+ */
+void CScalableBar::OnScalingLeft(long inAmount)
+{
+ CPt theLoc = m_Thumb->GetPosition();
+ CPt theSize = m_Thumb->GetSize();
+
+ // Don't let the loc go before the end of the control.
+ if (theLoc.x + inAmount < 0)
+ inAmount = -theLoc.x;
+
+ // Anchors the scroller position when its size reaches the minimum size
+ // The algorithm does not modify the inAmount as the scale can be further reduced,
+ // when the scroller reaches the minimum size.
+
+ CPt thePreviousPosition;
+ bool theAnchor = false;
+ if (theSize.x - inAmount < m_Thumb->GetMinimumSize().x) {
+ thePreviousPosition = m_Thumb->GetPosition();
+ theAnchor = true;
+ }
+
+ // Tell the listener of the scaling, it's the listener that will do the actual scaling work.
+ if (m_Listener != nullptr)
+ m_Listener->OnScalingLeft(theSize.x - inAmount, m_Background->GetSize().x,
+ m_Thumb->GetPosition().x + inAmount);
+
+ // When the Anchor flag is true (i.e. when the scroller has reach its minimum size), stop the
+ // scroller
+ // from moving by restoring its previous position.
+ if (theAnchor) {
+ m_Thumb->SetPosition(thePreviousPosition);
+ }
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Event from the right ThumbTab that it is scaling.
+ * @param inAmount the amount that the left ThumbTab is being scaled by.
+ */
+void CScalableBar::OnScalingRight(long inAmount)
+{
+ CPt theLoc = m_Thumb->GetPosition();
+ CPt theSize = m_Thumb->GetSize();
+ // Don't let the loc go after the end of the control.
+ if (theLoc.x + theSize.x + inAmount > m_Background->GetSize().x)
+ inAmount = m_Background->GetSize().x - (theLoc.x + theSize.x);
+
+ // Anchors the scroller position when its size reaches the minimum size
+ // The algorithm does not modify the inAmount as the scale can be further reduced,
+ // when the scroller reaches the minimum size.
+
+ CPt thePreviousPosition;
+ bool theAnchor = false;
+ if (theSize.x + inAmount < m_Thumb->GetMinimumSize().x) {
+ thePreviousPosition = m_Thumb->GetPosition();
+ theAnchor = true;
+ }
+
+ // Tell the listener of the scaling, it's the listener that will do the actual scaling work.
+ if (m_Listener != nullptr)
+ m_Listener->OnScalingRight(theSize.x + inAmount, m_Background->GetSize().x,
+ m_Thumb->GetPosition().x);
+
+ // When the Anchor flag is true (i.e. when the scroller has reach its minimum size), stop the
+ // scroller
+ // from moving by restoring its previous position.
+ if (theAnchor) {
+ m_Thumb->SetPosition(thePreviousPosition);
+ }
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Handles scaling reset messages commands, just routes them to the listener.
+ */
+void CScalableBar::OnScalingReset()
+{
+ m_Listener->OnScalingReset();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.h b/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.h
new file mode 100644
index 00000000..afc0ef1e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ScalableScrollerBar.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SCALABLE_SCROLLER_BAR_H
+#define INCLUDED_SCALABLE_SCROLLER_BAR_H 1
+
+#pragma once
+
+#include "ScrollerThumb.h"
+#include "ScrollerBar.h"
+
+#include <QCursor>
+
+class CScalableBar;
+class CScalableScroller;
+class CScalableThumb;
+
+class CScalingListener
+{
+public:
+ virtual void OnScalingLeft(long inLength, long inTotalLength, long inOffset) = 0;
+ virtual void OnScalingRight(long inLength, long inTotalLength, long inOffset) = 0;
+ virtual void OnScalingReset() = 0;
+};
+
+class CScalableThumbTab : public CControl
+{
+public:
+ CScalableThumbTab(CScalableThumb *inThumb, bool inIsRightTab, CScalableBar *inBar);
+ virtual ~CScalableThumbTab();
+
+ void Draw(CRenderer *inRenderer) override;
+
+ void OnMouseOver(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ CScalableThumb *m_Thumb;
+ CScalableBar *m_Bar;
+ bool m_IsMouseDown;
+ bool m_IsRightTab;
+ CPt m_MouseDownLoc;
+};
+
+class CScalableThumb : public CScrollerThumb
+{
+public:
+ CScalableThumb(CScalableBar *inScrollerBar);
+ virtual ~CScalableThumb();
+
+ void SetSize(CPt inSize) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ CScalableBar *m_ScrollerBar;
+ CScalableThumbTab m_LeftTab;
+ CScalableThumbTab m_RightTab;
+};
+
+class CScalableBar : public CScrollerBar
+{
+public:
+ CScalableBar(CScalableScroller *inScroller);
+ virtual ~CScalableBar();
+
+ void SetEnabled(bool inIsEnabled) override;
+
+ void SetScalingListener(CScalingListener *inListener);
+
+ void OnScalingRight(long inAmount);
+ void OnScalingLeft(long inAmount);
+ void OnScalingReset();
+
+protected:
+ CControl *CreateThumb() override;
+
+ CScalableScroller *m_ScalableScroller;
+
+ CScalingListener *m_Listener;
+ CScalableThumb *m_Thumb;
+};
+
+#endif // INCLUDED_SCALABLE_SCROLLER_BAR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/SlideRow.cpp b/src/Authoring/Studio/Palettes/Timeline/SlideRow.cpp
new file mode 100644
index 00000000..ac14bee2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/SlideRow.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "SlideRow.h"
+#include "BlankToggleControl.h"
+#include "ColorControl.h"
+#include "SlideTimebarRow.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "ITimelineControl.h"
+
+CSlideRow::CSlideRow(ITimelineItemBinding *inTimelineItem)
+ : m_TimelineControl(nullptr)
+{
+ Initialize(inTimelineItem);
+}
+
+CSlideRow::~CSlideRow()
+{
+}
+
+void CSlideRow::SetTimelineControl(ITimelineControl *inTimelineControl)
+{
+ m_TimelineControl = inTimelineControl;
+}
+
+//=============================================================================
+/**
+ * Expand this node of the tree control.
+ * This will display all children the fit the filter.
+ */
+void CSlideRow::Expand(bool inExpandAll /*= false*/, bool inExpandUp)
+{
+ if (!m_Loaded) {
+ m_Loaded = true;
+ LoadChildren();
+ }
+
+ CBaseStateRow::Expand(inExpandAll, inExpandUp);
+}
+
+//=============================================================================
+/**
+ * This do not 'contribute' to its child's active start time
+ */
+bool CSlideRow::CalculateActiveStartTime()
+{
+ return false;
+}
+//=============================================================================
+/**
+ * This do not 'contribute' to its child's active end time
+ */
+bool CSlideRow::CalculateActiveEndTime()
+{
+ return false;
+}
+
+ISnappingListProvider *CSlideRow::GetSnappingListProvider() const
+{
+ ASSERT(m_TimelineControl);
+ return m_TimelineControl->GetSnappingListProvider();
+}
+
+void CSlideRow::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ // does nothing
+ Q_UNUSED(inProvider);
+}
+
+//=============================================================================
+/**
+ * See CBaseStateRow::GetTopControl comments.
+ * This being to "top" row will have the actual pointer
+ */
+ITimelineControl *CSlideRow::GetTopControl() const
+{
+ return m_TimelineControl;
+}
+
+//=============================================================================
+/**
+ * Create a new CStateTimebarRow.
+ * This is virtual and used for objects to return their type specific
+ * timebar rows if they want to.
+ * @return the created timebar row.
+ */
+CBaseTimebarlessRow *CSlideRow::CreateTimebarRow()
+{
+ return new CSlideTimebarRow(this);
+}
+
+bool CSlideRow::PerformFilter(const CFilter &inFilter)
+{
+ Q_UNUSED(inFilter);
+ return true;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/SlideRow.h b/src/Authoring/Studio/Palettes/Timeline/SlideRow.h
new file mode 100644
index 00000000..d978f11e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/SlideRow.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIME_CONTEXT_ROW_H
+#define INCLUDED_TIME_CONTEXT_ROW_H 1
+
+#pragma once
+
+#include "BaseStateRow.h"
+
+class ISnappingListProvider;
+class ITimelineControl;
+
+class CSlideRow : public CBaseStateRow
+{
+public:
+ CSlideRow(ITimelineItemBinding *inTimelineItem);
+ virtual ~CSlideRow();
+
+ void SetTimelineControl(ITimelineControl *inTimelineControl);
+
+ // BaseStateRow
+ void Expand(bool inExpandAll = false, bool inExpandUp = false) override;
+ bool CalculateActiveStartTime() override;
+ bool CalculateActiveEndTime() override;
+ ISnappingListProvider *GetSnappingListProvider() const override;
+ void SetSnappingListProvider(ISnappingListProvider *inProvider) override;
+ ITimelineControl *GetTopControl() const override;
+
+protected:
+ // CBaseStateRow
+ CBaseTimebarlessRow *CreateTimebarRow() override;
+ bool PerformFilter(const CFilter &inFilter) override;
+
+ ITimelineControl *m_TimelineControl;
+};
+#endif // INCLUDED_TIME_CONTEXT_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.cpp b/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.cpp
new file mode 100644
index 00000000..49ce261f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "SlideTimebarRow.h"
+#include "SlideRow.h"
+
+CSlideTimebarRow::CSlideTimebarRow(CSlideRow *inSlideRow)
+ : m_SlideRow(inSlideRow)
+{
+}
+
+CSlideTimebarRow::~CSlideTimebarRow()
+{
+}
+
+void CSlideTimebarRow::CommitSelections()
+{
+}
+
+void CSlideTimebarRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown)
+{
+ Q_UNUSED(inRect);
+ Q_UNUSED(inModifierKeyDown);
+}
+
+void CSlideTimebarRow::SelectKeysByTime(long inTime, bool inSelected)
+{
+ Q_UNUSED(inTime);
+ Q_UNUSED(inSelected);
+}
+
+void CSlideTimebarRow::SelectAllKeys()
+{
+}
+
+void CSlideTimebarRow::PopulateSnappingList(CSnapper *inSnapper)
+{
+ Q_UNUSED(inSnapper);
+}
+
+CBaseStateRow *CSlideTimebarRow::GetBaseStateRow() const
+{
+ return m_SlideRow;
+}
+
+// This is not applicable to a SlideTimebarRow!!
+ISnappingListProvider &CSlideTimebarRow::GetSnappingListProvider() const
+{
+ throw;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.h b/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.h
new file mode 100644
index 00000000..a2f63e33
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/SlideTimebarRow.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIME_CONTEXT_TIMEBAR_ROW_H
+#define INCLUDED_TIME_CONTEXT_TIMEBAR_ROW_H 1
+
+#pragma once
+
+#include "BaseTimebarlessRow.h"
+
+class CSlideRow;
+
+class CSlideTimebarRow : public CBaseTimebarlessRow
+{
+public:
+ CSlideTimebarRow(CSlideRow *inSlideRow);
+ virtual ~CSlideTimebarRow();
+
+ void CommitSelections() override;
+
+ void SelectKeysInRect(CRct inRect, bool inModifierKeyDown) override;
+ void SelectKeysByTime(long inTime, bool inSelected) override;
+ void SelectAllKeys() override;
+
+ void PopulateSnappingList(CSnapper *inSnappingList) override;
+ ISnappingListProvider &GetSnappingListProvider() const override;
+
+protected:
+ CBaseStateRow *GetBaseStateRow() const override;
+
+protected:
+ CSlideRow *m_SlideRow;
+};
+#endif // INCLUDED_TIME_CONTEXT_TIMEBAR_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/Snapper.cpp b/src/Authoring/Studio/Palettes/Timeline/Snapper.cpp
new file mode 100644
index 00000000..2b405e70
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Snapper.cpp
@@ -0,0 +1,455 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "Snapper.h"
+#include "StudioPreferences.h"
+#include "Control.h"
+#include "CoreUtils.h"
+#include "StudioUtils.h"
+#include "HotKeys.h"
+
+//=============================================================================
+/**
+ * Create a new snapper object.
+ * @param inTimeRatio the default time ratio.
+ */
+CSnapper::CSnapper(double inTimeRatio)
+ : m_theStartTime(0)
+ , m_theEndTime(0)
+ , m_TimeRatio(inTimeRatio)
+ , m_IsSnappingKeyframes(false)
+ , m_IsSnappingSelectedKeyframes(true)
+ , m_Offset(0)
+ , m_StartHeight(0)
+ , m_EndHeight(0)
+ , m_PeriodicInterval(LONG_MAX)
+ , m_InitialOffset(0)
+ , m_ObjectTimeOffset(0)
+ , m_TimeOffset(0)
+ , m_KeyFrameClicked(false)
+ , m_Source(nullptr)
+{
+ SetSnappingDistance(CStudioPreferences::GetSnapRange());
+}
+
+CSnapper::~CSnapper()
+{
+}
+//=============================================================================
+/**
+ * Set if keyframe is clicked
+ * @param inKeyFrameClicked toggles snapping to end handles at 1 pixel range,
+ * if true.
+ */
+void CSnapper::SetKeyFrameClicked(bool inKeyFrameClicked)
+{
+ m_KeyFrameClicked = inKeyFrameClicked;
+}
+//=============================================================================
+/**
+ * Clear all the snapping points from this.
+ * This effectively erases this object.
+ */
+void CSnapper::Clear()
+{
+ m_PeriodicInterval = LONG_MAX;
+ m_Times.clear();
+ m_KeyFrameClicked = false;
+ SetSnappingDistance(CStudioPreferences::GetSnapRange());
+}
+
+//=============================================================================
+/**
+ * Add a snapping point at the specified time.
+ * @param inTime the time to add the point at.
+ */
+void CSnapper::AddTime(long inTime)
+{
+ m_Times.insert(inTime);
+}
+
+//=============================================================================
+/**
+ * Add a snapping point at the specified pixel location.
+ * @param inPosition the position of the snapping point to add.
+ */
+void CSnapper::AddPixelLocation(long inPosition)
+{
+ AddTime(::PosToTime(inPosition, m_TimeRatio));
+}
+
+//=============================================================================
+/**
+ * Set whether or not keyframes should be added as snapping points.
+ * @param true if keyframes should be added as snapping points.
+ */
+void CSnapper::SetSnappingKeyframes(bool inIsSnappingKeyframes)
+{
+ m_IsSnappingKeyframes = inIsSnappingKeyframes;
+}
+
+//=============================================================================
+/**
+ * Checks whether or not keyframes are being snapped to.
+ * @return true if keyframes should be snapped to.
+ */
+bool CSnapper::IsSnappingKeyframes()
+{
+ return m_IsSnappingKeyframes;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not selected keyframes are being snapped to.
+ * This is used to ignore all selected keyframes when keyframes are being
+ * dragged.
+ * @param inIsSnappingSelectedKeyframes true if selected keys should be snapped.
+ */
+void CSnapper::SetSnappingSelectedKeyframes(bool inIsSnappingSelectedKeyframes)
+{
+ m_IsSnappingSelectedKeyframes = inIsSnappingSelectedKeyframes;
+}
+
+//=============================================================================
+/**
+ * Checks whether or not selected keyframes are being snapped to.
+ * This is used to ignore all selected keyframes when keyframes are being
+ * dragged.
+ * @return true if selected keyframes should be snapping points.
+ */
+bool CSnapper::IsSnappingSelectedKeyframes()
+{
+ return m_IsSnappingKeyframes && m_IsSnappingSelectedKeyframes;
+}
+
+//=============================================================================
+/**
+ * Set the current time ratio.
+ * This will effect all time based positions as well as the intervals. It is
+ * suggested that this is called on an empty snapper when possible for speed.
+ * @param inTimeRatio the new time ratio.
+ */
+void CSnapper::SetTimeRatio(double inTimeRatio)
+{
+ m_SnapDistance = ::dtol(m_SnapDistance * m_TimeRatio / inTimeRatio);
+ m_TimeRatio = inTimeRatio;
+}
+
+//=============================================================================
+/**
+ * Set the visible area of this snapper.
+ * This is used to limit snapping to visible areas only. The visibility limit
+ * is only on vertical because the user can scroll horizontally and expose
+ * previously non visible objects. The heights are relative to the initial
+ * offset and
+ * @param inStartHeight the minimum height for visibility.
+ * @param inEndHeight the maximum height for visibility.
+ */
+void CSnapper::SetVisibleArea(long inStartHeight, long inEndHeight)
+{
+ m_StartHeight = inStartHeight;
+ m_EndHeight = inEndHeight;
+}
+
+//=============================================================================
+/**
+ * Push an offset into this for calculating later visibilities.
+ * This is accumulated with previous offsets.
+ * @param inHeight the amount to modify the offset by.
+ */
+void CSnapper::PushOffset(long inHeight)
+{
+ m_Offsets.push_back(inHeight);
+ m_Offset += inHeight;
+}
+
+//=============================================================================
+/**
+ * Remove an offset that was pushed on.
+ * This will update the current offset to be what it was before the push.
+ */
+void CSnapper::PopOffset()
+{
+ m_Offset -= m_Offsets.back();
+ m_Offsets.pop_back();
+}
+
+//=============================================================================
+/**
+ * Checks to see if an object at inPosition of height inHeight is visible.
+ * This uses the current offset with the visible area to check visibility.
+ * @param inPosition the position of the object to be checked.
+ * @param inHeight the height of the object to be checked.
+ */
+bool CSnapper::IsVisible(long inPosition, long inHeight)
+{
+ return (inPosition + m_Offset < m_EndHeight
+ && inPosition + m_Offset + inHeight > m_StartHeight);
+}
+
+//=============================================================================
+/**
+ * Add a periodic interval to the snapping points.
+ * This will make snapping points at every multiple of inInterval. The interval
+ * starts at time 0.
+ * @param inInterval time in millis for the periodic points.
+ */
+void CSnapper::SetPeriodicInterval(long inInterval)
+{
+ m_PeriodicInterval = inInterval;
+}
+
+//=============================================================================
+/**
+ * Interpret the given position into a snapped/nonsnapped position.
+ * This will use the inFlags to determine whether or not this should be snapping
+ * and uses inPosition to figure out the closest snapping position. If the
+ * closest position is not within the tolerances then inPosition will be
+ * returned.
+ * @param inPosition to position to check for snapping.
+ * @param inFlags the mouse state flags, to determine whether or not snapping.
+ */
+bool CSnapper::InterpretTimeEx(long &ioTime, long inFlags)
+{
+ // Only snap if shift key is down.
+ if (inFlags & CHotKeys::MODIFIER_SHIFT)
+ return GetSnapTime(ioTime, true);
+ else
+ return GetSnapTime(ioTime, false);
+}
+
+//=============================================================================
+/**
+ * Interpret the given position into a snapped/nonsnapped position.
+ * This will use the inFlags to determine whether or not this should be snapping
+ * and uses inPosition to figure out the closest snapping position. If the
+ * closest position is not within the tolerances then inPosition will be
+ * returned.
+ * @param inPosition to position to check for snapping.
+ * @param inFlags the mouse state flags, to determine whether or not snapping.
+ */
+long CSnapper::InterpretTime(long inTime, long inFlags)
+{
+ if (inFlags & CHotKeys::MODIFIER_SHIFT) {
+ GetSnapTime(inTime, true);
+ return inTime;
+ }
+ GetSnapTime(inTime, false);
+ return inTime;
+}
+
+//=============================================================================
+/**
+ * Set the maximum distance that snapping will occur at.
+ * This sets the maximum tolerances for a position to be away from a snapping
+ * point and still get snapped to it.
+ * @param inSnapDistance the snap distance, in pixels.
+ */
+void CSnapper::SetSnappingDistance(long inSnapDistance)
+{
+ m_SnapDistance = ::dtol(inSnapDistance / m_TimeRatio);
+}
+
+//=============================================================================
+/**
+ * Helper method to find the closer of two values to a third.
+ * If both values are the same distance then the first value will be returned.
+ * @param inFirst the first value to check the distance to inBase.
+ * @param inSecond the second value to check the distance to inBase.
+ * @param inBase the value the others are being compared to.
+ * @return the value, either first or second, that is closest to inBase.
+ */
+long GetClosestValue(long inFirst, long inSecond, long inBase)
+{
+ return (::labs(inFirst - inBase) <= ::labs(inSecond - inBase)) ? inFirst : inSecond;
+}
+
+//=============================================================================
+/**
+ * Given the current time, it is adjusted if necessary to snap.
+ * @param ioTime the current time on input; on output the adjusted time
+ * @param inShiftKeyDown true if the shift key was down, otherwise false
+ * @return true if a snap occurred, otherwise false
+ */
+bool CSnapper::GetSnapTime(long &ioTime, bool inShiftKeyDown)
+{
+ bool theReturnValue = false;
+
+ if (inShiftKeyDown) // If user hits the shift key (i.e. snapping is toggled on)
+ {
+ long thePreviousTime = 0;
+ long theNextTime = 0;
+
+ // Go through all the snapping positions finding the positions on either
+ // side of ioPosition. Bsically just loop through until a snap position
+ // is larger than in position and use that with the previous value to get
+ // the closest snapping location.
+ TTimeList::iterator thePos = m_Times.begin();
+ for (; thePos != m_Times.end(); ++thePos) {
+ thePreviousTime = theNextTime;
+ theNextTime = (*thePos);
+
+ // Don't need to go any further because we've hit the first point larget than
+ // ioPosition.
+ if (theNextTime >= ioTime) {
+ break;
+ }
+ }
+
+ // Use the last snap position less than ioPosition and the first snap position greater than
+ // ioPosition
+ // to find the closest of the two.
+ long theClosestTime = GetClosestValue(thePreviousTime, theNextTime, ioTime);
+ long theClosestInterval = GetClosestPeriodicInterval(ioTime);
+
+ // Get the closest snapping position between the periodic interval and the position
+ theClosestTime = GetClosestValue(theClosestTime, theClosestInterval, ioTime);
+
+ // If the closest position is within tolerances then use it, otherwise return the original
+ // value.
+ if (::labs(theClosestTime - ioTime) <= m_SnapDistance) {
+ ioTime = theClosestTime;
+ theReturnValue = true;
+ }
+ } else // If user does not hit the shift key (i.e. snapping is toggled off)
+ {
+ // Snap to end handles at 1 pixel range if the current object dragged
+ // is a keyframe
+ if (m_KeyFrameClicked) {
+ // Returns if the startTime or the endTime of a Time Bar is closer to the dragged
+ // keyframe
+ long theClosestTime = GetClosestValue(m_theStartTime, m_theEndTime, ioTime);
+
+ // Set snapping range to 1 pixel and converts it to time.
+ // The snapping range of 1 pixel applies for keyframes that are dragged really
+ // close to the end handles.
+ long thePixel = 1;
+ long theTime = ::dtol(thePixel / m_TimeRatio);
+
+ // Determines if the closest time is within 1 pixel range
+ // If so returns the closest time, which is the snapping time, and true indicating
+ // that snapping occurs.
+ if (::labs(theClosestTime - ioTime) <= theTime) {
+ ioTime = theClosestTime;
+ theReturnValue = true;
+ }
+ }
+ }
+
+ return theReturnValue;
+}
+//=============================================================================
+/**
+ * Get the closest periodic interval to inPosition.
+ * Since it is too expensive to store every possible periodic interval in the
+ * snapping list, this just dynamically figures out the closest periodic
+ * interval.
+ */
+void CSnapper::SetStartEndTime(long theStartTime, long theEndTime)
+{
+ m_theStartTime = theStartTime;
+ m_theEndTime = theEndTime;
+}
+//=============================================================================
+/**
+ * Get the closest periodic interval to inPosition.
+ * Since it is too expensive to store every possible periodic interval in the
+ * snapping list, this just dynamically figures out the closest periodic
+ * interval.
+ */
+long CSnapper::GetClosestPeriodicInterval(long inTime)
+{
+ long theIntervalLow = inTime / m_PeriodicInterval * m_PeriodicInterval;
+ long theIntervalHigh = (inTime / m_PeriodicInterval + 1) * m_PeriodicInterval;
+
+ return GetClosestValue(theIntervalLow, theIntervalHigh, inTime);
+}
+
+//=============================================================================
+/**
+ * Used to pass off snapping logic to this for objects being dragged.
+ * This does all the work of an object when the object itself is being dragged
+ * and it's position is being modified by the drag, but it needs to be snapped
+ * as well. It is not necessary to call this unless ProcessDrag is being
+ * used.
+ * @param inClickPosition the mouse click location, usually just inPoint.x.
+ * @param inCenterOffset the center of the object where it should be snapped to.
+ */
+void CSnapper::BeginDrag(long inClickLocation, long inCenterTimeOffset)
+{
+ m_InitialOffset = inClickLocation;
+ m_ObjectTimeOffset = inCenterTimeOffset;
+}
+
+//=============================================================================
+/**
+ * Process an object's drag event and figure out where the object should be.
+ * This does all the work of figuring out where the object should be snapped
+ * to. It is not necessary to call this, but it may make implementing snapping
+ * much easier. BeginDrag needs to be called on OnMouseDown to use this.
+ * @param inPosition the position of the mouse, GetPosition( ).x + inPoint.x.
+ * @param inFlags the mouse state flags, used to determine snapping state.
+ */
+long CSnapper::ProcessDrag(long inTime, long inOffset, long inFlags)
+{
+ long theModPos = inTime + m_TimeOffset + ::dtol((inOffset - m_InitialOffset) / m_TimeRatio);
+ InterpretTimeEx(theModPos, inFlags);
+ theModPos -= m_TimeOffset;
+
+ return theModPos;
+}
+
+//=============================================================================
+/**
+ * Set the source for this Snapper object.
+ * This is so that objects may choose to no add themselves to snapping lists
+ * that they originated. This is only meant for comparison with the 'this' ptr.
+ * @param inSource the source object for this snapping list.
+ */
+void CSnapper::SetSource(void *inSource)
+{
+ m_Source = inSource;
+}
+
+//=============================================================================
+/**
+ * Get the source for this snapper object.
+ * @return the source of the snapping.
+ */
+void *CSnapper::GetSource()
+{
+ return m_Source;
+}
+
+void CSnapper::SetTimeOffset(long inTimeOffset)
+{
+ m_TimeOffset = inTimeOffset;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/Snapper.h b/src/Authoring/Studio/Palettes/Timeline/Snapper.h
new file mode 100644
index 00000000..ea230bc0
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/Snapper.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SNAPPER_H
+#define INCLUDED_SNAPPER_H 1
+
+#pragma once
+
+#include <set>
+#include <vector>
+
+class CSnapper
+{
+ typedef std::set<long> TTimeList;
+ typedef std::vector<long> TOffsetList;
+
+public:
+ CSnapper(double inTimeRatio = .1);
+ virtual ~CSnapper();
+
+ void AddTime(long inTime);
+ void AddPixelLocation(long inPixelLoc);
+
+ void SetSnappingKeyframes(bool inIsSnappingKeyframes);
+ bool IsSnappingKeyframes();
+ void SetSnappingSelectedKeyframes(bool inIsSnappingSelectedKeyframes);
+ bool IsSnappingSelectedKeyframes();
+
+ void Clear();
+
+ void SetVisibleArea(long inStartHeight, long inEndHeight);
+ void PushOffset(long inHeight);
+ void PopOffset();
+ bool IsVisible(long inPosition, long inHeight);
+
+ void SetTimeRatio(double inTimeRatio);
+
+ void SetPeriodicInterval(long inInterval);
+
+ bool InterpretTimeEx(long &ioTime, long inFlags);
+ long InterpretTime(long inTime, long inFlags);
+
+ void SetSnappingDistance(long inSnapDistance);
+
+ void BeginDrag(long inPosition, long inOffset = 0);
+ long ProcessDrag(long inTime, long inPosition, long inFlags);
+
+ void SetSource(void *inSource);
+ void *GetSource();
+
+ void SetTimeOffset(long inTimeOffset);
+ void SetStartEndTime(long theStartTime, long theEndTime);
+ void SetKeyFrameClicked(bool inKeyFrameClicked);
+
+protected:
+ bool GetSnapTime(long &inTime, bool inShiftKeyDown);
+ long GetClosestPeriodicInterval(long inTime);
+
+ long m_theStartTime;
+ long m_theEndTime;
+
+ TTimeList m_Times;
+ TOffsetList m_Offsets;
+
+ double m_TimeRatio;
+
+ bool m_IsSnappingKeyframes;
+ bool m_IsSnappingSelectedKeyframes;
+ long m_Offset;
+ long m_StartHeight;
+ long m_EndHeight;
+ long m_SnapDistance;
+
+ long m_PeriodicInterval;
+
+ long m_InitialOffset;
+ long m_ObjectTimeOffset;
+ long m_TimeOffset;
+ bool m_KeyFrameClicked;
+ void *m_Source;
+};
+
+// Interface that will provider the info for snapping logic in the timebars and keyframes
+class ISnappingListProvider
+{
+public:
+ virtual ~ISnappingListProvider() {}
+
+ virtual void PopulateSnappingList(CSnapper *inSnappingList) = 0;
+};
+
+#endif // INCLUDED_SNAPPER_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateRow.cpp b/src/Authoring/Studio/Palettes/Timeline/StateRow.cpp
new file mode 100644
index 00000000..bdc07a11
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateRow.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StateRow.h"
+#include "TimelineControl.h"
+#include "TimelineTimelineLayout.h"
+#include "ColorControl.h"
+#include "ToggleControl.h"
+#include "PropertyRow.h"
+#include "StateTimebarRow.h"
+#include "BaseTimebarlessRow.h"
+#include "BaseTimelineTreeControl.h"
+#include "Bindings/ITimelineItemBinding.h"
+
+//=============================================================================
+/**
+ * Creates a new CStateRow for the Asset.
+ * @param inParentRow the parent of this row.
+ */
+CStateRow::CStateRow(CBaseStateRow *inParentRow)
+{
+ m_ParentRow = inParentRow;
+}
+
+CStateRow::~CStateRow()
+{
+}
+
+CBlankToggleControl *CStateRow::CreateToggleControl()
+{
+ return (m_TimelineItemBinding->ShowToggleControls())
+ ? new CToggleControl(this, m_TimelineItemBinding)
+ : CBaseStateRow::CreateToggleControl();
+}
+
+//=============================================================================
+/**
+ * Create a new CStateTimebarRow.
+ * This is virtual and used for objects to return their type specific
+ * timebar rows if they want to.
+ * @return the created timebar row.
+ */
+CBaseTimebarlessRow *CStateRow::CreateTimebarRow()
+{
+ return new CStateTimebarRow(this);
+}
+
+//=============================================================================
+/**
+ * Initialize this object.
+ * This must be called after construction and may only be called once.
+ */
+void CStateRow::Initialize(ITimelineItemBinding *inTimelineItemBinding,
+ ISnappingListProvider *inProvider)
+{
+ CBaseStateRow::Initialize(inTimelineItemBinding);
+
+ // cache these numbers. I believe caching these numbers is to avoid having to incur expensive
+ // recursive calculations on ever draw.
+ CalculateActiveStartTime();
+ CalculateActiveEndTime();
+
+ SetSnappingListProvider(inProvider);
+
+ if (GetTimelineItem()->IsExpanded()) // this is stored for the current opened presentation and
+ // conveniently help you remember the last view before you
+ // switch slides.
+ Expand();
+ // sk - Delay loading till this is expanded. I think it makes more sense to not have to incur
+ // work till the UI needs to be displayed
+ // Plus it would not work now since the parent always needs to be 'fully' initialized for the
+ //SnappingListProvider, prior to any child creation.
+ // else
+ // LoadChildren( );
+}
+
+//=============================================================================
+/**
+ * Expand this node of the tree control.
+ * This will display all children the fit the filter.
+ */
+void CStateRow::Expand(bool inExpandAll /*= false*/, bool inExpandUp)
+{
+ // Only RecalcLayout if loaded children or expanded.
+ bool theDoRecalLayout = !m_Loaded;
+
+ if (!m_Loaded)
+ LoadChildren();
+
+ bool theWasExpanded = m_IsExpanded;
+ CBaseStateRow::Expand(inExpandAll, inExpandUp);
+ // Check if this is expanded
+ theDoRecalLayout |= (theWasExpanded != m_IsExpanded);
+ GetTimelineItem()->SetExpanded(
+ m_IsExpanded); // remember this setting so that it persist when this row is recreated
+
+ if (theDoRecalLayout)
+ DoTimelineRecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Collapse this node of the tree control.
+ * This will hide all children of this control.
+ */
+void CStateRow::Collapse(bool inCollapseAll /* = false */)
+{
+ bool theWasExpanded = m_IsExpanded;
+ CBaseStateRow::Collapse(inCollapseAll);
+
+ GetTimelineItem()->SetExpanded(
+ m_IsExpanded); // remember this setting so that it persist when this row is recreated
+ // only RecalcLayout if this is collapsed
+ if (theWasExpanded != m_IsExpanded)
+ DoTimelineRecalcLayout();
+}
+
+bool CStateRow::PerformFilter(const CFilter &inFilter)
+{
+ return inFilter.Filter(m_TimelineItemBinding->GetTimelineItem());
+}
+
+//=============================================================================
+/**
+ * Set the indent of this control.
+ * This controls how far to the right the toggle and text display on this
+ * control. The indent should be increased for every level of sub-controls.
+ * @param inIndent how much this control should be indented.
+ */
+void CStateRow::SetIndent(long inIndent)
+{
+ CTimelineRow::SetIndent(inIndent);
+
+ m_TreeControl->SetIndent(inIndent);
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->SetIndent(inIndent + CTimelineRow::TREE_INDENT);
+
+ // For each property on this object
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->SetIndent(inIndent + CTimelineRow::TREE_INDENT);
+ }
+}
+
+bool CStateRow::HasVisibleChildren()
+{
+ if (!m_Loaded) {
+ CTimelineItemOrderedIterator theChildren(m_TimelineItemBinding);
+ // Return true if has children but do not load the children.
+ if (!theChildren.IsDone()) {
+ return true;
+ }
+ CTimelineItemPropertyIterator theProperties(m_TimelineItemBinding);
+ if (!theProperties.IsDone()) {
+ return true;
+ }
+ }
+ return CBaseStateRow::HasVisibleChildren();
+}
+
+ISnappingListProvider *CStateRow::GetSnappingListProvider() const
+{
+ CStateTimebarRow *theTimebarControl = dynamic_cast<CStateTimebarRow *>(m_TimebarControl);
+ return (theTimebarControl) ? &theTimebarControl->GetSnappingListProvider() : nullptr;
+}
+
+void CStateRow::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ CStateTimebarRow *theTimebarControl = dynamic_cast<CStateTimebarRow *>(m_TimebarControl);
+ if (theTimebarControl)
+ theTimebarControl->SetSnappingListProvider(inProvider);
+}
+
+//=============================================================================
+/**
+ * Trigger any external applications where applicable.
+ */
+void CStateRow::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inPoint);
+ Q_UNUSED(inFlags);
+
+ if (!m_TimelineItemBinding
+ ->OpenAssociatedEditor()) // if not handled, fall backon the base class
+ CBaseStateRow::OnMouseDoubleClick(inPoint, inFlags);
+}
+
+void CStateRow::OnTimeChange()
+{
+ CalculateActiveStartTime();
+ CalculateActiveEndTime();
+
+ // sk - I don't see the need to DoTimelineRecalcLayout here, because that is usually when height
+ // of the control change
+ // this should just change width.. but maybe I am missing something, so I am leaving this
+ //here for 'easy' debugging
+ // DoTimelineRecalcLayout( );
+
+ m_TimebarControl->UpdateTime(GetStartTime(), GetEndTime());
+
+ GetTopControl()->OnLayoutChanged();
+}
+
+//=============================================================================
+/**
+ * calculate the active start time... this function set the active start to its
+ * parent's start time if it comes after the objects start time
+ */
+bool CStateRow::CalculateActiveStartTime()
+{
+ long theRetVal = GetStartTime();
+
+ if (m_ParentRow) {
+ if (m_ParentRow->CalculateActiveStartTime()) {
+ long theParentActiveStart = m_ParentRow->GetActiveStart();
+ if (theParentActiveStart > theRetVal)
+ theRetVal = theParentActiveStart;
+ }
+ }
+ m_ActiveStart = theRetVal;
+ return true;
+}
+
+//=============================================================================
+/**
+ * calculate the active end time... this function set the active end to its
+ * parent's end time if it comes before the objects end time
+ */
+bool CStateRow::CalculateActiveEndTime()
+{
+ long theRetVal = GetEndTime();
+ if (m_ParentRow) {
+ if (m_ParentRow->CalculateActiveEndTime()) {
+ long theParentActiveEnd = m_ParentRow->GetActiveEnd();
+ if (theParentActiveEnd < theRetVal)
+ theRetVal = theParentActiveEnd;
+ }
+ }
+ m_ActiveEnd = theRetVal;
+ return true;
+}
+
+//==============================================================================
+/**
+ *
+ */
+long CStateRow::GetLatestEndTime()
+{
+ if (m_IsExpanded) // if its children are not visible, they do not have any affect
+ return CBaseStateRow::GetLatestEndTime();
+
+ return GetActiveEnd();
+}
+
+//=============================================================================
+/**
+ * Load all the properties on this object.
+ */
+void CStateRow::LoadProperties()
+{
+ m_TimelineItemBinding->LoadProperties();
+}
+
+//==============================================================================
+/**
+ * Tells the timeline timeline layout to recalc its layout. Should only be called
+ * from this class, that's why it's protected.
+ */
+void CStateRow::DoTimelineRecalcLayout()
+{
+ GetTopControl()->OnLayoutChanged();
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateRow.h b/src/Authoring/Studio/Palettes/Timeline/StateRow.h
new file mode 100644
index 00000000..f03682e3
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateRow.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STATE_ROW_H
+#define INCLUDED_STATE_ROW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BaseStateRow.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CButtonControl;
+class CButtonDownListener;
+class CColorControl;
+class CStateTreeControl;
+class CToggleControl;
+class CStateTimebarlessRow;
+class CPropertyRow;
+class CCmdBatch;
+class CSnapper;
+class CResImage;
+
+class CStateRow : public CBaseStateRow
+{
+public:
+ CStateRow(CBaseStateRow *inParentRow);
+ virtual ~CStateRow();
+
+ using CBaseStateRow::Initialize;
+ virtual void Initialize(ITimelineItemBinding *inTimelineItemBinding,
+ ISnappingListProvider *inProvider);
+
+ void Expand(bool inExpandAll = false, bool inExpandUp = false) override;
+ void Collapse(bool inCollapseAll = false) override;
+ void SetIndent(long inIndent) override;
+ virtual void OnTimeChange();
+
+ long GetLatestEndTime() override;
+ bool CalculateActiveStartTime() override;
+ bool CalculateActiveEndTime() override;
+ bool HasVisibleChildren() override;
+ ISnappingListProvider *GetSnappingListProvider() const override;
+ void SetSnappingListProvider(ISnappingListProvider *inProvider) override;
+
+ // CControl
+ void OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+protected:
+ CBlankToggleControl *CreateToggleControl() override;
+ CBaseTimebarlessRow *CreateTimebarRow() override;
+
+ bool PerformFilter(const CFilter &inFilter) override;
+ void LoadProperties() override;
+ void DoTimelineRecalcLayout();
+};
+#endif // INCLUDED_STATE_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.cpp b/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.cpp
new file mode 100644
index 00000000..921b4b04
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "StateRowFactory.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "StateRow.h"
+
+//=============================================================================
+/**
+ * Create the type specific StateRow for the Asset.
+ * Different asset use different derivations of StateRow, and this will
+ * return the proper state row for the asset.
+ * @param inTimelineItem the timeline item to create the state row for.
+ * @param inParentRow the parent row of the state row being created.
+ * @param inSnappingListProvider For keyframe/timebar snapping
+ * @return CStateRow the row that represents the state, or nullptr if it should not show up.
+ */
+CStateRow *CStateRowFactory::CreateStateRow(ITimelineItemBinding *inTimelineItem,
+ CBaseStateRow *inParentRow,
+ ISnappingListProvider *inSnappingListProvider)
+{
+ CStateRow *theRow = nullptr;
+ if (inTimelineItem) {
+ theRow = new CStateRow(inParentRow);
+
+ if (theRow != nullptr) {
+ theRow->Initialize(inTimelineItem, inSnappingListProvider);
+ }
+ }
+
+ return theRow;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.h b/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.h
new file mode 100644
index 00000000..d818f4cd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateRowFactory.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STATE_ROW_FACTORY_H
+#define INCLUDED_STATE_ROW_FACTORY_H 1
+
+#pragma once
+
+class ITimelineItemBinding;
+class CStateRow;
+class CBaseStateRow;
+class ISnappingListProvider;
+
+class CStateRowFactory
+{
+protected:
+ CStateRowFactory();
+ virtual ~CStateRowFactory();
+
+public:
+ static CStateRow *CreateStateRow(ITimelineItemBinding *inTimelineItem, CBaseStateRow *inParent,
+ ISnappingListProvider *inSnappingListProvider);
+};
+#endif // INCLUDED_STATE_ROW_FACTORY_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.cpp b/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.cpp
new file mode 100644
index 00000000..0a0ba258
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "StateTimebarRow.h"
+#include "Renderer.h"
+#include "StateRow.h"
+#include "StudioPreferences.h"
+#include "TimebarControl.h"
+#include "MasterP.h"
+#include "Snapper.h"
+#include "Bindings/ITimelineItemBinding.h"
+
+//=============================================================================
+/**
+ * Creates a new CStateTimebarRow for the StateRow.
+ * @param inStateRow the State Row that this is on.
+ * @param inCreateTimebar true if the constructor is responsible for creating a timebar, otherwise
+ * the derived class will take care of the construction.
+ *
+ */
+CStateTimebarRow::CStateTimebarRow(CStateRow *inStateRow, bool inCreateTimebar /*=true*/)
+ : CStateTimebarlessRow(inStateRow)
+ , m_Timebar(nullptr)
+{
+ if (inCreateTimebar) {
+ m_Timebar = new CTimebarControl(this, inStateRow->GetTimelineItemBinding());
+ m_Timebar->SetMinimumSize(CPt(0, CStudioPreferences::GetRowSize()));
+
+ AddChild(m_Timebar);
+ }
+}
+
+CStateTimebarRow::~CStateTimebarRow()
+{
+ delete m_Timebar;
+}
+
+//=============================================================================
+/**
+ * OnMouseRDown event, handles context menus for this object.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+bool CStateTimebarRow::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CStateTimebarlessRow::OnMouseRDown(inPoint, inFlags)) {
+ CPt theTimebarPoint = inPoint - m_Timebar->GetPosition();
+ m_Timebar->ShowContextMenu(theTimebarPoint, false);
+ }
+ return true;
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is being represented per pixel.
+ * @param inTimerPerPixel the amound of time being represented per pixel.
+ */
+void CStateTimebarRow::SetTimeRatio(double inTimeRatio)
+{
+ CStateTimebarlessRow::SetTimeRatio(inTimeRatio);
+
+ m_Timebar->SetTimeRatio(inTimeRatio);
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been selected.
+ */
+void CStateTimebarRow::OnSelect()
+{
+ CStateTimebarlessRow::OnSelect();
+
+ m_Timebar->SetSelected(true);
+}
+
+//=============================================================================
+/**
+ * Notification that the object that this row is representing has been deselected.
+ */
+void CStateTimebarRow::OnDeselect()
+{
+ CStateTimebarlessRow::OnDeselect();
+
+ m_Timebar->SetSelected(false);
+}
+
+//=============================================================================
+/**
+ * Notification from the Asset that it's time has changed.
+ * @param inStartTime the new start time.
+ * @param inEndTime the new end time.
+ */
+void CStateTimebarRow::UpdateTime(long inStartTime, long inEndTime)
+{
+ m_Timebar->Refresh(inStartTime, inEndTime);
+ Invalidate();
+}
+
+void CStateTimebarRow::PopulateSnappingList(CSnapper *inSnappingList)
+{
+ if (inSnappingList->GetSource() != m_Timebar) {
+ inSnappingList->AddTime(m_Timebar->GetStartTime());
+ inSnappingList->AddTime(m_Timebar->GetEndTime());
+ }
+ m_Timebar->PopulateSnappingList(inSnappingList);
+ CStateTimebarlessRow::PopulateSnappingList(inSnappingList);
+}
+
+//=============================================================================
+/**
+ * called when meta data for this row is changed... should be overridden by the
+ * timebar row
+ */
+void CStateTimebarRow::RefreshRowMetaData()
+{
+ Invalidate();
+ m_Timebar->RefreshMetaData();
+}
+
+void CStateTimebarRow::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ m_Timebar->SetSnappingListProvider(inProvider);
+}
+
+ISnappingListProvider &CStateTimebarRow::GetSnappingListProvider() const
+{
+ return m_Timebar->GetSnappingListProvider();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.h b/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.h
new file mode 100644
index 00000000..7b62e3fd
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateTimebarRow.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STATE_TIMEBAR_ROW_H
+#define INCLUDED_STATE_TIMEBAR_ROW_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "StateTimebarlessRow.h"
+
+class CStateRow;
+class CTimebarControl;
+class CSnapper;
+class ITimelineItemBinding;
+
+class CStateTimebarRow : public CStateTimebarlessRow
+{
+public:
+ CStateTimebarRow(CStateRow *inStateRow, bool inCreateTimebar = true);
+ virtual ~CStateTimebarRow();
+
+ // CControl
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void SetTimeRatio(double inTimeRatio) override;
+
+ void OnSelect() override;
+ void OnDeselect() override;
+ void UpdateTime(long inStartTime, long inEndTime) override;
+
+ void PopulateSnappingList(CSnapper *inSnappingList) override;
+ void RefreshRowMetaData() override;
+
+ void SetSnappingListProvider(ISnappingListProvider *inProvider);
+ ISnappingListProvider &GetSnappingListProvider() const override;
+
+protected:
+ CTimebarControl *m_Timebar;
+};
+#endif // INCLUDED_STATE_TIMEBAR_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.cpp b/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.cpp
new file mode 100644
index 00000000..6eebd0c4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.cpp
@@ -0,0 +1,364 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StateTimebarlessRow.h"
+#include "IKeyframe.h"
+#include "Renderer.h"
+#include "StateRow.h"
+#include "MasterP.h"
+#include "KeyframeContextMenu.h"
+#include "Snapper.h"
+#include "MultiSelectAspect.h"
+#include "PropertyTimebarRow.h"
+#include "PropertyRow.h"
+#include "AssetTimelineKeyframe.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "StudioUtils.h"
+
+//=============================================================================
+/**
+ * Creates a new CStateTimebarRow for the StateRow.
+ * @param inStateRow the State Row that this is on.
+ */
+CStateTimebarlessRow::CStateTimebarlessRow(CStateRow *inStateRow)
+ : m_StateRow(inStateRow)
+ , m_Selected(false)
+ , m_Refreshing(false)
+{
+ m_BackgroundColor = m_StateRow->GetTimebarBackgroundColor(m_StateRow->GetObjectType());
+}
+
+CStateTimebarlessRow::~CStateTimebarlessRow()
+{
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ CAssetTimelineKeyframe *theDeletedKey = (*thePos);
+ RemoveChild(theDeletedKey);
+ delete theDeletedKey;
+ }
+}
+
+void CStateTimebarlessRow::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ CControl::OnDraw(inRenderer, inDirtyRect, inIgnoreValidation);
+}
+
+//=============================================================================
+/**
+ * Draws the this row background.
+ * @param inRenderer the renderer to draw to.
+ */
+void CStateTimebarlessRow::Draw(CRenderer *inRenderer)
+{
+ UICPROFILE(Draw);
+
+ if (m_DirtyFlag) {
+ RefreshKeyframes();
+ m_DirtyFlag = false;
+ }
+
+ CBaseTimebarlessRow::Draw(inRenderer);
+}
+
+//=============================================================================
+/**
+ * OnMouseRDown event, handles context menus for this object.
+ * @param inPoint the location of the mouse over this control.
+ * @param inFlags the mouse state flags.
+ */
+bool CStateTimebarlessRow::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_StateRow->Select(SBaseStateRowSelectionKeyState()); // ensure this is selected, but doesn't
+ // affect any key selections, because this
+ // can be triggered from a key being
+ // selected
+ return CControl::OnMouseRDown(inPoint, inFlags);
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is being represented per pixel.
+ * @param inTimerPerPixel the amound of time being represented per pixel.
+ */
+void CStateTimebarlessRow::SetTimeRatio(double inTimeRatio)
+{
+ CBaseTimebarlessRow::SetTimeRatio(inTimeRatio);
+
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ (*thePos)->SetTimeRatio(inTimeRatio);
+ }
+}
+
+//=============================================================================
+/**
+ * Get the state row that this belongs to.
+ */
+CStateRow *CStateTimebarlessRow::GetStateRow()
+{
+ return m_StateRow;
+}
+
+//=============================================================================
+/**
+ * Handler for when a child key is selected
+ *
+ * @param inTime time of the key
+ */
+void CStateTimebarlessRow::OnKeySelected(long inTime, bool inSelected,
+ bool inClearPreviouslySelectedKeys)
+{
+ ITimelineItemBinding *theTimelineItemBinding = m_StateRow->GetTimelineItemBinding();
+ if (inSelected)
+ theTimelineItemBinding->SetSelected(false);
+
+ if (inClearPreviouslySelectedKeys)
+ theTimelineItemBinding->ClearKeySelection();
+
+ theTimelineItemBinding->SelectKeyframes(inSelected, inTime);
+ RefreshKeyframes();
+ Invalidate();
+}
+
+CBaseStateRow *CStateTimebarlessRow::GetBaseStateRow() const
+{
+ return m_StateRow;
+}
+
+//=============================================================================
+/**
+ * Checks the data binding, instead of the property rows since they may not be created
+ * (delayed-loading) if this is not expanded.
+ */
+bool CStateTimebarlessRow::PropertiesHaveKeyframe(long inTime)
+{
+ bool theResult = false;
+
+ ITimelineItemBinding *theTimelineItemBinding = m_StateRow->GetTimelineItemBinding();
+ long theNumProps = theTimelineItemBinding->GetPropertyCount();
+ for (long theIndex = 0; theIndex < theNumProps; ++theIndex) {
+ ITimelineItemProperty *theProp = theTimelineItemBinding->GetProperty(theIndex);
+ if (theProp && theProp->GetKeyframeByTime(inTime)) {
+ theResult = true;
+ break;
+ }
+ }
+ return theResult;
+}
+
+//=============================================================================
+/**
+ * called when keyframes need to be updated, this funciton has two loops:
+ * the first loops through and deletes any keys no longer in the sskf list. the
+ * second adds any keys in the sskf list that are not already in the list
+ *
+ */
+void CStateTimebarlessRow::RefreshKeyframes()
+{
+ UICPROFILE(RefreshKeyframes);
+
+ m_Refreshing = true;
+
+ ITimelineItemBinding *theTimelineItemBinding = m_StateRow->GetTimelineItemBinding();
+ long theKeyframeCount = theTimelineItemBinding->GetKeyframeCount();
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+
+ // First Loop clears any keys that do not correlate to a supersetkey
+ while (thePos != m_Keyframes.end()) {
+ CAssetTimelineKeyframe *theTimelineKey = (*thePos);
+ IKeyframe *theTempKey = nullptr;
+ theTempKey = theTimelineItemBinding->GetKeyframeByTime(theTimelineKey->GetTime());
+
+ // If we find a key at this time, then the timeline key doesn't need to be deleted
+ if (!theTempKey || !PropertiesHaveKeyframe(theTimelineKey->GetTime())) {
+ RemoveChild(theTimelineKey);
+ delete theTimelineKey;
+ thePos = m_Keyframes.erase(thePos);
+ } else if (theTempKey->IsDynamic() != theTimelineKey->IsDynamic()) {
+ theTimelineKey->SetDynamic(theTempKey->IsDynamic());
+ } else {
+ // Set the position
+ theTimelineKey->SetPosition(::TimeToPos(theTempKey->GetTime(), m_TimeRatio)
+ - (theTimelineKey->GetSize().x / 2),
+ 0);
+ ++thePos;
+ }
+ }
+
+ // Second Loop adds the remaining keys
+ for (long theKey = 0; theKey < theKeyframeCount; ++theKey) {
+ bool theFoundFlag = false;
+ IKeyframe *theTempKey = theTimelineItemBinding->GetKeyframeByIndex(theKey);
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+
+ // each key needs to be compared to all the keys in the sskf list to see if it has to be
+ // added.
+ for (; thePos != m_Keyframes.end() && !theFoundFlag; ++thePos) {
+ CAssetTimelineKeyframe *theCurrentKey = (*thePos);
+ if (theCurrentKey->GetTime() == theTempKey->GetTime()) {
+ theFoundFlag = true;
+ }
+ }
+ if (!theFoundFlag && PropertiesHaveKeyframe(theTempKey->GetTime())) {
+ // If we don't have a timeline key, then we have to make a new one
+ CAssetTimelineKeyframe *theAssetTimelineKey =
+ new CAssetTimelineKeyframe(this, m_TimeRatio);
+ theAssetTimelineKey->SetTime(theTempKey->GetTime());
+ theAssetTimelineKey->Select(theTempKey->IsSelected());
+ theAssetTimelineKey->SetDynamic(theTempKey->IsDynamic());
+ theAssetTimelineKey->SetSize(CPt(17, 16));
+ theAssetTimelineKey->SetPosition(::TimeToPos(theTempKey->GetTime(), m_TimeRatio)
+ - (theAssetTimelineKey->GetSize().x / 2),
+ 0);
+ AddChild(theAssetTimelineKey);
+ m_Keyframes.push_back(theAssetTimelineKey);
+ }
+ }
+ m_Refreshing = false;
+}
+
+void CStateTimebarlessRow::Invalidate(bool inInvalidate)
+{
+ if (!m_Refreshing) {
+ CControl::Invalidate(inInvalidate);
+ }
+}
+
+//=============================================================================
+/**
+ * called when a list has a member change selection
+ * @param inTime -1 to affect all keyframes.
+ *
+ */
+void CStateTimebarlessRow::SelectKeysByTime(long inTime, bool inSelected)
+{
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ bool theFoundFlag = false;
+ for (; thePos != m_Keyframes.end() && !theFoundFlag; ++thePos) {
+ CAssetTimelineKeyframe *theKey = (*thePos);
+ if (inTime == -1 || theKey->GetTime() == inTime) {
+ theKey->Select(inSelected);
+ theFoundFlag = (inTime != -1);
+ }
+ }
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * SelectKeysInRect: selects any keyframes inside the rect
+ *
+ * @param inRect the Rect to select the keyframes in
+ * @return NONE.
+ */
+void CStateTimebarlessRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown)
+{
+ CMultiSelectAspect<TTimelineAssetKeyframeList> theMultiSelectAspect(
+ m_Keyframes, m_StateRow->GetTimelineItemBinding());
+ theMultiSelectAspect.MultiSelect(inRect, inModifierKeyDown);
+}
+
+//=============================================================================
+/**
+ * CommitSelections: commits all the master keyframe selections by setting their
+ * previous selection state to the current selection state.
+ * This will prevent the current keyframe states from
+ *changing.
+ *
+ * @param NONE
+ * @return NONE
+ */
+
+void CStateTimebarlessRow::CommitSelections()
+{
+ CMultiSelectAspect<TTimelineAssetKeyframeList> theMultiSelectAspect(
+ m_Keyframes, m_StateRow->GetTimelineItemBinding());
+ theMultiSelectAspect.CommitSelections();
+}
+
+//=============================================================================
+/**
+ * true if there are selected keys on this object
+ */
+bool CStateTimebarlessRow::HasSelectedKeys()
+{
+ bool theRetVal = false;
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end() && !theRetVal; ++thePos) {
+ if ((*thePos)->IsSelected()) {
+ theRetVal = true;
+ }
+ }
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * selects all keys for this timebar row
+ */
+void CStateTimebarlessRow::SelectAllKeys()
+{
+ m_StateRow->GetTimelineItemBinding()->SelectKeyframes(true, -1);
+
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos)
+ (*thePos)->Select(true);
+
+ Invalidate();
+}
+
+//=============================================================================
+/**
+ * Populates the snapping list with any snapping points that may be on this.
+ * This will add the timebar ends, master keyframes to the snapping list, and
+ * time labels to the snapping list.
+ * @param inSnapper the snapper to add the points to.
+ */
+void CStateTimebarlessRow::PopulateSnappingList(CSnapper *inSnapper)
+{
+ // Only add points if this is not the object originating the snapping.
+ if (inSnapper->GetSource() != this) {
+ // Add Keyframes
+ TTimelineAssetKeyframeList::iterator thePos = m_Keyframes.begin();
+ for (; thePos != m_Keyframes.end(); ++thePos) {
+ if (inSnapper->IsSnappingSelectedKeyframes())
+ inSnapper->AddTime((*thePos)->GetTime());
+ else if (!(*thePos)->IsSelected())
+ inSnapper->AddTime((*thePos)->GetTime());
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.h b/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.h
new file mode 100644
index 00000000..1812e365
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/StateTimebarlessRow.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STATE_TIMEBARLESS_ROW_H
+#define INCLUDED_STATE_TIMEBARLESS_ROW_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BaseTimebarlessRow.h"
+#include "DispatchListeners.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStateRow;
+class CSnapper;
+class CAssetTimelineKeyframe;
+class ITimelineItemBinding;
+
+class CStateTimebarlessRow : public CBaseTimebarlessRow
+{
+ typedef std::vector<CAssetTimelineKeyframe *> TTimelineAssetKeyframeList;
+
+public:
+ CStateTimebarlessRow(CStateRow *inStateRow);
+ virtual ~CStateTimebarlessRow();
+
+ void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false) override;
+ void Draw(CRenderer *inRenderer) override;
+
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void OnKeySelected(long inTime, bool inState, bool inClearPreviouslySelectedKeys);
+
+ void SetTimeRatio(double inTimeRatio) override;
+
+ virtual void RefreshKeyframes();
+ void Invalidate(bool inInvalidate = true) override;
+ void CommitSelections() override;
+ void SelectKeysInRect(CRct inRect, bool inModifierKeyDown) override;
+ void SelectAllKeys() override;
+ void SelectKeysByTime(long inTime, bool inSelected) override;
+ bool HasSelectedKeys();
+ CStateRow *GetStateRow();
+ void PopulateSnappingList(CSnapper *inSnapper) override;
+
+protected:
+ CBaseStateRow *GetBaseStateRow() const override;
+ bool PropertiesHaveKeyframe(long inTime);
+
+protected:
+ CStateRow *m_StateRow;
+ bool m_Selected;
+ TTimelineAssetKeyframeList m_Keyframes; ///<Master Keyframe list ( STL )
+ bool m_Refreshing;
+};
+#endif // INCLUDED_STATE_TIMEBARLESS_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.cpp b/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.cpp
new file mode 100644
index 00000000..5ef32619
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.cpp
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "TimeEditAspect.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimeEditAspect::CTimeEditAspect()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimeEditAspect::~CTimeEditAspect()
+{
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.h b/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.h
new file mode 100644
index 00000000..f8cf0599
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeEditAspect.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIME_EDIT_ASPECT_H
+#define INCLUDED_TIME_EDIT_ASPECT_H 1
+
+#pragma once
+//==============================================================================
+// Prefix
+//==============================================================================
+
+//==============================================================================
+/**
+ * @class CTimeEditAspect: It contains cross-platform codes that handles the Time Edit
+ * processing for the the time edit dialog box in Mac and Win
+ */
+//==============================================================================
+class CTimeEditAspect
+{
+protected:
+public:
+ CTimeEditAspect();
+ ~CTimeEditAspect();
+};
+#endif // INCLUDED_TIME_EDIT_ASPECT_H \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.cpp b/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.cpp
new file mode 100644
index 00000000..9735499a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.cpp
@@ -0,0 +1,340 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "TimeMeasure.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "StudioUtils.h"
+#include "TimelineTimelineLayout.h"
+#include "Snapper.h"
+#include "UICDMSignals.h"
+
+const long AUTO_TICK_AMNT = 60;
+using namespace Q3DStudio;
+
+//=============================================================================
+/**
+ * Create a new time measure.
+ * @param inLayout the layout this is representing, used for modifying time.
+ * @param inTimeRatio the current time ratio.
+ * @param inIsTransparent true if the background of this control should not be drawn.
+ */
+CTimeMeasure::CTimeMeasure(CTimelineTimelineLayout *inLayout, double inTimeRatio,
+ bool inFillBackground /*= true */)
+ : CBaseMeasure(inTimeRatio, inFillBackground)
+ , m_ScrollDir(0)
+ , m_TimePerPixel(0)
+ , m_IsMouseDown(false)
+ , m_TimelineLayout(inLayout)
+{
+ SetTimeRatio(inTimeRatio);
+ SetName("TimeMeasure");
+
+ m_EdgeMargin = 2;
+ // the large tickmark is shorter than the medium to leave room for the text
+ m_LargeHashOffset = 5;
+}
+
+CTimeMeasure::~CTimeMeasure()
+{
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is represented for each pixel.
+ * @param inTimePerPixel the amount of time represented for each pixel.
+ */
+void CTimeMeasure::SetTimeRatio(double inTimeRatio)
+{
+ m_Ratio = inTimeRatio;
+
+ double theTimePerPixel = (double)(1 / inTimeRatio);
+
+ // Only go through this if it has actually changed
+ if (theTimePerPixel != m_TimePerPixel) {
+ m_TimePerPixel = theTimePerPixel;
+
+ // Go through the possible hash settings and find the one that best suits the
+ // time per pixel.
+ double theMillisPerLargeHash = theTimePerPixel * 50;
+ if (theMillisPerLargeHash <= 100) // 100ms
+ theMillisPerLargeHash = 100;
+ else if (theMillisPerLargeHash <= 200) // 200ms
+ theMillisPerLargeHash = 200;
+ else if (theMillisPerLargeHash <= 500) // .5s
+ theMillisPerLargeHash = 500;
+ else if (theMillisPerLargeHash <= 1000) // 1s
+ theMillisPerLargeHash = 1000;
+ else if (theMillisPerLargeHash <= 2000) // 2s
+ theMillisPerLargeHash = 2000;
+ else if (theMillisPerLargeHash <= 5000) // 5s
+ theMillisPerLargeHash = 5000;
+ else if (theMillisPerLargeHash <= 10000) // 10s
+ theMillisPerLargeHash = 10000;
+ else if (theMillisPerLargeHash <= 20000) // 20s
+ theMillisPerLargeHash = 20000;
+ else if (theMillisPerLargeHash <= 30000) // 30s
+ theMillisPerLargeHash = 30000;
+ else if (theMillisPerLargeHash <= 60000) // 1m
+ theMillisPerLargeHash = 60000;
+ else if (theMillisPerLargeHash <= 120000) // 2m
+ theMillisPerLargeHash = 120000;
+ else if (theMillisPerLargeHash <= 300000) // 5m
+ theMillisPerLargeHash = 300000;
+ else if (theMillisPerLargeHash <= 600000) // 10m
+ theMillisPerLargeHash = 600000;
+ else if (theMillisPerLargeHash <= 1200000) // 20m
+ theMillisPerLargeHash = 1200000;
+ else if (theMillisPerLargeHash <= 1800000) // 30m
+ theMillisPerLargeHash = 1800000;
+ else if (theMillisPerLargeHash <= 3600000) // 1h
+ theMillisPerLargeHash = 3600000;
+ else
+ theMillisPerLargeHash = 7200000; // 2h
+
+ // Set the distances between the hashes
+ m_LargeHashInterval = theMillisPerLargeHash;
+ m_MediumHashInterval = theMillisPerLargeHash / 2;
+ m_SmallHashInterval = theMillisPerLargeHash / 10;
+
+ // update to StudioPreferences so that the ',' '.' and '<' '>' keys would respond
+ // accordingly
+ CStudioPreferences::SetTimeAdvanceAmount(static_cast<long>(m_SmallHashInterval));
+ CStudioPreferences::SetBigTimeAdvanceAmount(static_cast<long>(m_MediumHashInterval));
+
+ Invalidate();
+ }
+}
+//=============================================================================
+/**
+ * Get the time formatted as a string.
+ * This will figure out the best way to display the time and return it as a
+ * string.
+ * @param inTime the time to display in milliseconds.
+ * @return the time formatted in a string.
+ */
+Q3DStudio::CString CTimeMeasure::FormatTime(long inTime)
+{
+ long theHours = inTime / 3600000;
+ long theMinutes = inTime % 3600000 / 60000;
+ long theSeconds = inTime % 60000 / 1000;
+ long theMillis = inTime % 1000;
+
+ bool theHoursOnlyFlag = theHours != 0 && theMinutes == 0 && theSeconds == 0 && theMillis == 0;
+ bool theMinutesOnlyFlag =
+ !theHoursOnlyFlag && theMinutes != 0 && theSeconds == 0 && theMillis == 0;
+ bool theSecondsOnlyFlag = !theMinutesOnlyFlag && theMillis == 0;
+
+ Q3DStudio::CString theTime;
+ // If only hours are being displayed then format it as hours.
+ if (theHoursOnlyFlag) {
+ theTime.Format(_UIC("%dh"), theHours);
+ }
+ // If only minutes are being displayed then format it as minutes.
+ else if (theMinutesOnlyFlag) {
+ theTime.Format(_UIC("%dm"), theMinutes);
+ }
+ // If only seconds are being displayed then format as seconds
+ else if (theSecondsOnlyFlag) {
+ theTime.Format(_UIC("%ds"), theSeconds);
+ }
+ // If the intervals are correct then this should only be tenths of seconds, so do that.
+ else {
+ theTime.Format(_UIC("0.%ds"), theMillis / 100);
+ }
+
+ return theTime;
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that this time measure is offset by.
+ * @param inTimeOffset the offset time in milliseconds.
+ */
+void CTimeMeasure::SetTimeOffset(long inTimeOffset)
+{
+ if (inTimeOffset != m_Offset) {
+ m_Offset = inTimeOffset;
+
+ Invalidate();
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the left mouse button was clicked.
+ * This tells the timeline to move the playhead to the current loc.
+ * @param inPoint the location where the mouse was clicked.
+ * @param inFlags the state of the mouse.
+ */
+bool CTimeMeasure::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_IsMouseDown = true;
+
+ m_TimelineLayout->OnTimeMeasureMouseDown(inPoint, inFlags);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse is moving over this control.
+ * If the mouse was clicked on this control this will drag the playhead.
+ * @param inPoint the location where the mouse was clicked.
+ * @param inFlags the state of the mouse.
+ */
+void CTimeMeasure::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+
+ // subtract out the button width since the playhead is never allowed into that area on the right
+ // side
+ // of the timeline and use it for the initial autoscrolling place
+ if (inPoint.x > 0 && inPoint.x <= GetSize().x - CStudioPreferences::GetDefaultButtonWidth()) {
+ CControl::OnMouseMove(inPoint, inFlags);
+ if (m_IsMouseDown)
+ m_TimelineLayout->OnTimeMeasureMouseDown(inPoint, inFlags);
+ m_ScrollDir = 0;
+ } else if (m_IsMouseDown) {
+ if (inPoint.x < 0)
+ m_ScrollDir = -1;
+ else if (inPoint.x > GetSize().x - CStudioPreferences::GetDefaultButtonWidth())
+ m_ScrollDir = 1;
+ m_TimerConnection = ITickTock::GetInstance().AddTimer(
+ 150, true, std::bind(&CTimeMeasure::OnTimer, this), "CTimeMeasure::OnMouseMove");
+ OnTimer();
+ }
+}
+
+//=============================================================================
+/**
+ * Call back for the timer that was set in on mouse move
+ */
+void CTimeMeasure::OnTimer()
+{
+ CPt theOffset;
+ if (m_ScrollDir > 0)
+ theOffset.x =
+ GetSize().x - 2 * CStudioPreferences::GetDefaultButtonWidth() + AUTO_TICK_AMNT;
+ else if (m_ScrollDir < 0)
+ theOffset.x = -AUTO_TICK_AMNT;
+ m_TimelineLayout->OnTimeMeasureMouseDown(theOffset, 0);
+ ;
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse was unclicked.
+ * This stops dragging of the playhead if it was dragging it.
+ * @param inPoint the location where the mouse was unclicked.
+ * @param inFlags the state of the mouse.
+ */
+void CTimeMeasure::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+ m_IsMouseDown = false;
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse was unclicked.
+ * This stops dragging of the playhead if it was dragging it.
+ * @param inPoint the location where the mouse was unclicked.
+ * @param inFlags the state of the mouse.
+ */
+void CTimeMeasure::OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+ m_IsMouseDown = false;
+}
+
+//=============================================================================
+/**
+ * Add the tick marks to the snapping list.
+ * This uses the user preference for the tick marks and adds them.
+ */
+void CTimeMeasure::PopulateSnappingList(CSnapper *inSnapper)
+{
+ // Only if this is supposed to snap to time markers.
+ if (CStudioPreferences::IsTimelineSnappingGridActive()) {
+ // Check the resolution to snap to
+ ESnapGridResolution theResolution = CStudioPreferences::GetTimelineSnappingGridResolution();
+ double thePeriodicInterval;
+ if (theResolution == SNAPGRID_TICKMARKS) {
+ thePeriodicInterval = m_SmallHashInterval;
+ } else if (theResolution == SNAPGRID_HALFSECONDS) {
+ thePeriodicInterval = m_MediumHashInterval;
+ } else {
+ thePeriodicInterval = m_LargeHashInterval;
+ }
+
+ // Set a periodic interval for snapping
+ inSnapper->SetPeriodicInterval(::dtol(thePeriodicInterval));
+ }
+}
+
+void CTimeMeasure::OnLoseFocus()
+{
+ m_TimerConnection = std::shared_ptr<UICDM::ISignalConnection>();
+ m_IsMouseDown = false;
+}
+
+//=============================================================================
+/**
+ * Draw the time at the specified position.
+ * @param inRenderer the renderer to draw to.
+ * @param inPosition the position to draw the time to, the time will be centered here.
+ * @param inTime the time to draw.
+ */
+void CTimeMeasure::DrawMeasureText(CRenderer *inRenderer, long inPosition, long inMeasure)
+{
+ Q3DStudio::CString theTimeFormat(FormatTime(inMeasure));
+ // Offset the position by half the text size to center it over the hash.
+ const auto textSize = inRenderer->GetTextSize(theTimeFormat.toQString());
+ inPosition -= ::dtol(textSize.width() / 2);
+
+ inRenderer->DrawText((float)inPosition, -3, theTimeFormat.toQString(),
+ QRect(0, 0, GetSize().x, GetSize().y),
+ CStudioPreferences::GetRulerTickColor().getQColor());
+}
+
+//=============================================================================
+/**
+ * Calculate the position of a time value on the time measure
+ */
+long CTimeMeasure::CalculatePos(double inNewValue)
+{
+ return ::TimeToPos(inNewValue, m_Ratio);
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.h b/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.h
new file mode 100644
index 00000000..7e72a52c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeMeasure.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIME_MEASURE_H
+#define INCLUDED_TIME_MEASURE_H 1
+
+#pragma once
+
+#include "BaseMeasure.h"
+#include "ITickTock.h"
+
+class CTimelineTimelineLayout;
+class CSnapper;
+
+class CTimeMeasure : public CBaseMeasure
+{
+public:
+ CTimeMeasure(CTimelineTimelineLayout *inLayout, double inTimeRatio,
+ bool inFillBackground = true);
+ virtual ~CTimeMeasure();
+
+ void SetTimeRatio(double inTimeRatio);
+ void SetTimeOffset(long inTimeOffset);
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseRUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ void PopulateSnappingList(CSnapper *inSnapper);
+
+ virtual void OnTimer();
+ void OnLoseFocus() override;
+
+protected:
+ // CBaseMeasure
+ void DrawMeasureText(CRenderer *inRenderer, long inPosition, long inMeasure) override;
+ long CalculatePos(double inNewValue) override;
+
+ Q3DStudio::CString FormatTime(long inTime);
+
+ long m_ScrollDir;
+ double m_TimePerPixel;
+ bool m_IsMouseDown;
+ CTimelineTimelineLayout *m_TimelineLayout;
+ std::shared_ptr<UICDM::ISignalConnection> m_TimerConnection;
+};
+#endif // INCLUDED_TIME_MEASURE_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.cpp b/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.cpp
new file mode 100644
index 00000000..639e61d2
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.cpp
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TimeToolbar.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "BlankControl.h"
+#include "IDoc.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimeToolbar::CTimeToolbar(IDoc *inDoc)
+{
+ m_Doc = inDoc;
+ m_TimeEdit = new CTimeEdit(inDoc);
+ m_Color = CStudioPreferences::GetBaseColor();
+ m_TimeEdit->SetBackgroundColor(m_Color);
+ AddChild(m_TimeEdit);
+
+ m_TimeEdit->AddTimeChangeListener(this);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimeToolbar::~CTimeToolbar()
+{
+ delete m_TimeEdit;
+}
+
+//=============================================================================
+/**
+ * Fills in the background color for this layout.
+ */
+void CTimeToolbar::Draw(CRenderer *inRenderer)
+{
+ // Fill in the background color and draw the child controls
+
+ // Draw the shadow lines at the top and bottom of the layout
+ CRct theRect(GetSize());
+ inRenderer->FillSolidRect(theRect, m_Color);
+
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(theRect.size.x, 0));
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theRect.size.y - 2));
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 2));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 2));
+ inRenderer->PopPen();
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y - 1));
+ inRenderer->LineTo(CPt(theRect.size.x, theRect.size.y - 1));
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * Override of Control's set size to reposition the TimeEdit.
+ * @param inSize the new size.
+ */
+void CTimeToolbar::SetSize(CPt inSize)
+{
+ m_TimeEdit->SetSize(CPt(m_TimeEdit->GetWidth(), inSize.y - 4));
+ m_TimeEdit->SetPosition(inSize.x - m_TimeEdit->GetWidth(), 2);
+
+ CControl::SetSize(inSize);
+}
+
+//=============================================================================
+/**
+ * Call from the TimelineView (or thereabouts) that the scene time changed.
+ * @param inTime the new time.
+ */
+void CTimeToolbar::SetTime(long inTime)
+{
+ m_TimeEdit->SetTime(inTime);
+}
+//=============================================================================
+/**
+ * Returns the playhead time
+ */
+long CTimeToolbar::GetTime()
+{
+ return m_TimeEdit->GetTime();
+}
+
+//=============================================================================
+/**
+ * Callback from the TimeEdit that it's time was manually changed.
+ * @param inNewTime the new time.
+ */
+void CTimeToolbar::OnTimeChanged(long inNewTime)
+{
+ m_Doc->NotifyTimeChanged(inNewTime);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.h b/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.h
new file mode 100644
index 00000000..c1ed948d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimeToolbar.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TIME_TOOLBAR_H
+#define INCLUDED_TIME_TOOLBAR_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TimeEdit.h"
+#include "Control.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+class IDoc;
+
+//=============================================================================
+/**
+ * Control at the top of the time display and a header for the toggle column.
+ */
+class CTimeToolbar : public CControl, public CTimeEditChangeListener
+{
+public:
+ CTimeToolbar(IDoc *inDoc);
+ virtual ~CTimeToolbar();
+ void Draw(CRenderer *inRenderer) override;
+
+ void SetSize(CPt inSize) override;
+ void SetTime(long inTime);
+ virtual long GetTime();
+
+ void OnTimeChanged(long inNewTime) override;
+
+protected:
+ CTimeEdit *m_TimeEdit;
+ IDoc *m_Doc;
+ CColor m_Color;
+};
+
+#endif // INCLUDED_TIME_TOOLBAR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimebarControl.cpp b/src/Authoring/Studio/Palettes/Timeline/TimebarControl.cpp
new file mode 100644
index 00000000..af0084aa
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimebarControl.cpp
@@ -0,0 +1,690 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "TimebarControl.h"
+#include "StateTimebarRow.h"
+#include "StateRow.h"
+#include "Renderer.h"
+#include "ColorControl.h"
+#include "StudioPreferences.h"
+#include "Views.h"
+#include "TimelineControl.h"
+#include "TimelineTimelineLayout.h"
+#include "ResourceCache.h"
+#include "HotKeys.h"
+#include "Preferences.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "Bindings/ITimelineTimebar.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "CoreUtils.h"
+#include "StudioUtils.h"
+#include "MasterP.h"
+
+const float SCALING_FACTOR = 0.50;
+
+//=============================================================================
+/**
+ * Create a timebar control on the specified state timebar row.
+ * Attaches a new ToolTip to it that displays the time range this timebar control
+ * encompasses.
+ * @param inRow the row on which this timebar is attached.
+ */
+CTimebarControl::CTimebarControl(CStateTimebarRow *inRow,
+ ITimelineItemBinding *inTimelineItemBinding)
+ : m_IsSelected(false)
+ , m_IsMouseDown(false)
+ , m_MaybeDragStart(false)
+ , m_LeftLeftTip(this, true)
+ , m_LeftTip(this, true)
+ , m_RightTip(this, false)
+ , m_RightRightTip(this, false)
+ , m_SnappingListProvider(nullptr)
+{
+ m_TimebarRow = inRow;
+ m_TimelineItemBinding = inTimelineItemBinding;
+ // Start/End times
+ ITimelineTimebar *theTimelineTimebar = GetTimebar();
+ m_StartTime = theTimelineTimebar->GetStartTime();
+ m_EndTime = theTimelineTimebar->GetEndTime();
+ bool theShowHandleBars = theTimelineTimebar->ShowHandleBars();
+ m_LeftLeftTip.ShowHandles(theShowHandleBars);
+ m_RightRightTip.ShowHandles(theShowHandleBars);
+
+ m_LeftLeftTip.SetSize(CStudioPreferences::GetTimebarTipSize(),
+ CStudioPreferences::GetRowSize());
+ m_LeftTip.SetPosition(CPt(0, 0));
+ m_LeftTip.SetSize(CStudioPreferences::GetTimebarInnerTipSize(),
+ CStudioPreferences::GetRowSize());
+ m_LeftTip.SetPosition(CPt(m_LeftLeftTip.GetSize().x, 0));
+
+ m_RightTip.SetSize(CStudioPreferences::GetTimebarInnerTipSize(),
+ CStudioPreferences::GetRowSize());
+ m_RightRightTip.SetSize(CStudioPreferences::GetTimebarTipSize(),
+ CStudioPreferences::GetRowSize());
+
+ m_EditControl = new CCommentEdit(theTimelineTimebar);
+ m_EditControl->SetPosition(CStudioPreferences::GetTimebarTipSize() * 2, 1);
+ m_EditControl->SetSize(CPt(100, 15));
+ m_EditControl->SetFillBackground(false);
+ AddChild(m_EditControl);
+ AddChild(&m_LeftTip);
+ AddChild(&m_RightTip);
+ AddChild(&m_RightRightTip);
+ AddChild(&m_LeftLeftTip);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimebarControl::~CTimebarControl()
+{
+ delete m_EditControl;
+}
+
+//=============================================================================
+/**
+ * Draw this timebar control to the renderer.
+ * @param inRenderer the renderer to draw to.
+ */
+void CTimebarControl::Draw(CRenderer *inRenderer)
+{
+ CStateRow *theRow = m_TimebarRow->GetStateRow();
+ CRct theRect(GetSize());
+
+ ::CColor theNormalColor = GetTimebar()->GetTimebarColor();
+ ::CColor theSelectedColor = CColorControl::CalculateSelectedColor(theNormalColor);
+
+ ::CColor theBorderColor = CStudioPreferences::GetTimeBarBorderColor();
+ ::CColor theDarkExtendedColor = CStudioPreferences::GetExtendedObjectDarkColor();
+ ::CColor theLightExtendedColor = CStudioPreferences::GetExtendedObjectLightColor();
+
+ long theTipOffset = CStudioPreferences::GetTimebarTipSize();
+
+ if (!IsEnabled()) {
+ theNormalColor = CStudioPreferences::GetLockedTimebarColor();
+ theBorderColor = CStudioPreferences::GetLockedBorderColor();
+ theDarkExtendedColor = CStudioPreferences::GetExtendedLockedDarkColor();
+ theLightExtendedColor = CStudioPreferences::GetExtendedLockedLightColor();
+ }
+
+ // Calculate the start/end/activestart
+ long theObjectLifeStart = ::TimeToPos(m_StartTime, m_TimeRatio);
+ long theStartPos = ::TimeToPos(theRow->GetActiveStart(), m_TimeRatio) - theObjectLifeStart;
+ long theEndPos = ::TimeToPos(theRow->GetActiveEnd(), m_TimeRatio) - theObjectLifeStart;
+ long theObjectLifeEnd = ::TimeToPos(m_EndTime, m_TimeRatio) - theObjectLifeStart;
+
+ CRct theGradientRct(theStartPos + theTipOffset, 0, theEndPos - theStartPos, theRect.size.y - 1);
+
+ if (theEndPos > theStartPos) {
+ inRenderer->DrawGradientBitmap(theGradientRct, theNormalColor, 0, SCALING_FACTOR);
+ // Calculate the gradient rect a bit differently depending on selection
+ if (m_IsSelected) {
+ CRct theSelectedRct(CPt(theGradientRct.position.x, theGradientRct.position.y + 3),
+ CPt(theGradientRct.size.x, theGradientRct.size.y - 7));
+ inRenderer->FillSolidRect(theSelectedRct, theSelectedColor);
+ }
+ }
+
+ inRenderer->PushPen(theBorderColor);
+ // Check to see if we need some hashes at the end
+ if (theObjectLifeEnd > theEndPos) {
+ long theUpdatedStartTime = theEndPos;
+ if (theStartPos > theUpdatedStartTime)
+ theUpdatedStartTime = theStartPos;
+ else {
+ inRenderer->MoveTo(theEndPos + theTipOffset, 0);
+ inRenderer->LineTo(theEndPos + theTipOffset, theRect.size.y - 1);
+ }
+ CRct theClippingRect(CPt(theUpdatedStartTime + theTipOffset + 1, 0),
+ CPt(theObjectLifeEnd - theUpdatedStartTime - 1, theRect.size.y - 1));
+ inRenderer->PushClippingRect(theClippingRect);
+
+ // Draw the hashed background
+ DrawHashedBackgroundX(inRenderer, theDarkExtendedColor, theLightExtendedColor,
+ theClippingRect);
+ inRenderer->PopClippingRect();
+ }
+
+ // Check to see if we need some hashes at the beginning
+ if (theStartPos > 0) {
+ long theUpdatedEndTime = theStartPos;
+ if (theObjectLifeEnd < theUpdatedEndTime)
+ theUpdatedEndTime = theObjectLifeEnd;
+ else {
+ inRenderer->MoveTo(theStartPos + theTipOffset, 0);
+ inRenderer->LineTo(theStartPos + theTipOffset, theRect.size.y - 1);
+ }
+ CRct theClippingRect(CPt(theTipOffset, 0), CPt(theUpdatedEndTime, theRect.size.y - 1));
+ inRenderer->PushClippingRect(theClippingRect);
+
+ // Draw the hashed background
+ DrawHashedBackgroundX(inRenderer, theDarkExtendedColor, theLightExtendedColor,
+ theClippingRect);
+ inRenderer->PopClippingRect();
+ }
+
+ // Draw the border stuff
+ inRenderer->MoveTo(CPt(theTipOffset, 0));
+ inRenderer->LineTo(CPt(theTipOffset, theRect.size.y - 1));
+ inRenderer->MoveTo(CPt(theObjectLifeEnd + theTipOffset, 0));
+ inRenderer->LineTo(CPt(theObjectLifeEnd + theTipOffset, theRect.size.y - 1));
+
+ inRenderer->PopPen();
+ // Setting the position with the active time
+ m_EditControl->SetPosition(CStudioPreferences::GetTimebarTipSize() * 2, 1);
+}
+
+//=============================================================================
+/**
+ * Draws a hashed background in a given clipping rect
+ *
+ * @param inStartX the x position to start from
+ * @param inSizeY the y size the you want the lines to range from
+ * @param inEndX one after the last place where lines can be drawn from
+ * @inRenderer the renderer to draw to
+ * @param inFirstColor the first hash color
+ * @param inSecondColor the second hash color
+ * @para inRect the clipping rect
+ */
+void CTimebarControl::DrawHashedBackgroundX(CRenderer *inRenderer, ::CColor inFirstColor,
+ ::CColor inSecondColor, CRct inRect)
+{
+ inRenderer->FillSolidRect(inRect, inFirstColor);
+ if (m_IsSelected) {
+ CRct theSelectedRct(CPt(inRect.position.x, inRect.position.y + 4),
+ CPt(inRect.size.x, inRect.size.y - 8));
+ inRenderer->FillSolidRect(
+ theSelectedRct, CColorControl::CalculateSelectedColor(GetTimebar()->GetTimebarColor()));
+ }
+
+ inRenderer->FillHashed(inRect, inSecondColor);
+}
+
+//=============================================================================
+/**
+ * Set the current time ratio.
+ * The time ratio controls the length of this control and is the ratio of
+ * pixels to milliseconds.
+ * @param inTimeRatio the new time ratio.
+ */
+void CTimebarControl::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+
+ Refresh();
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * @param inSize the new size of this control.
+ */
+void CTimebarControl::SetSize(CPt inSize)
+{
+ CControl::SetSize(CPt(inSize.x, inSize.y));
+
+ CStateRow *theRow = m_TimebarRow->GetStateRow();
+ long theTipSize =
+ CStudioPreferences::GetTimebarTipSize() + CStudioPreferences::GetTimebarInnerTipSize();
+ long theCommentSize = CStudioPreferences::GetDefaultCommentSize();
+ if (inSize.x < theCommentSize)
+ theCommentSize = inSize.x;
+
+ // Recalculate the comment size depending on where the timebar is and how large it is
+ long theDiff = ::dtol((theRow->GetActiveEnd() - theRow->GetActiveStart()) * m_TimeRatio);
+ if (theDiff < theCommentSize) {
+ theCommentSize = theDiff - theTipSize;
+ if (theCommentSize < 0)
+ theCommentSize = 0;
+ }
+
+ m_EditControl->SetSize(CPt(theCommentSize, 15));
+
+ // Set the two right tips depending on where the right side is
+ m_RightTip.SetPosition(CPt(inSize.x - theTipSize, 0));
+ m_RightRightTip.SetPosition(CPt(inSize.x - m_RightRightTip.GetSize().x + 1, 0));
+}
+
+//=============================================================================
+/**
+ * Set whether this control is selected or not.
+ * If this is selected then it will modify how this control looks.
+ * @param inIsSelected true if this control is to be selected.
+ */
+void CTimebarControl::SetSelected(bool inIsSelected)
+{
+ if (inIsSelected != m_IsSelected) {
+ m_IsSelected = inIsSelected;
+ m_EditControl->SetSelected(m_IsSelected);
+ Invalidate();
+ }
+}
+
+void CTimebarControl::RefreshMetaData()
+{
+ m_EditControl->RefreshMetaData();
+}
+
+//=============================================================================
+/**
+ * Request for this control to refresh it's properties.
+ * This checks the size of the asset and adjusts it's size the the asset's
+ * length. Called when the time ratio or properties have changed.
+ * If the time has changed then Refresh( long, long ) must be called with the
+ * new times.
+ */
+void CTimebarControl::Refresh()
+{
+ Refresh(m_StartTime, m_EndTime);
+}
+
+//=============================================================================
+/**
+ * Request for this control to refresh it's properties.
+ * This updates all the properties of this control and resize it as necessary.
+ * Called when the time changes on the asset, the time ratio changes or any
+ * properties that this displays change.
+ * @param inStartTime the asset's start time.
+ * @param inEndTime the asset's end time.
+ */
+void CTimebarControl::Refresh(long inStartTime, long inEndTime)
+{
+ m_StartTime = inStartTime;
+ m_EndTime = inEndTime;
+
+ long thePosition = ::TimeToPos(inStartTime, m_TimeRatio);
+ long theSize = ::dtol((inEndTime - inStartTime) * m_TimeRatio);
+
+ SetPosition(thePosition - CStudioPreferences::GetTimebarTipSize(), GetPosition().y);
+
+ SetSize(CPt(theSize + 2 * CStudioPreferences::GetTimebarTipSize(), GetMinimumSize().y));
+ if (IsInvalidated())
+ m_TimebarRow->Invalidate();
+}
+
+//=============================================================================
+/**
+ * Get the interface to the timebar item in the data model
+ */
+ITimelineTimebar *CTimebarControl::GetTimebar()
+{
+ return m_TimelineItemBinding->GetTimelineItem()->GetTimebar();
+}
+
+//=============================================================================
+/**
+* Updates the ToolTip and moves it to the correct place on screen.
+* @param inPoint the point that the tooltip is supposed to be placed.
+*/
+void CTimebarControl::RefreshToolTip(CPt inPoint)
+{
+ Q3DStudio::CString theCommentText;
+ CStateRow *theRow = m_TimebarRow->GetStateRow();
+
+ CRct theTimelineBounds(GetTopControlBounds());
+ // format label as: startTime - endTime (timeDifference)
+ theCommentText = " " + FormatTimeString(theRow->GetStartTime()) + " - "
+ + FormatTimeString(theRow->GetEndTime()) + " ("
+ + FormatTimeString(theRow->GetEndTime() - theRow->GetStartTime()) + ")";
+ inPoint.y = GetPosition().y - GetSize().y;
+ ShowMoveableWindow(inPoint, theCommentText, theTimelineBounds);
+}
+
+//=============================================================================
+/**
+ * OnMouseDoubleClick: Pop up a dialog box for the editing of the timebar start
+ * and end time.
+ */
+bool CTimebarControl::OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDoubleClick(inPoint, inFlags)
+ && !m_TimelineItemBinding->IsLockedEnabled()) {
+ CTimeEditDlg theTimeEditDlg;
+ theTimeEditDlg.ShowDialog(m_StartTime, m_EndTime, g_StudioApp.GetCore()->GetDoc(), TIMEBAR,
+ this);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Allows this timebar control to add any times it wishes to the snapper list
+ * @param inSnapper the Snapper that is handling the snapping functions for this timebar
+ */
+void CTimebarControl::PopulateSnappingList(CSnapper *inSnapper)
+{
+ Q_UNUSED(inSnapper);
+}
+
+//=============================================================================
+/**
+ * Start drag handler, puts this control into drag mode.
+ * @param inPoint the point where the mouse was clicked.
+ * @param inFlags the mouse state.
+ */
+bool CTimebarControl::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_IsMouseDown = true;
+ m_MaybeDragStart = true;
+ m_MouseDownLoc = inPoint;
+
+ OnBeginDrag();
+
+ m_TimebarRow->GetStateRow()->Select(SBaseStateRowSelectionKeyState());
+
+ m_Snapper.Clear();
+ m_Snapper.SetSource(this);
+
+ GetSnappingListProvider().PopulateSnappingList(&m_Snapper);
+ m_Snapper.BeginDrag(inPoint.x);
+
+ if (HasFocus(m_EditControl) && !m_EditControl->HitTest(inPoint)) {
+ m_EditControl->OnLoseFocus();
+ }
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+ return true;
+}
+
+//=============================================================================
+/**
+ * Puts up the context menu.
+ * @param inPoint the point where the mouse was clicked.
+ * @param inFlags the mouse state.
+ */
+bool CTimebarControl::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseRDown(inPoint, inFlags)) {
+ if (m_IsMouseDown) {
+ m_IsMouseDown = false;
+ CommitTimeChange();
+ HideMoveableWindow();
+ }
+ // only right-clicking ON the timebar will show the timebar (text and color) properties'
+ // options
+ ShowContextMenu(inPoint, true);
+ }
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Notification that the drag has finished.
+ * @param inPoint the point where the mouse was let go.
+ * @param inFlags the state of the mouse.
+ */
+void CTimebarControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // try to prevent stuck mousetips on exceptions
+ try {
+ CControl::OnMouseUp(inPoint, inFlags);
+ CommitTimeChange();
+ } catch (...) {
+ }
+ m_IsMouseDown = false;
+ m_MaybeDragStart = false;
+ HideMoveableWindow();
+}
+
+//=============================================================================
+/**
+ * Handler for the mouse move messages.
+ * If the mouse is down then this will drag the control and offset the timebar.
+ * @param inPoint the current location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CTimebarControl::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ bool theCommentEditMode = m_EditControl->GetEditMode();
+
+ // If we are in edit Comment mode or locked, then we do not drag the timebar.
+ if (!theCommentEditMode && m_IsMouseDown && !m_TimelineItemBinding->IsLockedEnabled()) {
+ UICPROFILE(OnMouseMove);
+
+ if (m_MaybeDragStart) {
+ // Dragging in the first 5 pixels will be ignored to avoid unconsciously accidental
+ // moves
+ CPt theDragDistance = inPoint - m_MouseDownLoc;
+ if (theDragDistance.x * theDragDistance.x + theDragDistance.y * theDragDistance.y <= 25)
+ return;
+
+ m_MaybeDragStart = false;
+ }
+
+ long theNewTime = m_Snapper.ProcessDrag(m_StartTime, inPoint.x, inFlags);
+ if (theNewTime < 0)
+ theNewTime = 0;
+ long theDiffTime = theNewTime - m_StartTime;
+
+ if (theDiffTime) {
+ GetTimebar()->OffsetTime(theDiffTime);
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Call from the left TimebarTab to resize the control.
+ * @param inTime the time to set the start time to.
+ */
+void CTimebarControl::ResizeTimebarLeftTo(long inTime)
+{
+ // TOOD: sk - Figure out what this does
+ // if ( inTime != 0 )
+ {
+ // The whole idea is to not do anything additional once times passes 0 (negatively)
+ // unless it is valid that time is negative on the timebar
+ if (inTime < 0 && m_StartTime > 0)
+ inTime = -m_StartTime; // so that it decrements to 0
+
+ if (m_StartTime > 0 || (m_StartTime == 0 && inTime > 0))
+ GetTimebar()->ChangeTime(inTime, true);
+ }
+}
+
+//=============================================================================
+/**
+ * Call from the right TimebarTab to resize the control.
+ * @param inTime the time to set the start time to.
+ */
+void CTimebarControl::ResizeTimebarRightTo(long inTime)
+{
+ GetTimebar()->ChangeTime(inTime, false);
+}
+
+//=============================================================================
+/**
+ * Sets the Actual string of text
+ * @param inText the text to set the comment text to
+ */
+void CTimebarControl::SetText(const Q3DStudio::CString &inText)
+{
+ m_EditControl->SetData(inText);
+}
+
+//=============================================================================
+/**
+ * Sets the Text Color on the edit control
+ * @param inColor the color
+ */
+void CTimebarControl::SetTextColor(::CColor inColor)
+{
+ m_EditControl->SetTextColor(inColor);
+}
+
+long CTimebarControl::GetStartTime()
+{
+ return m_StartTime;
+}
+
+long CTimebarControl::GetEndTime()
+{
+ return m_EndTime;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not this control is enabled.
+ * If the control is not enabled then it is still drawn and still intercepts
+ * mouse clicks, but it will not actually process them.
+ * @param inIsEnabled true if this control is to be enabled.
+ */
+void CTimebarControl::SetEnabled(bool inIsEnabled)
+{
+ CControl::SetEnabled(inIsEnabled);
+}
+
+//=============================================================================
+/**
+ * COMMENT!!!!!!!!!!!!!!!!!!!!!!
+ */
+void CTimebarControl::OnLoseFocus()
+{
+ if (m_IsMouseDown) {
+ m_IsMouseDown = false;
+ CommitTimeChange();
+ HideMoveableWindow();
+ }
+ CControl::OnLoseFocus();
+}
+
+//=============================================================================
+/**
+ * Setup prior to dragging.
+ */
+void CTimebarControl::OnBeginDrag()
+{
+ GetTimebar()->OnBeginDrag();
+}
+
+void CTimebarControl::ChangeStartTime(long inTime)
+{
+ ResizeTimebarLeftTo(inTime);
+}
+
+void CTimebarControl::ChangeEndTime(long inTime)
+{
+ ResizeTimebarRightTo(inTime);
+}
+
+void CTimebarControl::Commit()
+{
+ GetTimebar()->CommitTimeChange();
+}
+void CTimebarControl::Rollback()
+{
+ GetTimebar()->RollbackTimeChange();
+}
+
+void CTimebarControl::ShowContextMenu(CPt inPoint, bool inShowTimebarPropertiesOptions)
+{
+ CTimebarKeyframeContextMenu theMenu(this, m_TimelineItemBinding->GetKeyframesManager(),
+ inShowTimebarPropertiesOptions);
+ DoPopup(&theMenu, inPoint);
+}
+
+void CTimebarControl::CommitTimeChange()
+{
+ GetTimebar()->CommitTimeChange();
+}
+
+//=============================================================================
+/**
+ * The binding is a keyframes holder
+ */
+ITimelineItemKeyframesHolder *CTimebarControl::GetKeyframesHolder()
+{
+ return m_TimelineItemBinding;
+}
+
+//=============================================================================
+/**
+ * Start editing the timebar comment
+ */
+void CTimebarControl::OnEditTimeComment()
+{
+ GrabFocus(m_EditControl);
+ m_EditControl->DoChangeComment();
+}
+
+//=============================================================================
+/**
+ * Need to invalidate all timebars to redraw
+ */
+void CTimebarControl::OnToggleTimebarHandles()
+{
+ Invalidate();
+}
+
+void CTimebarControl::SetTimebarTime()
+{
+ GetTimebar()->SetTimebarTime(this);
+}
+
+::CColor CTimebarControl::GetTimebarColor()
+{
+ return GetTimebar()->GetTimebarColor();
+}
+
+void CTimebarControl::SetTimebarColor(const ::CColor &inColor)
+{
+ GetTimebar()->SetTimebarColor(inColor);
+}
+
+void CTimebarControl::SetSnappingListProvider(ISnappingListProvider *inProvider)
+{
+ m_SnappingListProvider = inProvider;
+}
+
+ISnappingListProvider &CTimebarControl::GetSnappingListProvider() const
+{
+ // sk - If you hit this, it means the setup order is incorrect. e.g. loading children is done
+ // depth first, ie your child's children is loaded before parent, doesn't work that way.
+ ASSERT(m_SnappingListProvider);
+ return *m_SnappingListProvider;
+}
+
+CRct CTimebarControl::GetTopControlBounds() const
+{
+ return m_TimebarRow->GetStateRow()->GetTopControl()->GetBounds();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimebarControl.h b/src/Authoring/Studio/Palettes/Timeline/TimebarControl.h
new file mode 100644
index 00000000..ec37d134
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimebarControl.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIMEBAR_CONTROL_H
+#define INCLUDED_TIMEBAR_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "Snapper.h"
+#include "CommentEdit.h"
+#include "TimebarTip.h"
+#include "TimeEditDlg.h"
+#include "KeyframeContextMenu.h"
+
+class CStateTimebarRow;
+class ITimelineItemBinding;
+class ISnappingListProvider;
+
+//=============================================================================
+/**
+ * Interface to a timebar control
+ */
+class ITimebarControl
+{
+public:
+ virtual ~ITimebarControl() {}
+
+ virtual ITimelineItemKeyframesHolder *GetKeyframesHolder() = 0;
+ virtual void OnEditTimeComment() = 0;
+ virtual void OnToggleTimebarHandles() = 0;
+ virtual void SetTimebarTime() = 0;
+ virtual ::CColor GetTimebarColor() = 0;
+ virtual void SetTimebarColor(const ::CColor &inColor) = 0;
+};
+
+class CTimebarControl : public CControl, public ITimeChangeCallback, public ITimebarControl
+{
+public:
+ CTimebarControl(CStateTimebarRow *inRow, ITimelineItemBinding *inTimelineItemBinding);
+ virtual ~CTimebarControl();
+ void Draw(CRenderer *inRenderer) override;
+ void SetSize(CPt inSize) override;
+ void SetSelected(bool inIsSelected);
+ void SetTimeRatio(double inTimeRatio);
+ void Refresh();
+ void Refresh(long inStartTime, long inEndTime);
+ void RefreshMetaData();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseDoubleClick(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void ResizeTimebarLeftTo(long inTime);
+ void ResizeTimebarRightTo(long inTime);
+ void SetText(const Q3DStudio::CString &inText);
+ void SetTextColor(::CColor inColor = ::CColor(0, 0, 0));
+ void SetEnabled(bool inIsEnabled) override;
+
+ long GetStartTime();
+ long GetEndTime();
+
+ virtual void PopulateSnappingList(CSnapper *inSnapper);
+ void OnLoseFocus() override;
+
+ void OnBeginDrag();
+
+ // ITimeChangeCallback
+ void ChangeStartTime(long) override;
+ void ChangeEndTime(long) override;
+ void Commit() override;
+ void Rollback() override;
+
+ void ShowContextMenu(CPt inPoint, bool inShowTimebarPropertiesOptions);
+ void CommitTimeChange();
+
+ // ITimebarControl
+ ITimelineItemKeyframesHolder *GetKeyframesHolder() override;
+ void OnEditTimeComment() override;
+ void OnToggleTimebarHandles() override;
+ void SetTimebarTime() override;
+ ::CColor GetTimebarColor() override;
+ void SetTimebarColor(const ::CColor &inColor) override;
+
+ void SetSnappingListProvider(ISnappingListProvider *inProvider);
+ ISnappingListProvider &GetSnappingListProvider() const;
+
+ CRct GetTopControlBounds() const;
+
+protected:
+ ITimelineTimebar *GetTimebar();
+
+ void RefreshToolTip(CPt inPoint);
+ void DrawHashedBackgroundX(CRenderer *inRenderer, ::CColor inFirstColor, ::CColor inSecondColor,
+ CRct inRect);
+
+ CStateTimebarRow *m_TimebarRow;
+ bool m_IsSelected;
+ double m_TimeRatio;
+ bool m_IsMouseDown;
+ bool m_MaybeDragStart;
+ CPt m_MouseDownLoc;
+ long m_StartTime;
+ long m_EndTime;
+
+ CTimebarTip m_LeftLeftTip;
+ CTimebarTip m_LeftTip;
+
+ CTimebarTip m_RightTip;
+ CTimebarTip m_RightRightTip;
+ CCommentEdit *m_EditControl;
+ CSnapper m_Snapper;
+
+ ITimelineItemBinding *m_TimelineItemBinding;
+ ISnappingListProvider *m_SnappingListProvider;
+};
+#endif // INCLUDED_TIMEBAR_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimebarTip.cpp b/src/Authoring/Studio/Palettes/Timeline/TimebarTip.cpp
new file mode 100644
index 00000000..9968a838
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimebarTip.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TimebarTip.h"
+#include "TimebarControl.h"
+#include "MouseCursor.h"
+#include "TimelineControl.h"
+#include "ResourceCache.h"
+#include "Renderer.h"
+#include "StudioUtils.h"
+
+#include <QApplication>
+
+//=============================================================================
+/**
+ * Create a timebar tip for the timebar.
+ * This handles displaying the resize cursor and processing the mouse commands.
+ * @param inTimebar the timebar on which this tip is attached.
+ * @param inIsLeft true if this is the left timebar tip.
+ */
+CTimebarTip::CTimebarTip(CTimebarControl *inTimebar, bool inIsLeft, bool inHasHandle /*=false*/)
+ : m_IsMouseDown(false)
+ , m_MaybeDragStart(false)
+ , m_HasHandle(false)
+{
+ m_Timebar = inTimebar;
+ m_IsLeft = inIsLeft;
+
+ ShowHandles(inHasHandle);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimebarTip::~CTimebarTip()
+{
+}
+
+//=============================================================================
+/**
+* Updates the ToolTip and moves it to the correct place on screen.
+* @param inPoint the point that the tooltip is supposed to be placed.
+*/
+void CTimebarTip::RefreshToolTip(CPt inPoint)
+{
+ Q3DStudio::CString theCommentText;
+
+ // format label as: startTime - endTime (timeDifference)
+ theCommentText = " " + FormatTimeString(m_Timebar->GetStartTime()) + " - "
+ + FormatTimeString(m_Timebar->GetEndTime()) + " ("
+ + FormatTimeString(m_Timebar->GetEndTime() - m_Timebar->GetStartTime()) + ")";
+
+ CRct theTimelineBounds(m_Timebar->GetTopControlBounds());
+ inPoint.y = GetPosition().y - GetSize().y;
+ ShowMoveableWindow(inPoint, theCommentText, theTimelineBounds);
+}
+
+//=============================================================================
+/**
+ * Starts the dragging of the timebar tip.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+bool CTimebarTip::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseDown(inPoint, inFlags);
+
+ m_Timebar->OnBeginDrag();
+
+ m_Snapper.Clear();
+ m_Snapper.SetSource(m_Timebar);
+ m_Timebar->GetSnappingListProvider().PopulateSnappingList(&m_Snapper);
+ m_Snapper.BeginDrag(inPoint.x);
+
+ m_IsMouseDown = true;
+ m_MaybeDragStart = true;
+ m_MouseDownLoc = inPoint;
+
+ setCursorIfNotSet(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+
+ return true;
+}
+
+//=============================================================================
+/**
+ * Ends the dragging of the tip and commits the commands.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CTimebarTip::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // try to prevent stuck mousetips on exceptions
+ try {
+ CControl::OnMouseUp(inPoint, inFlags);
+
+ // Commit the current command so it will not be merged with drag commands if this gets
+ // dragged again.
+ m_Timebar->CommitTimeChange();
+ } catch (...) {
+ }
+
+ m_IsMouseDown = false;
+ m_MaybeDragStart = false;
+ HideMoveableWindow();
+ resetCursor();
+}
+
+//=============================================================================
+/**
+ * If the mouse is down then this handles the resizing of the timebar.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse.
+ */
+void CTimebarTip::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ // Don't show the cursor if the mouse is down from someone else.
+ if (!(inFlags & CHotKeys::MOUSE_RBUTTON) && !(inFlags & CHotKeys::MOUSE_LBUTTON))
+ setCursorIfNotSet(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT);
+
+ if (m_IsMouseDown) {
+ if (m_MaybeDragStart) {
+ // Dragging in the first 5 pixels will be ignored to avoid unconsciously accidental
+ // moves
+ CPt theDragDistance = inPoint - m_MouseDownLoc;
+ if (theDragDistance.x * theDragDistance.x + theDragDistance.y * theDragDistance.y <= 25)
+ return;
+
+ m_MaybeDragStart = false;
+ }
+
+ // Figure out which method to call based on which tip we are.
+ if (m_IsLeft) {
+ long theNewTime = m_Snapper.ProcessDrag(m_Timebar->GetStartTime(), inPoint.x, inFlags);
+ m_Timebar->ResizeTimebarLeftTo(theNewTime);
+ } else {
+ long theNewTime = m_Snapper.ProcessDrag(m_Timebar->GetEndTime(), inPoint.x, inFlags);
+ m_Timebar->ResizeTimebarRightTo(theNewTime);
+ }
+
+ // display the time range tooltip
+ RefreshToolTip(inPoint);
+ }
+}
+
+//=============================================================================
+/**
+ * Resets the cursor back to normal.
+ * @param inPoint the location of the mouse.
+ * @param inFlags the state of the mouse/modifier buttons.
+ */
+void CTimebarTip::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+
+ resetCursor();
+}
+
+//=============================================================================
+/**
+ * Draws timebar handles if necessary.
+ */
+void CTimebarTip::Draw(CRenderer *inRenderer)
+{
+ if (m_HasHandle) { // to show or not is based on Studio preferences
+ bool theShowHandle =
+ CPreferences::GetUserPreferences("Timeline").GetValue("ShowTimebarHandles", false);
+ if (theShowHandle) {
+ if (IsEnabled())
+ inRenderer->DrawBitmap(CPt(0, 0), m_HandleImage);
+ else
+ inRenderer->DrawBitmap(CPt(0, 0), m_HandleDisabledImage);
+ }
+ }
+}
+
+void CTimebarTip::ShowHandles(bool inShowHandles)
+{
+ m_HasHandle = inShowHandles;
+
+ // If this tip can have a handle
+ if (m_HasHandle) {
+ if (!m_HandleImage) {
+ // If this is a tip on the left side, load the images for the left side
+ const char *theBitMap =
+ (m_IsLeft) ? "timebarhandle-left.png" : "timebarhandle-right.png";
+ m_HandleImage = CResourceCache::GetInstance()->GetBitmap(theBitMap);
+ }
+
+ if (!m_HandleDisabledImage) {
+ const char *theBitMap =
+ (m_IsLeft) ? "timebarhandle-disabled-left.png" : "timebarhandle-disabled-right.png";
+ m_HandleDisabledImage = CResourceCache::GetInstance()->GetBitmap(theBitMap);
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimebarTip.h b/src/Authoring/Studio/Palettes/Timeline/TimebarTip.h
new file mode 100644
index 00000000..4aca03a7
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimebarTip.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TIMEBAR_TIP
+#define INCLUDED_TIMEBAR_TIP 1
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "Snapper.h"
+
+#include <QCursor>
+#include <QPixmap>
+//==============================================================================
+// Forwards
+//==============================================================================
+class CTimebarControl;
+class CRenderer;
+
+//==============================================================================
+/**
+ * Class for the tips of timebar controls. Allows the user to resize timebars
+ * by grabbing the tips.
+ */
+class CTimebarTip : public CControl
+{
+public:
+ CTimebarTip(CTimebarControl *inTimebarControl, bool inIsLeft, bool inHasHandle = false);
+ virtual ~CTimebarTip();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void Draw(CRenderer *inRenderer) override;
+
+ void ShowHandles(bool inShowHandles);
+
+protected:
+ void RefreshToolTip(CPt inPoint);
+
+ CTimebarControl *m_Timebar;
+ bool m_IsMouseDown;
+ bool m_MaybeDragStart;
+ CPt m_MouseDownLoc;
+ bool m_IsLeft;
+ bool m_HasHandle;
+ QPixmap m_HandleImage;
+ QPixmap m_HandleDisabledImage;
+ CSnapper m_Snapper;
+};
+
+#endif // INCLUDED_TIMEBAR_TIP
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineControl.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineControl.cpp
new file mode 100644
index 00000000..9e0f4303
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineControl.cpp
@@ -0,0 +1,587 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TimelineControl.h"
+#include "TimelineSplitter.h"
+#include "StudioApp.h"
+#include "Dispatch.h"
+#include "TimelineTreeLayout.h"
+#include "TimelineTimelineLayout.h"
+#include "SlideRow.h"
+#include "IDoc.h"
+#include "InsertionLine.h"
+#include "InsertionOverlay.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "BreadCrumbControl.h"
+#include "BaseTimelineTreeControl.h"
+#include "Bindings/TimelineTranslationManager.h"
+#include "Doc.h"
+#include "Core.h"
+#include "MasterP.h"
+
+// Data model specific
+#include "TimelineDropTarget.h"
+
+#include "ClientDataModelBridge.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMSlides.h"
+
+IMPLEMENT_OBJECT_COUNTER(CTimelineControl)
+
+CTimelineControl::CTimelineControl()
+ : m_SuspendRecalcLayout(false)
+ , m_TranslationManager(nullptr)
+{
+ ADDTO_OBJECT_COUNTER(CTimelineControl)
+
+ m_TranslationManager = new CTimelineTranslationManager();
+
+ m_Splitter = new CTimelineSplitter();
+ AddChild(m_Splitter);
+
+ CDoc *theDoc = g_StudioApp.GetCore()->GetDoc();
+ m_TreeLayout = new CTimelineTreeLayout(this, theDoc);
+ m_Splitter->AddChild(m_TreeLayout);
+
+ m_TimelineLayout = new CTimelineTimelineLayout(this, theDoc);
+ m_Splitter->AddChild(m_TimelineLayout);
+
+ m_Splitter->SetSplitDirection(CSplitter::SPLIT_VERTICAL);
+ m_Splitter->SetSplitLocation(CStudioPreferences::GetTimelineSplitterLocation());
+
+ CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch();
+ theDispatch->AddPresentationChangeListener(this);
+ theDispatch->AddClientPlayChangeListener(this);
+
+ // Insertion line
+ m_InsertionLine = new CInsertionLine();
+ m_InsertionLine->SetName("TimelineInsertionLine");
+ AddChild(m_InsertionLine);
+
+ // Insertion overlay marker
+ m_InsertionOverlay = new CInsertionOverlay();
+ m_InsertionOverlay->SetName("TimelineInsertionOverlay");
+ AddChild(m_InsertionOverlay);
+
+ m_Splitter->SetPosition(CPt(0, CStudioPreferences::GetHeaderHeight()));
+
+ m_BreadCrumbToolbar = new CBreadCrumbControl();
+ AddChild(m_BreadCrumbToolbar);
+
+ SetPreferredSize(CPt(400, 200));
+}
+
+CTimelineControl::~CTimelineControl()
+{
+ CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch();
+ theDispatch->RemovePresentationChangeListener(this);
+ theDispatch->RemoveClientPlayChangeListener(this);
+
+ delete m_InsertionOverlay;
+ delete m_InsertionLine;
+ delete m_TimelineLayout;
+ delete m_TreeLayout;
+ delete m_Splitter;
+ delete m_BreadCrumbToolbar;
+
+ REMOVEFROM_OBJECT_COUNTER(CTimelineControl)
+}
+
+//=============================================================================
+/**
+ * Returns the playhead time
+ */
+long CTimelineControl::GetTime()
+{
+ return m_TreeLayout->GetTime();
+}
+
+//=============================================================================
+/**
+ * Clear the contents of this view.
+ * This will empty out this view and leave it ready for inspecting other objects.
+ */
+void CTimelineControl::ClearView()
+{
+ m_TimelineLayout->ClearRows();
+ m_TreeLayout->ClearRows();
+ m_ActiveSlide = 0;
+
+ // clean out all previous translations, because the bindings are not guaranteed to be valid when
+ // switching from one slide to another.
+ m_TranslationManager->Clear();
+}
+
+//=============================================================================
+/**
+ * Populates this view with the provided state.
+ * This will set the state as being the root object on this view. ClearView
+ * should be called before this is called.
+ * The object will become the root object of this and will become the active
+ * root of the doc.
+ * @param inState the state to be viewed as the root asset.
+ */
+void CTimelineControl::ViewSlide(UICDM::CUICDMSlideHandle inSlide)
+{
+ m_ActiveSlide = inSlide;
+
+ UICDM::ISlideSystem *theSlideSystem = GetDoc()->GetStudioSystem()->GetSlideSystem();
+ UICDM::CUICDMInstanceHandle theSlideInstance = theSlideSystem->GetSlideInstance(inSlide);
+ CSlideRow *theSlideRow = new CSlideRow(m_TranslationManager->GetOrCreate(theSlideInstance));
+ theSlideRow->SetTimelineControl(this);
+
+ m_TreeLayout->AddRow(theSlideRow);
+ m_TimelineLayout->AddRow(theSlideRow);
+
+ // Since this would be loading the entire context's assets, fire the OnTimelineLayoutChange
+ // event just once.
+ SuspendLayoutChanges(true);
+ try {
+ theSlideRow->LoadChildren();
+ theSlideRow->Expand();
+ } catch (...) { // restore the 'states' before passing the exception up
+ SuspendLayoutChanges(false);
+ throw;
+ }
+ // Update breadcrumbs
+ m_BreadCrumbToolbar->RefreshTrail(m_TranslationManager->GetBreadCrumbProvider());
+
+ SuspendLayoutChanges(false);
+ OnLayoutChanged();
+}
+
+//=============================================================================
+/**
+ * Notification from the StudioFullSystem signal provider that a we have a new active slide.
+ * This will populate this view with the new context.
+ */
+void CTimelineControl::OnActiveSlide(UICDM::CUICDMSlideHandle inSlide)
+{
+ ClearView();
+ ViewSlide(inSlide);
+
+ double theStoredRatio = m_TimelineLayout->GetTimelineRatio(inSlide);
+ if (theStoredRatio != -1)
+ m_TimelineLayout->SetTimeRatio(theStoredRatio);
+ else
+ m_TimelineLayout->OnScalingReset();
+
+ m_TimelineLayout->RecalcLayout();
+}
+
+void CTimelineControl::OnNewPresentation()
+{
+ m_TranslationManager->OnNewPresentation();
+
+ // Register callback
+ UICDM::IStudioFullSystemSignalProvider *theSignalProvider =
+ GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider();
+ m_Connections.push_back(theSignalProvider->ConnectActiveSlide(
+ std::bind(&CTimelineControl::OnActiveSlide, this, std::placeholders::_3)));
+ m_Connections.push_back(theSignalProvider->ConnectSlideDeleted(
+ std::bind(&CTimelineControl::OnDeleteSlide, this, std::placeholders::_1)));
+ CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch();
+ m_Connections.push_back(theDispatch->ConnectSelectionChange(
+ std::bind(&CTimelineControl::OnSelectionChange, this, std::placeholders::_1)));
+}
+
+//=============================================================================
+/**
+ * Notification from the dispatch that the presentation is being closed.
+ * This will clear all the objects from this presentation.
+ */
+void CTimelineControl::OnClosingPresentation()
+{
+ ClearView();
+ m_TimelineLayout->ClearAllTimeRatios();
+ m_BreadCrumbToolbar->RefreshTrail(nullptr);
+
+ m_Connections.clear();
+}
+
+//=============================================================================
+/**
+ * Accessor for the root object being displayed in this view.
+ */
+UICDM::CUICDMSlideHandle CTimelineControl::GetActiveSlide()
+{
+ return m_ActiveSlide;
+}
+
+//=============================================================================
+/**
+ * Gets the timeline layout which is the portion of the timeline to the right
+ * of the splitter. The timeline layout contains the timebars.
+ */
+CTimelineTimelineLayout *CTimelineControl::GetTimelineLayout()
+{
+ return m_TimelineLayout;
+}
+
+//=============================================================================
+/**
+ * Gets the tree layout which is the portion of the timeline to the left
+ * of the splitter. The tree layout contains the tree controls for expanding
+ * rows in the timeline.
+ */
+CTimelineTreeLayout *CTimelineControl::GetTreeLayout()
+{
+ return m_TreeLayout;
+}
+
+//=============================================================================
+/**
+ * Notification from the dispatch that the presentation is going into play mode.
+ */
+void CTimelineControl::OnPlayStart()
+{
+}
+
+//=============================================================================
+/**
+ * Notification from the dispatch that the presentation is exiting play state.
+ */
+void CTimelineControl::OnPlayStop()
+{
+}
+
+//=============================================================================
+/**
+ * Notification from the dispatch that the time has changed.
+ * This is used to update the playhead location and view time.
+ * @param inNewTime the new time that this should display.
+ */
+void CTimelineControl::OnTimeChanged(long inNewTime)
+{
+ SetTime(inNewTime);
+}
+
+//==============================================================================
+// CSelectionChangeListener
+//==============================================================================
+void CTimelineControl::OnSelectionChange(Q3DStudio::SSelectedValue inNewSelectable)
+{
+ // testing for nullptr selection OR if the selected is not displayed in the timeline
+ bool theLoseFocus = !inNewSelectable.empty();
+ if (!theLoseFocus) {
+ Q3DStudio::SelectedValueTypes::Enum theSelectionType = inNewSelectable.getType();
+ // for now, its just UICDM objects
+ theLoseFocus = theSelectionType != Q3DStudio::SelectedValueTypes::Instance; // UICDM objects
+ }
+ if (theLoseFocus)
+ m_TreeLayout->OnLoseFocus();
+
+ GetTranslationManager()->OnSelectionChange(inNewSelectable);
+
+ // The drag&drop doesn't have any sort of callback after a drop
+ // so for now, this acts as a "event-trigger" after a drop ( because new items are always
+ // selcted after a drop )
+ HideInsertionMarkers();
+}
+
+//=============================================================================
+/**
+ * Callback when individual rows has affected the layout, such that the treelayout needs to be
+ * synchronized with the timelinelayout or vice versa.
+ */
+void CTimelineControl::OnLayoutChanged()
+{
+ if (m_SuspendRecalcLayout) // optimization where this is explicitly shutoff.
+ return;
+
+ m_TreeLayout->RecalcLayout();
+ m_TimelineLayout->OnTimelineLayoutChanged();
+}
+
+//=============================================================================
+/**
+ * typically for displaying tooltip
+ */
+CRct CTimelineControl::GetBounds() const
+{
+ return CRct(GetGlobalPosition(CPt(0, 0)), GetSize());
+}
+
+void CTimelineControl::HideTimelineMoveableTooltip()
+{
+ HideMoveableWindow();
+}
+
+//=============================================================================
+/**
+ * For snapping timebars/keyframes
+ */
+ISnappingListProvider *CTimelineControl::GetSnappingListProvider() const
+{
+ return m_TimelineLayout;
+}
+
+//=============================================================================
+/**
+ * Sets the current time as seen in this palette.
+ * This will update the Playhead time and the time view time.
+ * @param inNewTime the time to set on this.
+ */
+void CTimelineControl::SetTime(long inNewTime)
+{
+ m_TimelineLayout->SetTime(inNewTime);
+ m_TreeLayout->SetTime(inNewTime);
+}
+
+void CTimelineControl::HideInsertionMarkers()
+{
+ bool theInvalidate = false;
+ if (m_InsertionOverlay->IsVisible()) {
+ m_InsertionOverlay->SetVisible(false);
+ theInvalidate = true;
+ }
+ if (m_InsertionLine->IsVisible()) {
+ m_InsertionLine->SetVisible(false);
+ theInvalidate = true;
+ }
+ if (theInvalidate) {
+ m_TreeLayout->Invalidate();
+ Invalidate();
+ }
+}
+
+void CTimelineControl::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+
+ m_Splitter->SetSize(CPt(inSize.x, inSize.y));
+}
+
+//=============================================================================
+/**
+ * Scrolls both sides of the timeline along the y-axis so that they stay synced.
+ * @param inSource Scroller that generated the scroll messsage
+ * @param inPositionY New vertical scroll bar position
+ */
+void CTimelineControl::SetScrollPositionY(CScroller *inSource, long inPositionY)
+{
+ m_TreeLayout->SetScrollPositionY(inSource, inPositionY);
+ m_TimelineLayout->SetScrollPositionY(inSource, inPositionY);
+}
+
+//=============================================================================
+/**
+ * Override OnDraw to provide Timeline only draw profiling stats.
+ */
+void CTimelineControl::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation)
+{
+ UICPROFILE(OnDraw);
+ CControl::OnDraw(inRenderer, inDirtyRect, inIgnoreValidation);
+}
+
+//=============================================================================
+/**
+ * Fills the whole control with the base (gray) color, then other controls will
+ * draw on top of that.
+ * @param inRenderer renderer to draw to
+ */
+void CTimelineControl::Draw(CRenderer *inRenderer)
+{
+ const auto size = GetSize();
+ inRenderer->FillSolidRect(QRect(0, 0, size.x, size.y), CStudioPreferences::GetBaseColor());
+}
+
+//=============================================================================
+/**
+ * Overriden from CControl. We want to propagate keydown (specifically F2)
+ * messages to the selected row regardless if the control has focus or not.
+ */
+void CTimelineControl::OnGainFocus()
+{
+ CControl::OnGainFocus();
+
+ CBaseStateRow *theRow = m_TranslationManager->GetSelectedRow();
+ if (theRow)
+ theRow->SetFocus();
+}
+
+//=============================================================================
+/**
+ * Overridden to draw insertion lines
+ */
+CDropTarget *CTimelineControl::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags)
+{
+ CDropTarget *theDropTarget = CControl::FindDropCandidate(inMousePoint, inFlags);
+
+ bool theHideInsertionMarkers = true;
+ CTimeLineDropTarget *theTimelineDropTarget = nullptr;
+ if (theDropTarget
+ && (theTimelineDropTarget = dynamic_cast<CTimeLineDropTarget *>(theDropTarget))) {
+ CControl *theInsertionOverControl = theTimelineDropTarget->GetInsertionMarkerRow();
+ if (theInsertionOverControl) {
+ CRct theTreeRect = GetVisibleTreeLayoutArea();
+ EDROPDESTINATION theDropDest = theTimelineDropTarget->GetDestination();
+ switch (theDropDest) {
+ case EDROPDESTINATION_ABOVE:
+ case EDROPDESTINATION_BELOW: {
+ // the insertion line starts from the indent to the end of the row
+ long theIndent = theTimelineDropTarget->GetInsertionMarkerIndent();
+ if (theDropDest == EDROPDESTINATION_ABOVE)
+ m_InsertionLine->SetPosition(theInsertionOverControl->GetGlobalPosition(
+ CPt(theIndent, -GetPosition().y)));
+ else
+ m_InsertionLine->SetPosition(theInsertionOverControl->GetGlobalPosition(CPt(
+ theIndent, theInsertionOverControl->GetSize().y - 1 - GetPosition().y)));
+
+ long theWidth =
+ theTreeRect.size.x + theTreeRect.position.x - m_InsertionLine->GetPosition().x;
+ m_InsertionLine->SetLineWidth(theWidth);
+ m_InsertionLine->SetVisible(true);
+ m_InsertionOverlay->SetVisible(false);
+ } break;
+ case EDROPDESTINATION_ON: {
+ // insertion overlay spans the width of the row
+ m_InsertionOverlay->SetPosition(theInsertionOverControl->GetGlobalPosition(
+ CPt(theTreeRect.position.x, -GetPosition().y)));
+
+ long theWidth = theTreeRect.size.x + theTreeRect.position.x
+ - m_InsertionOverlay->GetPosition().x;
+ m_InsertionOverlay->SetWidth(theWidth);
+ m_InsertionOverlay->SetVisible(true);
+ m_InsertionLine->SetVisible(false);
+ } break;
+ }
+ theHideInsertionMarkers = false;
+ }
+ }
+ // not drawn
+ if (theHideInsertionMarkers)
+ HideInsertionMarkers();
+
+ return theDropTarget;
+}
+
+void CTimelineControl::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseOut(inPoint, inFlags);
+ HideInsertionMarkers();
+}
+
+void CTimelineControl::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+ HideInsertionMarkers();
+}
+
+//=============================================================================
+/**
+ * Gets the insertion line for the timeline. The insertion line should be used
+ * to indicate when you can drag-and-drop and item between two other items.
+ * Call SetVisible on this control to show/hide it.
+ * @return the insertion line control
+ */
+CInsertionLine *CTimelineControl::GetInsertionLine()
+{
+ return m_InsertionLine;
+}
+
+//=============================================================================
+/**
+ * Gets the insertion overlay marker for the timeline. This control should be
+ * used to indicate that you can drag-and-drop and object onto another item in
+ * the timeline. Call SetVisible on this control to show/hide it.
+ * @return the insertion overlay marker for the timeline
+ */
+CInsertionOverlay *CTimelineControl::GetInsertionOverlay()
+{
+ return m_InsertionOverlay;
+}
+
+//=============================================================================
+/**
+ * Fetches the bounding rect for the CTimelineTreeLayout section of the
+ * timeline. This is the section that contains toggles and text names of items
+ * in the timeline. The actual tree layout might be bigger than this rect
+ * specifies. This is because portions of the tree layout might be overlapped
+ * by other controls.
+ * @return rectangle describing visible area of the tree control
+ */
+CRct CTimelineControl::GetVisibleTreeLayoutArea()
+{
+ return m_TreeLayout->GetVisibleArea();
+}
+
+void CTimelineControl::RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler)
+{
+ m_TimelineLayout->RegisterGlobalKeyboardShortcuts(inShortcutHandler);
+}
+
+//=============================================================================
+/**
+ * event that takes place just before a save or export, on lose focus will commit changes
+ * in text boxes
+ */
+void CTimelineControl::OnSavingPresentation(const CUICFile *inNewPresentationFile)
+{
+ Q_UNUSED(inNewPresentationFile);
+ OnLoseFocus();
+}
+
+//=============================================================================
+/**
+ * Notification from the StudioFullSystem signal provider that a slide has been deleted.
+ */
+void CTimelineControl::OnDeleteSlide(UICDM::CUICDMSlideHandle inSlide)
+{
+ m_TimelineLayout->DeleteTimelineRatio(inSlide);
+}
+
+//==============================================================================
+/**
+ * When caller knows that there are 'batch' changes to the timeline layout,
+ * to prevent unnecessary calls to recalclayout
+ */
+void CTimelineControl::SuspendLayoutChanges(bool inSuspend)
+{
+ m_SuspendRecalcLayout = inSuspend;
+}
+
+CDoc *CTimelineControl::GetDoc()
+{
+ return g_StudioApp.GetCore()->GetDoc();
+}
+
+CClientDataModelBridge *CTimelineControl::GetBridge()
+{
+ return GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineControl.h b/src/Authoring/Studio/Palettes/Timeline/TimelineControl.h
new file mode 100644
index 00000000..470a10a5
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineControl.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_TIMELINE_CONTROL_H
+#define INCLUDED_TIMELINE_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Control.h"
+#include "DispatchListeners.h"
+#include "TimelineRow.h"
+#include "ITimelineControl.h"
+
+#include "UICDMHandles.h"
+#include "UICDMSignals.h"
+#include "SelectedValueImpl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CDoc;
+class CDropTarget;
+class CTimelineSplitter;
+class CTimelineTreeLayout;
+class CTimelineTimelineLayout;
+class CScroller;
+class CInsertionLine;
+class CInsertionOverlay;
+class CRenderer;
+class CHotKeys;
+class CBreadCrumbControl;
+class CTimelineTranslationManager;
+class CClientDataModelBridge;
+
+//==============================================================================
+// Classes
+//==============================================================================
+
+class CTimelineControl : public CControl,
+ public CPresentationChangeListener,
+ public CClientPlayChangeListener,
+ public ITimelineControl
+{
+public:
+ CTimelineControl();
+ ~CTimelineControl();
+
+ DEFINE_OBJECT_COUNTER(CTimelineControl)
+
+ // CControl
+ void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false) override;
+ void Draw(CRenderer *inRenderer) override;
+ void OnGainFocus() override;
+ CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+
+ // Presentation Change Listener
+ void OnNewPresentation() override;
+ void OnClosingPresentation() override;
+ void OnSavingPresentation(const CUICFile *inNewPresentationFile) override;
+
+ // ClientPlayChangeListener
+ void OnPlayStart() override;
+ void OnPlayStop() override;
+ void OnTimeChanged(long inNewTime) override;
+
+ // CSelectionChangeListener,
+ virtual void OnSelectionChange(Q3DStudio::SSelectedValue inNewSelectable);
+
+ // ITimelineControl
+ void OnLayoutChanged() override;
+ CRct GetBounds() const override;
+ void HideTimelineMoveableTooltip() override;
+ ISnappingListProvider *GetSnappingListProvider() const override;
+
+ void ClearView();
+ void ViewSlide(UICDM::CUICDMSlideHandle inSlide);
+ UICDM::CUICDMSlideHandle GetActiveSlide();
+
+ CTimelineTimelineLayout *GetTimelineLayout();
+ CTimelineTreeLayout *GetTreeLayout();
+
+ void SetSize(CPt inSize) override;
+ long GetTime();
+ void SetScrollPositionY(CScroller *inSource, long inPositionY);
+
+ CInsertionLine *GetInsertionLine();
+ CInsertionOverlay *GetInsertionOverlay();
+ CRct GetVisibleTreeLayoutArea();
+
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+
+ CTimelineTranslationManager *GetTranslationManager() const { return m_TranslationManager; }
+
+protected:
+ void SuspendLayoutChanges(bool inSuspend);
+ void SetTime(long inNewTime);
+
+ void HideInsertionMarkers();
+
+ // UICDM callbacks
+ void OnActiveSlide(UICDM::CUICDMSlideHandle inSlide);
+ void OnDeleteSlide(UICDM::CUICDMSlideHandle inSlide);
+
+ // Helper functions
+ inline CDoc *GetDoc();
+ inline CClientDataModelBridge *GetBridge();
+
+ CTimelineSplitter *m_Splitter;
+ CTimelineTreeLayout *m_TreeLayout;
+ CTimelineTimelineLayout *m_TimelineLayout;
+ UICDM::CUICDMSlideHandle m_ActiveSlide;
+ CInsertionLine
+ *m_InsertionLine; ///< Drag-and-drop insertion line for dropping between timeline items
+ CInsertionOverlay
+ *m_InsertionOverlay; ///< Drag-and-drop insertion marker for dropping on a timeline item
+ CBreadCrumbControl *m_BreadCrumbToolbar;
+ bool m_SuspendRecalcLayout;
+
+ CTimelineTranslationManager *m_TranslationManager;
+
+ std::vector<std::shared_ptr<UICDM::ISignalConnection>>
+ m_Connections; /// connections to the UICDM
+};
+#endif // INCLUDED_TIMELINE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.cpp
new file mode 100644
index 00000000..5ef25911
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TimelineFilter.h"
+#include "Bindings/ITimelineItemProperty.h"
+#include "Bindings/ITimelineItem.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CFilter::CFilter()
+ : m_ShowBehaviors(true)
+ , m_ShowMaterials(true)
+ , m_ShowProperties(true)
+ , m_ShowShy(true)
+ , m_ShowLocked(true)
+ , m_ShowVisible(true)
+ , m_IsExpanded(true)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CFilter::~CFilter()
+{
+}
+
+//=============================================================================
+/**
+ * @return true if behaviors should be shown, false if they should be hidden
+ */
+bool CFilter::GetBehaviors() const
+{
+ return m_ShowBehaviors;
+}
+
+//=============================================================================
+/**
+ * @return true if materials should be shown, false if they should be hidden
+ */
+bool CFilter::GetMaterials() const
+{
+ return m_ShowMaterials;
+}
+
+//=============================================================================
+/**
+ * @return true if properties should be shown, false if they should be hidden
+ */
+bool CFilter::GetProperties() const
+{
+ return m_ShowProperties;
+}
+
+//=============================================================================
+/**
+ * @return true if shy objects should still be shown, false if they should be hidden
+ */
+bool CFilter::GetShy() const
+{
+ return m_ShowShy;
+}
+
+//=============================================================================
+/**
+ * @return true if locked objects should still be shown, false if they should be hidden
+ */
+bool CFilter::GetLocked() const
+{
+ return m_ShowLocked;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show behaviors on objects.
+ * @param inShow true to show behaviors, false to hide behaviors
+ */
+void CFilter::SetBehaviors(bool inShow /*= true*/)
+{
+ m_ShowBehaviors = inShow;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show materials on objects.
+ * @param inShow true to show materials, false to hide materials
+ */
+void CFilter::SetMaterials(bool inShow /*= true*/)
+{
+ m_ShowMaterials = inShow;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show properties on objects.
+ * @param inShow true to show properties, false to hide properties
+ */
+void CFilter::SetProperties(bool inShow /*= true*/)
+{
+ m_ShowProperties = inShow;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show objects with the shy flag.
+ * @param inShow true to show shy objects, false to hide shy objects
+ */
+void CFilter::SetShy(bool inShow /*= true*/)
+{
+ m_ShowShy = inShow;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show objects with the locked flag.
+ * @param inShow true to show locked objects, false to hide locked objects
+ */
+void CFilter::SetLocked(bool inShow /*= true*/)
+{
+ m_ShowLocked = inShow;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not the parent object is expanded.
+ * If the parent is not expanded then nothing should be visible.
+ */
+void CFilter::SetExpanded(bool inIsExpanded /*= true*/)
+{
+ m_IsExpanded = inIsExpanded;
+}
+
+//=============================================================================
+/**
+ * Sets whether or not to show objects that are not visible.
+ * @param inIsVisible true to show non-visible objects, false to hide them.
+ */
+void CFilter::SetVisible(bool inIsVisible /*= true*/)
+{
+ m_ShowVisible = inIsVisible;
+}
+
+//=============================================================================
+/**
+ * Gets whether or not non-visible objects should be displayed.
+ * @return true if non-visible objects should be displayed.
+ */
+bool CFilter::GetVisible() const
+{
+ return m_ShowVisible;
+}
+
+//=============================================================================
+/**
+ * Gets whether or not the parent object is expanded.
+ * If the parent is not expanded then nothing should be visible.
+ */
+bool CFilter::IsExpanded() const
+{
+ return m_IsExpanded;
+}
+
+//=============================================================================
+/**
+ * Checks to see if the specified property should be displayed or not.
+ * @return true if the property should be visible.
+ */
+bool CFilter::Filter(ITimelineItemProperty *inTimelineItemProperty) const
+{
+ Q_UNUSED(inTimelineItemProperty);
+
+ bool theVisibleFlag = GetProperties();
+ theVisibleFlag &= IsExpanded();
+
+ return theVisibleFlag;
+}
+
+//=============================================================================
+/**
+ * Checks to see if the specified state should be displayed or not.
+ * @return true if the state should be visible.
+ */
+bool CFilter::Filter(ITimelineItem *inTimelineItem) const
+{
+ bool theVisibleFlag = true;
+
+ // If this row is shy, we need to check the filter for shy objects
+ if (inTimelineItem->IsShy())
+ theVisibleFlag &= GetShy();
+
+ // If this row is locked, we need to check the filter for locked objects
+ if (inTimelineItem->IsLocked())
+ theVisibleFlag &= GetLocked();
+
+ // This is for hiding visible eye toggle objects
+ if (!inTimelineItem->IsVisible())
+ theVisibleFlag &= GetVisible();
+
+ // If this row is a behavior, we need to check the filter for behaviors
+ if (inTimelineItem->GetObjectType() == OBJTYPE_BEHAVIOR)
+ theVisibleFlag &= GetBehaviors();
+
+ // If this row is a material, we need to check the filter for materials
+ if (inTimelineItem->GetObjectType() == OBJTYPE_MATERIAL)
+ theVisibleFlag &= GetMaterials();
+
+ return theVisibleFlag;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.h b/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.h
new file mode 100644
index 00000000..e956b4da
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineFilter.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_FILTER_H
+#define INCLUDED_FILTER_H 1
+
+#pragma once
+
+//==============================================================================
+// Forward
+//==============================================================================
+class ITimelineItem;
+class ITimelineItemProperty;
+
+//=============================================================================
+/**
+ * Filter class for determining what objects to show in the timeline.
+ */
+class CFilter
+{
+public:
+ CFilter();
+ virtual ~CFilter();
+
+ bool GetBehaviors() const;
+ bool GetMaterials() const;
+ bool GetProperties() const;
+ bool GetShy() const;
+ bool GetLocked() const;
+ bool GetVisible() const;
+ void SetBehaviors(bool inShow = true);
+ void SetMaterials(bool inShow = true);
+ void SetProperties(bool inShow = true);
+ void SetShy(bool inShow = true);
+ void SetLocked(bool inShow = true);
+ void SetVisible(bool inShow = true);
+
+ void SetExpanded(bool inExpanded = true);
+ bool IsExpanded() const;
+
+ bool Filter(ITimelineItemProperty *inTimelineItemProperty) const;
+ bool Filter(ITimelineItem *inTimelineItem) const;
+
+protected:
+ bool m_ShowBehaviors;
+ bool m_ShowMaterials;
+ bool m_ShowProperties;
+ bool m_ShowShy;
+ bool m_ShowLocked;
+ bool m_ShowVisible;
+ bool m_IsExpanded;
+};
+
+#endif // INCLUDED_FILTER_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.cpp
new file mode 100644
index 00000000..36946432
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "TimelineKeyframe.h"
+#include "Renderer.h"
+
+CTimelineKeyframe::CTimelineKeyframe()
+ : m_Time(0)
+ , m_IsDynamic(false)
+{
+}
+
+CTimelineKeyframe::~CTimelineKeyframe()
+{
+}
+
+void CTimelineKeyframe::SetTime(long inTime)
+{
+ m_Time = inTime;
+}
+
+long CTimelineKeyframe::GetTime()
+{
+ return m_Time;
+}
+
+//=============================================================================
+/**
+ * called when this key is made dynamic
+ * @param inIsDynamic true if the keyframe is dynamic
+ */
+void CTimelineKeyframe::SetDynamic(bool inIsDynamic)
+{
+ if (m_IsDynamic != inIsDynamic) {
+ m_IsDynamic = inIsDynamic;
+ // Invalidate( );
+ }
+}
+
+//=============================================================================
+/**
+ * @return true if dynamic
+ */
+bool CTimelineKeyframe::IsDynamic()
+{
+ return m_IsDynamic;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.h b/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.h
new file mode 100644
index 00000000..a08f553c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineKeyframe.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIMELINE_KEYFRAME
+#define INCLUDED_TIMELINE_KEYFRAME 1
+
+#pragma once
+class CRenderer;
+
+class CTimelineKeyframe
+{
+public:
+ CTimelineKeyframe();
+ ~CTimelineKeyframe();
+ virtual void Draw(CRenderer *inRenderer) = 0;
+
+ virtual void SetTime(long inTime);
+ virtual long GetTime();
+
+ void SetDynamic(bool inIsDynamic);
+ bool IsDynamic();
+
+ static const long DRAGBUFFER =
+ 3; // Specifies how many pixels a keframe should be dragged before it actually moves
+
+protected:
+ long m_Time;
+ bool m_IsDynamic;
+};
+
+#endif // INCLUDED_TIMELINE_KEYFRAME \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineRow.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineRow.cpp
new file mode 100644
index 00000000..c465575c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineRow.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "TimelineRow.h"
+#include "StudioPreferences.h"
+#include "StudioObjectTypes.h"
+#include "BaseStateRow.h"
+
+const long CTimelineRow::TREE_INDENT = CStudioPreferences::GetRowSize();
+
+CTimelineRow::CTimelineRow()
+ : m_ParentRow(nullptr)
+ , m_IsViewable(false)
+ , m_Indent(0)
+{
+}
+
+CTimelineRow::~CTimelineRow()
+{
+}
+void CTimelineRow::SetIndent(long inIndent)
+{
+ m_Indent = inIndent;
+}
+
+long CTimelineRow::GetIndent()
+{
+ return m_Indent;
+}
+
+void CTimelineRow::SetParent(CBaseStateRow *inParent)
+{
+ m_ParentRow = inParent;
+}
+
+//=============================================================================
+/**
+ * Gets the Parent Row
+ */
+CBaseStateRow *CTimelineRow::GetParentRow() const
+{
+ return m_ParentRow;
+}
+
+void CTimelineRow::SetTimeRatio(double inTimeRatio)
+{
+ Q_UNUSED(inTimeRatio);
+}
+
+void CTimelineRow::OnChildVisibilityChanged()
+{
+}
+
+bool CTimelineRow::IsViewable() const
+{
+ return m_IsViewable;
+}
+
+void CTimelineRow::PopulateSnappingList(CSnapper *inSnapper)
+{
+ Q_UNUSED(inSnapper);
+}
+
+//=============================================================================
+/**
+ * By default, this will recurse up its parent, for an answer.
+ * If this proves to a performance hit, we can cache a ITimelineControl pointer at EVERY row.
+ */
+ITimelineControl *CTimelineRow::GetTopControl() const
+{
+ ITimelineControl *theControl = (m_ParentRow) ? m_ParentRow->GetTopControl() : nullptr;
+ ASSERT(theControl);
+ return theControl;
+}
+
+//=============================================================================
+/**
+ * Retrieves the background color for the row based upon the type of asset
+ * passed in.
+ * @param inType specifies which asset type you want the color for
+ * @return background color to use for this row
+ */
+::CColor CTimelineRow::GetTimebarBackgroundColor(EStudioObjectType inType)
+{
+ ::CColor theColor;
+
+ switch (inType) {
+ case OBJTYPE_LAYER:
+ theColor = CStudioPreferences::GetLayerBackgroundColor();
+ break;
+
+ case OBJTYPE_GROUP:
+ case OBJTYPE_COMPONENT:
+ theColor = CStudioPreferences::GetGroupBackgroundColor();
+ break;
+
+ default:
+ theColor = CStudioPreferences::GetObjectBackgroundColor();
+ break;
+ }
+
+ return theColor;
+}
+
+//=============================================================================
+/**
+ * Retrieves the background color for the row when the mouse is over the row,
+ * based upon the type of asset passed in.
+ * @param inType specifies which asset type you want the color for
+ * @return background color to use for this row when the mouse is over the row
+ */
+::CColor CTimelineRow::GetTimebarHighlightBackgroundColor(EStudioObjectType inType)
+{
+ ::CColor theColor;
+
+ switch (inType) {
+ case OBJTYPE_LAYER:
+ theColor = CStudioPreferences::GetMouseOverHighlightColor();
+ break;
+
+ case OBJTYPE_GROUP:
+ case OBJTYPE_COMPONENT:
+ theColor = CStudioPreferences::GetMouseOverHighlightColor();
+ break;
+
+ default:
+ theColor = CStudioPreferences::GetMouseOverHighlightColor();
+ break;
+ }
+
+ return theColor;
+}
+
+long CTimelineRow::GetLatestEndTime()
+{
+ return 0;
+}
+
+void CTimelineRow::Dispose()
+{
+ delete this;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineRow.h b/src/Authoring/Studio/Palettes/Timeline/TimelineRow.h
new file mode 100644
index 00000000..c18d438b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineRow.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TIMELINE_ROW_H
+#define INCLUDED_TIMELINE_ROW_H 1
+
+#pragma once
+
+#include "TimelineFilter.h"
+#include "CColor.h"
+#include "StudioObjectTypes.h"
+
+#include <vector>
+
+class CSnapper;
+class CControl;
+class CBaseStateRow;
+class ISnappingListProvider;
+class ITimelineControl;
+
+class CTimelineRow
+{
+public:
+ static const long TREE_INDENT;
+
+ CTimelineRow();
+ virtual ~CTimelineRow();
+
+ virtual CControl *GetColorControl() = 0;
+ virtual CControl *GetTreeControl() = 0;
+ virtual CControl *GetToggleControl() = 0;
+ virtual CControl *GetTimebarControl() = 0;
+
+ virtual void SetIndent(long inIndent);
+ long GetIndent();
+
+ virtual void Filter(const CFilter &inFilter, bool inFilterChildren = true) = 0;
+
+ void SetParent(CBaseStateRow *inParent);
+ CBaseStateRow *GetParentRow() const;
+ virtual void SetTimeRatio(double inTimeRatio);
+ virtual void OnChildVisibilityChanged();
+ virtual bool IsViewable() const;
+ virtual void PopulateSnappingList(CSnapper *inSnappingList);
+ virtual ISnappingListProvider *GetSnappingListProvider() const = 0;
+ virtual void SetSnappingListProvider(ISnappingListProvider *inProvider) = 0;
+ virtual ITimelineControl *GetTopControl() const;
+
+ virtual ::CColor GetTimebarBackgroundColor(EStudioObjectType inType);
+ virtual ::CColor GetTimebarHighlightBackgroundColor(EStudioObjectType inType);
+
+ virtual long GetLatestEndTime();
+
+ virtual void Dispose();
+
+protected:
+ CBaseStateRow *m_ParentRow;
+ bool m_IsViewable;
+ long m_Indent;
+};
+#endif // INCLUDED_TIMELINE_ROW_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.cpp
new file mode 100644
index 00000000..fe072db6
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "TimelineSplitter.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimelineSplitter::CTimelineSplitter()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimelineSplitter::~CTimelineSplitter()
+{
+}
+
+//=============================================================================
+/**
+ * Set the location of the splitter bar. Overridden so that this location can
+ * be stored in the user's preferences.
+ * @param inSplitLocation the location of the splitter bar, in pixels from the right/top.
+ */
+void CTimelineSplitter::SetSplitLocation(long inPixels)
+{
+ CSplitter::SetSplitLocation(inPixels);
+ CStudioPreferences::SetTimelineSplitterLocation(inPixels);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.h b/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.h
new file mode 100644
index 00000000..f9ba681a
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineSplitter.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef TIMELINE_SPLITTER_INCLUDED
+#define TIMELINE_SPLITTER_INCLUDED 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "Splitter.h"
+
+//=============================================================================
+/**
+ * Overridden splitter, specific to the timeline, which stores the split location
+ * so that it can be retrieved between program sessions.
+ */
+class CTimelineSplitter : public CSplitter
+{
+public:
+ CTimelineSplitter();
+ virtual ~CTimelineSplitter();
+ void SetSplitLocation(long inPixels) override;
+};
+
+#endif
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.cpp
new file mode 100644
index 00000000..ead5251d
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.cpp
@@ -0,0 +1,713 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "TimelineTimelineLayout.h"
+#include "TimeMeasure.h"
+#include "ScalableScroller.h"
+#include "StudioUtils.h"
+#include "TimelineRow.h"
+#include "TimelineControl.h"
+#include "StateRow.h"
+#include "Snapper.h"
+#include "Bindings/TimelineTranslationManager.h"
+#include "ControlData.h"
+#include "HotKeys.h"
+#include "foundation/Qt3DSLogging.h"
+
+//=============================================================================
+// Defines
+//=============================================================================
+// For Win the modifier key for keyframe multi selection is the control key.
+#define MODIFIER_KEY CHotKeys::MODIFIER_CONTROL
+
+//=============================================================================
+// Class constants
+//=============================================================================
+const double DEFAULT_TIME_RATIO = .05;
+const double CTimelineTimelineLayout::SCALING_PERCENTAGE_INC = 1.1;
+const double CTimelineTimelineLayout::SCALING_PERCENTAGE_DEC = 0.9;
+const double CTimelineTimelineLayout::MAX_ZOOM_OUT = 7e-005;
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimelineTimelineLayout::CTimelineTimelineLayout(CTimelineControl *inTimelineControl, IDoc *inDoc)
+ : m_Playhead(this, inDoc)
+ , m_IsLayoutChanged(false)
+ , m_IsMouseDown(false)
+{
+ m_ControlData->SetMouseWheelEnabled(true);
+ m_TimelineControl = inTimelineControl;
+
+ m_TimeRatio = DEFAULT_TIME_RATIO + .01;
+ m_TimeMeasure = new CTimeMeasure(this, m_TimeRatio);
+ m_Scroller = new CScalableScroller();
+
+ m_Scroller->SetVerticalScrollMode(CScroller::ALWAYS);
+ m_Scroller->SetHorizontalScrollMode(CScroller::ALWAYS);
+ m_Scroller->AddScrollListener(this);
+ m_BoundingRect = new CAreaBoundingRect();
+ m_BoundingRect->SetName("TimelineAreaBoundingRect");
+ m_BoundingRect->SetVisible(false);
+ m_BoundingRect->SetAlpha(128);
+
+ AddChild(m_TimeMeasure);
+ AddChild(m_Scroller);
+ AddChild(&m_Playhead);
+ AddChild(m_BoundingRect);
+
+ // Blank control filling in the bottom of the timeline, under the rows
+ CBlankControl *theTimelineBlankControl = new CBlankControl();
+ m_TimebarList = new CFlowLayout(theTimelineBlankControl);
+
+ m_Scroller->AddChild(m_TimebarList);
+ m_Scroller->SetScalingListener(this);
+ m_TimebarList->SetName("TimelineTimelineLayoutList");
+
+ // Initializing flags for keyframe multi select to work.
+ m_CommitKeyframeSelection = false;
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimelineTimelineLayout::~CTimelineTimelineLayout()
+{
+ delete m_TimeMeasure;
+ delete m_Scroller;
+ delete m_TimebarList;
+ delete m_BoundingRect;
+}
+
+//=============================================================================
+/**
+ * Clear all the StateRows out of the top-level list.
+ * This is used when the current presentation is being cleared out.
+ */
+void CTimelineTimelineLayout::ClearRows()
+{
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = (*thePos);
+ m_TimebarList->RemoveChild(theRow->GetTimebarControl());
+ }
+
+ m_Rows.clear();
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * Overrrides CControl::SetSize so that this can redo the layout of all inner
+ * controls.
+ * @param inSize the new size of this control.
+ */
+void CTimelineTimelineLayout::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+
+ RecalcLayout();
+}
+
+//=============================================================================
+/**
+ * Recalculate the positioning of all the child components.
+ */
+void CTimelineTimelineLayout::RecalcLayout()
+{
+ CPt mySize = GetSize();
+ // Put the time measure on top taking 21 pixels high.
+ m_TimeMeasure->SetSize(CPt(mySize.x, 21));
+ m_TimeMeasure->SetPosition(CPt(0, 0));
+
+ // Make the scroller take up the rest of the space.
+ m_Scroller->SetSize(CPt(mySize.x, mySize.y - 42));
+ m_Scroller->SetPosition(CPt(0, 21));
+
+ // Make it the full length of the view, minus the bottom scroll bar.
+ m_Playhead.SetSize(CPt(13, GetSize().y - m_Scroller->GetHorizontalBar()->GetSize().y - 21));
+
+ long theMinTime = -(m_Playhead.GetCenterOffset());
+
+ if (!m_Rows.empty()) {
+ long theLatestTime = 0;
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = (*thePos);
+ long theRowLatestTime = theRow->GetLatestEndTime();
+ if (theRowLatestTime > theLatestTime)
+ theLatestTime = theRowLatestTime;
+ }
+
+ long theMinWidth = ::TimeToPos(theLatestTime, m_TimeRatio) + END_BUFFER_SIZE;
+ long theMinHeight = m_TimebarList->GetMinimumSize().y;
+
+ CPt theVisSize = m_Scroller->GetVisibleSize();
+
+ if (theMinHeight < theVisSize.y)
+ theMinHeight = theVisSize.y;
+ if (theMinWidth < theVisSize.x)
+ theMinWidth = theVisSize.x;
+
+ m_TimebarList->ResetMinMaxPref();
+ m_TimebarList->SetAbsoluteSize(CPt(theMinWidth, theMinHeight));
+ }
+
+ // Set up the limits.
+ m_Playhead.SetMinMaxPosition(theMinTime, mySize.x - m_Scroller->GetVerticalBar()->GetSize().x);
+
+ // Set playhead to time 0.
+ SetTime(m_TimelineControl->GetTranslationManager()->GetCurrentViewTime(), true);
+
+ // Reset! so that this isn't unnecessarily run
+ m_IsLayoutChanged = false;
+}
+
+//=============================================================================
+/**
+ * Add a timeline row to this object.
+ * This will add the row as a top level object.
+ * @param inRow the row to be added.
+ */
+void CTimelineTimelineLayout::AddRow(CTimelineRow *inRow)
+{
+ m_Rows.push_back(inRow);
+
+ m_TimebarList->AddChild(inRow->GetTimebarControl());
+
+ inRow->SetTimeRatio(m_TimeRatio);
+ // For keyframe/timebar snapping.
+ inRow->SetSnappingListProvider(this);
+}
+
+//=============================================================================
+/**
+ * Call from the ScalableScroller that it is scaling the right side of the timebar.
+ * @param inLength the length that the thumb wants to be.
+ * @param inTotalLength the maximum length that the thumb can be.
+ * @param inOffset the offset of the thumb position.
+ */
+void CTimelineTimelineLayout::OnScalingRight(long inLength, long inTotalLength, long inOffset)
+{
+ double theViewSize = m_Scroller->GetVisibleSize().x;
+ double theClientSize = m_Scroller->GetContaineeSize().x;
+ double theLength = inLength;
+ double theTotalLength = inTotalLength;
+
+ double theTimeRatio =
+ (theViewSize * theTotalLength) / (theClientSize * theLength) * m_TimeRatio;
+
+ // This means the bar was dragged to the far end, just prevent it for getting wacky.
+ if (theTimeRatio > 0) {
+ // This will set the time ratio, but will cap it at 1 or MAX_ZOOM_OUT so if the Time ratio
+ // less than max, don't need to move the timeline
+ SetTimeRatio(theTimeRatio);
+ if (theTimeRatio < 1) {
+ double theMaxVisPos = m_Scroller->GetMaxVisiblePosition().x;
+ long theVisiblePosition = ::dtol(theMaxVisPos * inOffset / (inTotalLength - inLength));
+ m_Scroller->SetVisiblePosition(
+ CPt(theVisiblePosition, m_Scroller->GetVisiblePosition().y));
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Under construction.
+ */
+void CTimelineTimelineLayout::OnScalingLeft(long inLength, long inTotalLength, long inOffset)
+{
+ // Hey- look at that, doesn't matter which side you're scaling.
+ // Hey, nice comment especially the function header
+ OnScalingRight(inLength, inTotalLength, inOffset);
+}
+
+void CTimelineTimelineLayout::OnScalingReset()
+{
+ SetTimeRatio(DEFAULT_TIME_RATIO);
+}
+
+//=============================================================================
+/**
+ * Set the TimeRatio to be used.
+ * This will propagate the time ratio down to all the child items.
+ * @param inTimeRatio the time ratio to be set.
+ */
+void CTimelineTimelineLayout::SetTimeRatio(double inTimeRatio)
+{
+ if (inTimeRatio != m_TimeRatio) {
+ if (inTimeRatio > 1)
+ inTimeRatio = 1;
+ // if ( inTimeRatio < MAX_ZOOM_OUT )
+ // inTimeRatio = MAX_ZOOM_OUT;
+
+ m_TimeRatio = inTimeRatio;
+ m_TimeMeasure->SetTimeRatio(inTimeRatio);
+
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = (*thePos);
+ theRow->SetTimeRatio(inTimeRatio);
+ }
+
+ RecalcLayout();
+
+ // store the timeline ratio
+ SetTimelineRatio(m_TimelineControl->GetActiveSlide(), m_TimeRatio);
+ qCInfo(qt3ds::TRACE_INFO) << "Set time ratio: " << inTimeRatio;
+ }
+}
+
+//==============================================================================
+/**
+ * When timeline layout has changed. RecalcLayout should be called to adjust the scrollbars if
+ * a asset is expanded/collapsed in the timeline.
+ */
+void CTimelineTimelineLayout::OnTimelineLayoutChanged()
+{
+ RecalcLayout();
+
+ // In addition, this has to be 'marked' for if SetScrollerPositionY is called due to
+ // new assets being added, RecalcLayout has to be called again.
+ m_IsLayoutChanged = true;
+}
+
+/**
+ * Deletes the time zoom ratio for a particular slide.
+ * @param inContext the time context of that slide to delete
+ */
+void CTimelineTimelineLayout::DeleteTimelineRatio(UICDM::CUICDMSlideHandle inSlide)
+{
+ m_TimelineRatio.erase(inSlide);
+}
+
+/**
+ * Clear all entries
+ */
+void CTimelineTimelineLayout::ClearAllTimeRatios()
+{
+ m_TimelineRatio.clear();
+}
+
+/**
+ * Retrieves the time zoom ratio for a particular slide
+ * @param inContext the time context of that slide to retrieve zoom ratio
+ * @return the zoom ratio, or -1 if it's not found
+ */
+double CTimelineTimelineLayout::GetTimelineRatio(UICDM::CUICDMSlideHandle inSlide)
+{
+ TSlideRatioMap::iterator theResult = m_TimelineRatio.find(inSlide);
+ if (theResult != m_TimelineRatio.end())
+ return theResult->second;
+ else
+ return -1;
+}
+
+/**
+ * Sets the time zoom ratio for a particular slide
+ * @param inContext the time context of that slide
+ * @param inRatio the zoom factor
+ */
+void CTimelineTimelineLayout::SetTimelineRatio(UICDM::CUICDMSlideHandle inSlide, double inRatio)
+{
+ m_TimelineRatio[inSlide] = inRatio;
+}
+
+//=============================================================================
+/**
+ * For testing purposes.
+ */
+long CTimelineTimelineLayout::GetMaximumTimebarTime()
+{
+ return 30000;
+}
+
+//=============================================================================
+/**
+ * Call from the TimelineView to notifiy this that some of its objects got filtered.
+ * This was used for redoing the layout but is no longer necessary.
+ */
+void CTimelineTimelineLayout::Filter()
+{
+}
+
+//=============================================================================
+/**
+ * Notification from the CScroller that it is scrolling.
+ * This will update the other views with the verticall scrolling and update
+ * the TimeMeasure with the horizontal scroll amount.
+ * @param inScrollAmount the amount that was scrolled by.
+ */
+void CTimelineTimelineLayout::OnScroll(CScroller *inSource, CPt inScrollAmount)
+{
+ Q_UNUSED(inSource);
+
+ m_TimelineControl->SetScrollPositionY(m_Scroller, m_Scroller->GetVisiblePosition().y);
+
+ long theTimeOffset = GetViewTimeOffset();
+ m_TimeMeasure->SetTimeOffset(theTimeOffset);
+
+ long thePlayheadPos =
+ ::TimeToPos(m_TimelineControl->GetTranslationManager()->GetCurrentViewTime()
+ - theTimeOffset,
+ m_TimeRatio)
+ - m_Playhead.GetCenterOffset();
+
+ m_Playhead.SetPosition(CPt(thePlayheadPos, 0));
+
+ m_DragBeginPoint += inScrollAmount;
+}
+
+void CTimelineTimelineLayout::SetScrollPositionY(CScroller *inSource, long inPositionY,
+ bool inAbsolute)
+{
+ Q_UNUSED(inSource);
+
+ CPt theVisPos = m_Scroller->GetVisiblePosition();
+
+ if (!inAbsolute) {
+ CPt theMaxSize = m_Scroller->GetMaxVisiblePosition();
+
+ CRct theVisibleRect(CPt(theVisPos.x, theMaxSize.y - theVisPos.y),
+ m_Scroller->GetVisibleSize());
+ CPt thePoint(theVisPos.x, inPositionY);
+ if (!theVisibleRect.IsInRect(thePoint))
+ m_Scroller->SetVisiblePosition(CPt(theVisPos.x, inPositionY));
+ } else {
+ // For new assets added, RecalcLayout needs be called here if there was a layout changed
+ // because
+ // m_TimebarList->GetMinimumSize( ).y is only updated at this point, otherwise the tree and
+ // layout will
+ // go out of sync.
+ if (m_IsLayoutChanged)
+ RecalcLayout();
+
+ m_Scroller->SetVisiblePosition(CPt(theVisPos.x, inPositionY));
+ }
+}
+
+//=============================================================================
+/**
+ * Get the scroller control this is using.
+ * Meant for testing purposes.
+ * @return the scroller this is using.
+ */
+CScalableScroller *CTimelineTimelineLayout::GetScroller()
+{
+ return m_Scroller;
+}
+
+//=============================================================================
+/**
+ * Get the playhead control this is using.
+ * Meant for testing purposes.
+ * @return the playhead this is using.
+ */
+CPlayhead *CTimelineTimelineLayout::GetPlayhead()
+{
+ return &m_Playhead;
+}
+
+//=============================================================================
+/**
+ * Scroll the contents of the timeline horizontally.
+ * This is used mainly by the playhead to scroll the view when it gets to the
+ * edge.
+ * @param inAmount the amount to scroll the view by.
+ * @return the amount actually scrolled, limited by min/max values.
+ */
+long CTimelineTimelineLayout::ScrollLayout(long inAmount)
+{
+ // Log the current position for returning
+ CPt thePosition = m_Scroller->GetVisiblePosition();
+
+ m_Scroller->SetVisiblePosition(CPt(thePosition.x + inAmount, thePosition.y));
+
+ // Return how much was actually scrolled, let the scroller handle min/max scroll amounts.
+ return m_Scroller->GetVisiblePosition().x - thePosition.x;
+}
+
+//=============================================================================
+/**
+ * Recalculate what the time is based on the location of the playhead.
+ * This will call SetTime on the TimelineView with the new time.
+ * @param inUpdateClient true if the client time should be updated.
+ */
+void CTimelineTimelineLayout::RecalcTime(bool inUpdateClient, long inFlags)
+{
+ long theOffset = m_Playhead.GetPosition().x + m_Playhead.GetCenterOffset()
+ + m_Scroller->GetVisiblePosition().x;
+
+ long theTime = ::PosToTime(theOffset, m_TimeRatio);
+ m_Snapper.InterpretTimeEx(theTime, inFlags);
+
+ // Update the time
+ m_Playhead.UpdateTime(theTime, inUpdateClient);
+}
+
+//=============================================================================
+/**
+ * Call from the timeline view that the time is changing.
+ * @param inNewTime the new time.
+ * @param inIsSecondary lame flag to prevent infinite recursion.
+ */
+void CTimelineTimelineLayout::SetTime(long inNewTime, bool inIsSecondary)
+{
+ long theOffset = ::TimeToPos(inNewTime, m_TimeRatio);
+ theOffset -= m_Scroller->GetVisiblePosition().x + m_Playhead.GetCenterOffset();
+
+ long theViewSize = m_Scroller->GetVisibleSize().x;
+
+ if (!inIsSecondary) {
+ if (theOffset < -m_Playhead.GetCenterOffset()) {
+ long thePos = ::TimeToPos(inNewTime, m_TimeRatio) - m_Playhead.GetCenterOffset();
+ m_Scroller->SetVisiblePosition(CPt(thePos, m_Scroller->GetVisiblePosition().y));
+ } else if (theOffset > (theViewSize - (m_Playhead.GetCenterOffset() + 20))) {
+ long thePos = ::TimeToPos(inNewTime, m_TimeRatio) + 20;
+ thePos -= theViewSize;
+ m_Scroller->SetVisiblePosition(CPt(thePos, m_Scroller->GetVisiblePosition().y));
+ }
+ SetTime(inNewTime, true);
+ } else {
+ m_Playhead.SetPosition(CPt(theOffset, m_Playhead.GetPosition().y));
+ }
+}
+
+//=============================================================================
+/**
+ * Notification that the TimeMeasure was clicked on.
+ * This is used to reposition the playhead wherever the mouse was clicked.
+ * @param inPoint the location of the mouse local to the time measure.
+ */
+void CTimelineTimelineLayout::OnTimeMeasureMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ m_Snapper.Clear();
+ m_Snapper.SetSource(&m_Playhead);
+ PopulateSnappingList(&m_Snapper);
+ m_Snapper.SetSnappingKeyframes(true);
+
+ m_Playhead.SetPosition(
+ CPt(inPoint.x - m_Playhead.GetCenterOffset(), m_Playhead.GetPosition().y));
+ RecalcTime(true, inFlags);
+}
+
+//=============================================================================
+/**
+ * Handles left-clicks. Starts a drag operation if a child does not handle the
+ * message.
+ * @param inPoint location of the mouse when event occurred
+ * @param inFlags state of modifier keys when event occurred
+ */
+bool CTimelineTimelineLayout::OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ if (!CControl::OnMouseDown(inPoint, inFlags)) {
+ m_BoundingRect->SetSize(CPt(0, 0));
+ m_BoundingRect->SetVisible(true);
+
+ // Do not deselect all keyframes as the user intends to select more keyframes,
+ // when the modifier key is pressed.
+ if (!(inFlags & MODIFIER_KEY))
+ m_TimelineControl->GetTranslationManager()->ClearKeyframeSelection();
+
+ m_IsMouseDown = true;
+ m_DragBeginPoint = inPoint;
+ }
+ return true;
+}
+
+void CTimelineTimelineLayout::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseMove(inPoint, inFlags);
+
+ if (m_IsMouseDown) {
+ CPt theSize;
+ CRct theRect;
+
+ // Tests if the user has pressed the modifier key, while moving the mouse.
+ bool theModifierKeyDown;
+ if (inFlags & MODIFIER_KEY)
+ theModifierKeyDown = true;
+ else
+ theModifierKeyDown = false;
+
+ // Calculate the rect for the bounding box
+ theSize = CPt(inPoint.x - m_DragBeginPoint.x, inPoint.y - m_DragBeginPoint.y);
+ theRect = CRct(m_DragBeginPoint, theSize);
+ theRect.Normalize();
+ m_BoundingRect->SetPosition(theRect.position);
+ m_BoundingRect->SetSize(theRect.size);
+ theRect.Offset(-m_Scroller->GetPosition());
+ theRect.Offset(m_Scroller->GetVisiblePosition());
+
+ // Select all keys inside the rect
+
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CStateRow *theRow = reinterpret_cast<CStateRow *>(*thePos);
+ theRow->SelectKeysInRect(theRect, theModifierKeyDown, m_CommitKeyframeSelection);
+ }
+ m_CommitKeyframeSelection = false;
+ }
+}
+
+void CTimelineTimelineLayout::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ // try to prevent stuck mousetips on exceptions
+ try {
+ CControl::OnMouseUp(inPoint, inFlags);
+ m_BoundingRect->SetVisible(false);
+ } catch (...) {
+ }
+
+ m_IsMouseDown = false;
+
+ // Commits the key frame selection. This finalises the keyframes selection
+ // in the rect. When the mouse is down again, we would be able to append
+ // the commited keyframes with the new batch of keyframes.
+ m_CommitKeyframeSelection = true;
+}
+
+void CTimelineTimelineLayout::PopulateSnappingList(CSnapper *inSnappingList)
+{
+ CRct theArea(m_Scroller->GetVisibleSize());
+ theArea.Offset(-m_Scroller->GetPosition());
+ theArea.Offset(m_Scroller->GetVisiblePosition());
+
+ inSnappingList->SetVisibleArea(theArea.position.y, theArea.size.y);
+
+ inSnappingList->SetTimeRatio(m_TimeRatio);
+ if (inSnappingList->GetSource() != &m_Playhead)
+ inSnappingList->AddTime(m_Playhead.GetCurrentTime());
+
+ m_TimeMeasure->PopulateSnappingList(inSnappingList);
+
+ TTimelineRowList::iterator theRowIter = m_Rows.begin();
+ for (; theRowIter != m_Rows.end(); ++theRowIter) {
+ (*theRowIter)->PopulateSnappingList(inSnappingList);
+ }
+}
+
+long CTimelineTimelineLayout::GetViewTimeOffset()
+{
+ return ::dtol(m_Scroller->GetVisiblePosition().x / m_TimeRatio);
+}
+
+CTimeMeasure *CTimelineTimelineLayout::GetTimeMeasure()
+{
+ return m_TimeMeasure;
+}
+
+//=============================================================================
+/**
+ * Register all the events for hotkeys that are active for the entire application.
+ * Hotkeys for the entire application are ones that are not view specific in
+ * scope.
+ * @param inShortcutHandler the global shortcut handler.
+ */
+void CTimelineTimelineLayout::RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler)
+{
+ inShortcutHandler->RegisterKeyDownEvent(new CDynHotKeyConsumer<CTimelineTimelineLayout>(
+ this, &CTimelineTimelineLayout::OnScalingZoomIn),
+ 0, Qt::Key_Plus);
+ inShortcutHandler->RegisterKeyDownEvent(new CDynHotKeyConsumer<CTimelineTimelineLayout>(
+ this, &CTimelineTimelineLayout::OnScalingZoomOut),
+ 0, Qt::Key_Minus);
+ inShortcutHandler->RegisterKeyDownEvent(new CDynHotKeyConsumer<CTimelineTimelineLayout>(
+ this, &CTimelineTimelineLayout::OnScalingZoomIn),
+ Qt::KeypadModifier, Qt::Key_Plus);
+ inShortcutHandler->RegisterKeyDownEvent(new CDynHotKeyConsumer<CTimelineTimelineLayout>(
+ this, &CTimelineTimelineLayout::OnScalingZoomOut),
+ Qt::KeypadModifier, Qt::Key_Minus);
+}
+
+//=============================================================================
+/**
+ * Call from the Hotkey that it is zooming in the timebar.
+ */
+
+void CTimelineTimelineLayout::OnScalingZoomIn()
+{
+ double theTimeRatio = m_TimeRatio * SCALING_PERCENTAGE_INC;
+
+ SetTimeRatio(theTimeRatio);
+ CenterToPlayhead();
+}
+
+//=============================================================================
+/**
+ * Call from the Hotkey that it is zooming out of the timebar.
+ */
+
+void CTimelineTimelineLayout::OnScalingZoomOut()
+{
+ double theTimeRatio = m_TimeRatio * SCALING_PERCENTAGE_DEC;
+
+ SetTimeRatio(theTimeRatio);
+ CenterToPlayhead();
+}
+
+void CTimelineTimelineLayout::CenterToPlayhead()
+{
+ long theTime = m_Playhead.GetCurrentTime();
+ long thePos = ::TimeToPos(theTime, m_TimeRatio);
+ long theNewPosX = thePos - (m_Scroller->GetSize().x / 2);
+
+ m_Scroller->SetVisiblePosition(CPt(theNewPosX, m_Scroller->GetVisiblePosition().y));
+}
+
+//==============================================================================
+/**
+ * Handle mouse wheel messages to allow zooming
+ */
+bool CTimelineTimelineLayout::OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags)
+{
+ bool theRetVal = false;
+ if (inFlags & CHotKeys::MODIFIER_CONTROL) {
+ if (inAmount > 0)
+ OnScalingZoomIn();
+ else
+ OnScalingZoomOut();
+ theRetVal = true;
+ } else
+ theRetVal = CControl::OnMouseWheel(inPoint, inAmount, inFlags);
+ return theRetVal;
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.h b/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.h
new file mode 100644
index 00000000..35524d5b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineTimelineLayout.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_TIMELINE_TIMELINE_LAYOUT_H
+#define INCLUDED_TIMELINE_TIMELINE_LAYOUT_H 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "Control.h"
+#include <vector>
+#include "ScalableScrollerBar.h"
+#include "Scroller.h"
+#include "Pt.h"
+#include "Snapper.h"
+#include "AreaBoundingRect.h"
+#include "Playhead.h"
+#include "UICDMHandles.h"
+
+//=============================================================================
+// Forwards
+//=============================================================================
+class CScalableScroller;
+class CTimelineRow;
+class CFlowLayout;
+class CTimelineControl;
+class CSnapper;
+class IDoc;
+class CHotKeys;
+class CTimeMeasure;
+class CPlayhead;
+
+//=============================================================================
+/**
+ * Right-hand pane of the Timeline containing timebars, keyframes, etc.
+ */
+class CTimelineTimelineLayout : public CControl,
+ public CScalingListener,
+ public CScrollListener,
+ public ISnappingListProvider
+{
+ typedef std::vector<CTimelineRow *> TTimelineRowList;
+ typedef std::map<UICDM::CUICDMSlideHandle, double> TSlideRatioMap;
+
+public:
+ static const long END_BUFFER_SIZE = 20;
+ static const double SCALING_PERCENTAGE_INC;
+ static const double SCALING_PERCENTAGE_DEC;
+ static const double MAX_ZOOM_OUT;
+
+ CTimelineTimelineLayout(CTimelineControl *inView, IDoc *inDoc);
+ virtual ~CTimelineTimelineLayout();
+
+ void SetSize(CPt inSize) override;
+
+ void AddRow(CTimelineRow *inRow);
+
+ void OnScalingRight(long inLength, long inTotalLength, long inOffset) override;
+ void OnScalingLeft(long inLength, long inTotalLength, long inOffset) override;
+ void OnScalingReset() override;
+
+ long ScrollLayout(long inAmount);
+
+ void Filter();
+
+ void OnScroll(CScroller *inScroller, CPt inScrollAmount) override;
+ void SetScrollPositionY(CScroller *inSource, long inPositionY, bool inAbsolute = true);
+
+ void ClearRows();
+
+ bool OnMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ bool OnMouseWheel(CPt inPoint, long inAmount, Qt::KeyboardModifiers inFlags) override;
+
+ CScalableScroller *GetScroller();
+ CPlayhead *GetPlayhead();
+ CTimeMeasure *GetTimeMeasure();
+ void RecalcTime(bool inUpdateClient, long inFlags);
+ void SetTime(long inTime, bool inIsSecondary = false);
+ void OnTimeMeasureMouseDown(CPt inPoint, Qt::KeyboardModifiers inFlags);
+ long GetViewTimeOffset();
+ void RecalcLayout();
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+ void SetTimeRatio(double inTimeRatio);
+ void OnTimelineLayoutChanged();
+
+ void DeleteTimelineRatio(UICDM::CUICDMSlideHandle inSlide);
+ void ClearAllTimeRatios();
+ double GetTimelineRatio(UICDM::CUICDMSlideHandle inSlide);
+
+ // ISnappingListProvider
+ void PopulateSnappingList(CSnapper *inSnappingList) override;
+
+protected:
+ void SetTimelineRatio(UICDM::CUICDMSlideHandle inSlide, double inRatio);
+
+ long GetMaximumTimebarTime();
+ void OnScalingZoomIn();
+ void OnScalingZoomOut();
+ void CenterToPlayhead();
+
+ bool m_CommitKeyframeSelection; ///< flag for saving previous keyframe selection when the mouse
+ ///is released.
+ CTimelineControl *m_TimelineControl;
+ CTimeMeasure *m_TimeMeasure;
+ CScalableScroller *m_Scroller;
+ CFlowLayout *m_TimebarList;
+ double m_TimeRatio;
+
+ TTimelineRowList m_Rows;
+ CPlayhead m_Playhead;
+ CSnapper m_Snapper;
+ bool m_IsLayoutChanged; ///< flag to keep track of a need for a delayed RecalcLayout
+
+ bool m_IsMouseDown;
+ CPt m_DragBeginPoint;
+ CAreaBoundingRect *m_BoundingRect;
+
+ TSlideRatioMap m_TimelineRatio; ///< stores the time zooming ratios for each slide
+};
+#endif // INCLUDED_TIMELINE_TIMELINE_LAYOUT_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.cpp b/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.cpp
new file mode 100644
index 00000000..1229b88b
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.cpp
@@ -0,0 +1,360 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "TimelineTreeLayout.h"
+#include "FlowLayout.h"
+#include "Scroller.h"
+#include "StateRow.h"
+#include "FilterToolbar.h"
+#include "ToggleToolbar.h"
+#include "TimeToolbar.h"
+#include "StudioPreferences.h"
+#include "TimelineControl.h"
+#include "Renderer.h"
+#include "ToggleBlankControl.h"
+#include "ColorBlankControl.h"
+#include "TreeBlankControl.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimelineTreeLayout::CTimelineTreeLayout(CTimelineControl *inTimelineControl, IDoc *inDoc)
+ : m_IsScrolling(false)
+{
+ m_TimelineControl = inTimelineControl;
+
+ m_ColorScroller = new CScroller();
+ m_ColorScroller->SetHorizontalScrollMode(CScroller::NEVER);
+ m_ColorScroller->SetVerticalScrollMode(CScroller::NEVER);
+ m_ColorScroller->AddScrollListener(this);
+
+ m_ColorBlankControl = new CColorBlankControl();
+ m_ColorList = new CFlowLayout(m_ColorBlankControl);
+ m_ColorScroller->AddChild(m_ColorList);
+
+ m_ToggleScroller = new CScroller();
+ m_ToggleScroller->SetHorizontalScrollMode(CScroller::NEVER);
+ m_ToggleScroller->SetVerticalScrollMode(CScroller::NEVER);
+
+ m_ToggleBlankControl = new CToggleBlankControl();
+ m_ToggleList = new CFlowLayout(m_ToggleBlankControl);
+ m_ToggleScroller->AddChild(m_ToggleList);
+ m_ToggleScroller->AddScrollListener(this);
+
+ m_TreeScroller = new CScroller();
+ m_TreeScroller->SetVerticalScrollMode(CScroller::NEVER);
+ m_TreeScroller->SetHorizontalScrollMode(CScroller::ALWAYS);
+ m_TreeScroller->AddScrollListener(this);
+
+ m_TreeBlankControl = new CTreeBlankControl();
+ m_TreeList = new CFlowLayout(m_TreeBlankControl);
+ m_TreeScroller->AddChild(m_TreeList);
+
+ m_FilterToolbar = new CFilterToolbar(this);
+ m_TimeToolbar = new CTimeToolbar(inDoc);
+ m_ToggleToolbar = new CToggleToolbar(this);
+
+ AddChild(m_TreeScroller);
+ AddChild(m_ToggleScroller);
+ AddChild(m_TimeToolbar);
+ AddChild(m_ToggleToolbar);
+ AddChild(m_ColorScroller);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimelineTreeLayout::~CTimelineTreeLayout()
+{
+ m_ColorScroller->RemoveChild(m_ColorList);
+ m_ToggleScroller->RemoveChild(m_ToggleList);
+ m_TreeScroller->RemoveChild(m_TreeList);
+
+ RemoveChild(m_TreeScroller);
+ RemoveChild(m_ToggleScroller);
+ RemoveChild(m_TimeToolbar);
+ RemoveChild(m_FilterToolbar);
+ RemoveChild(m_ToggleToolbar);
+ RemoveChild(m_ColorScroller);
+ // RemoveChild( m_BreadCrumbToolbar );
+
+ delete m_FilterToolbar;
+ delete m_TimeToolbar;
+ delete m_ToggleToolbar;
+ delete m_ColorScroller;
+ delete m_ColorList;
+ delete m_ToggleScroller;
+ delete m_ToggleList;
+ delete m_TreeList;
+ delete m_TreeScroller;
+ // delete m_BreadCrumbToolbar;
+
+ // Delete all the rows, this control is responsible for the rows, maybe it should not
+ // be but currently it is.
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = (*thePos);
+ theRow->Dispose(); // Dispose will delete the row as well
+ }
+}
+
+//=============================================================================
+/**
+ * Clear out all the contents of this tree layout.
+ * This will also delete all the rows, so make sure it is called after the
+ * TimelineTimelineLayout::ClearRows is called.
+ */
+void CTimelineTreeLayout::ClearRows()
+{
+ TTimelineRowList::iterator thePos = m_Rows.begin();
+ for (; thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = (*thePos);
+ m_ColorList->RemoveChild(theRow->GetColorControl());
+ m_TreeList->RemoveChild(theRow->GetTreeControl());
+ m_ToggleList->RemoveChild(theRow->GetToggleControl());
+
+ theRow->Dispose();
+ }
+
+ m_Rows.clear();
+}
+
+//=============================================================================
+/**
+ * Set the filter back to it's default state.
+ */
+void CTimelineTreeLayout::ResetFilter()
+{
+ m_FilterToolbar->FilterBehaviors(false);
+ m_FilterToolbar->FilterProperties(false);
+ m_FilterToolbar->FilterMaterials(false);
+ m_FilterToolbar->FilterShy(false);
+}
+
+//=============================================================================
+/**
+ * Set the size of this control.
+ * Overrides CControl::SetSize so that RecalcLayout can be called.
+ */
+void CTimelineTreeLayout::SetSize(CPt inSize)
+{
+ if (inSize != GetSize()) {
+ CControl::SetSize(inSize);
+
+ RecalcLayout();
+ }
+}
+
+//=============================================================================
+/**
+ * Recalculate the layout of all the child components.
+ * Called when this changes size and all the children need to be repositioned.
+ */
+void CTimelineTreeLayout::RecalcLayout()
+{
+ CPt mySize = GetSize();
+ long theHeaderHeight = CStudioPreferences::GetHeaderHeight();
+
+ m_FilterToolbar->SetSize(CPt(120, theHeaderHeight));
+ m_FilterToolbar->SetPosition(0, 0);
+
+ m_ToggleToolbar->SetSize(CPt(61, theHeaderHeight));
+ m_ToggleToolbar->SetPosition(mySize.x - m_ToggleToolbar->GetSize().x, 0);
+
+ m_TimeToolbar->SetSize(CPt(mySize.x - m_ToggleToolbar->GetSize().x, theHeaderHeight));
+ m_TimeToolbar->SetPosition(0, 0);
+
+ m_ColorScroller->SetSize(CPt(CStudioPreferences::GetRowSize(), mySize.y - theHeaderHeight
+ - m_TreeScroller->GetHorizontalBar()->GetMinimumSize().y
+ - theHeaderHeight));
+ m_ColorScroller->SetPosition(0, theHeaderHeight);
+
+ m_ToggleScroller->SetSize(CPt(m_ToggleToolbar->GetSize().x, mySize.y - theHeaderHeight
+ - m_TreeScroller->GetHorizontalBar()->GetMinimumSize().y
+ - theHeaderHeight));
+ m_ToggleScroller->SetPosition(mySize.x - m_ToggleScroller->GetSize().x, theHeaderHeight);
+
+ m_TreeScroller->SetSize(
+ CPt(mySize.x, mySize.y - m_FilterToolbar->GetSize().y - theHeaderHeight));
+ m_TreeScroller->SetPosition(0, theHeaderHeight);
+
+ m_TreeScroller->SetAdditionalClippingRect(
+ CRct(m_ColorScroller->GetSize().x, 0,
+ m_ToggleScroller->GetPosition().x - m_ColorScroller->GetSize().x,
+ m_TreeScroller->GetSize().y));
+}
+
+//=============================================================================
+/**
+ * Add another top level item to the left side of the timeline.
+ * If there is already a top level item then this one will be appended to the
+ * list.
+ * @param inRow the row to be added.
+ */
+void CTimelineTreeLayout::AddRow(CTimelineRow *inRow)
+{
+ m_ColorList->AddChild(inRow->GetColorControl());
+ m_TreeList->AddChild(inRow->GetTreeControl());
+ m_ToggleList->AddChild(inRow->GetToggleControl());
+
+ m_Rows.push_back(inRow);
+
+ inRow->SetIndent(20);
+
+ inRow->Filter(m_Filter);
+}
+
+//=============================================================================
+/**
+ * Applies the current filter settings to the timeline. Although the filter
+ * preferences can be set independently, they are not actually applied until
+ * this function is called.
+ */
+void CTimelineTreeLayout::Filter()
+{
+ for (TTimelineRowList::iterator thePos = m_Rows.begin(); thePos != m_Rows.end(); ++thePos) {
+ CTimelineRow *theRow = *thePos;
+ theRow->Filter(m_Filter);
+ }
+
+ // TODO: sk - it is unclear to me what this is trying to do.. I am leavint this here till it
+ // becomes obvious it is totally redundant OR someone finds a related bug
+ /*
+ // Call OnSelect( ) on the selected object (if there is one) to get the timeline to scroll down.
+ CAsset* theSelectedObject = dynamic_cast<CAsset*>( m_Doc->GetSelectedObject( ) );
+ if ( theSelectedObject )
+ theSelectedObject->OnSelect( );
+
+ m_TimelineControl->GetTimelineLayout( )->RecalcLayout( );
+ */
+}
+
+void CTimelineTreeLayout::OnScroll(CScroller *inSource, CPt inScrollAmount)
+{
+ Q_UNUSED(inScrollAmount);
+
+ // SetScrollPositionY triggers another onScroll event and potentially causes the position
+ // to be set incorrectly.
+ if (!m_IsScrolling) {
+ m_IsScrolling = true;
+ m_TreeBlankControl->SetVisiblePositionX(inSource->GetVisiblePosition().x);
+ m_TimelineControl->SetScrollPositionY(inSource, inSource->GetVisiblePosition().y);
+ m_IsScrolling = false;
+ }
+}
+
+//=============================================================================
+/**
+ * Set the vertical position of all the scrollers in this view.
+ * This is used to sync up the positions with the timebar scroller view.
+ * @param inScrollPositionY the position of the scroller.
+ */
+void CTimelineTreeLayout::SetScrollPositionY(CScroller *inSource, long inScrollPositionY,
+ bool inAbsolute)
+{
+ Q_UNUSED(inSource);
+
+ if (!inAbsolute) {
+ CRct theVisibleRect(m_ColorScroller->GetVisiblePosition(),
+ m_ColorScroller->GetVisibleSize());
+ CPt thePoint(m_ColorScroller->GetVisiblePosition().x, inScrollPositionY);
+ if (!theVisibleRect.IsInRect(thePoint)) {
+ m_ColorScroller->SetVisiblePosition(
+ CPt(m_ColorScroller->GetVisiblePosition().x, inScrollPositionY));
+ m_TreeScroller->SetVisiblePosition(
+ CPt(m_TreeScroller->GetVisiblePosition().x, inScrollPositionY));
+ m_ToggleScroller->SetVisiblePosition(
+ CPt(m_ToggleScroller->GetVisiblePosition().x, inScrollPositionY));
+ }
+ } else {
+ m_ColorScroller->SetVisiblePosition(
+ CPt(m_ColorScroller->GetVisiblePosition().x, inScrollPositionY));
+ m_TreeScroller->SetVisiblePosition(
+ CPt(m_TreeScroller->GetVisiblePosition().x, inScrollPositionY));
+ m_ToggleScroller->SetVisiblePosition(
+ CPt(m_ToggleScroller->GetVisiblePosition().x, inScrollPositionY));
+ }
+}
+
+//=============================================================================
+/**
+ * Sets the time on the Time header display time.
+ * @param inTime the new time.
+ */
+void CTimelineTreeLayout::SetTime(long inTime)
+{
+ m_TimeToolbar->SetTime(inTime);
+}
+
+//=============================================================================
+/**
+ * This is overridden so the Gesture can Notify Drop Listeners that its time to Drop.
+ * If the gesture is dragging something then wee will drop.
+ */
+void CTimelineTreeLayout::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ CControl::OnMouseUp(inPoint, inFlags);
+}
+
+//=============================================================================
+/**
+ * Returns the playhead time
+ */
+long CTimelineTreeLayout::GetTime()
+{
+ return m_TimeToolbar->GetTime();
+}
+
+//=============================================================================
+/**
+ * @return rectangle describing the visible area of the tree control
+ */
+CRct CTimelineTreeLayout::GetVisibleArea()
+{
+ CPt theUpperLeftCorner;
+ CPt theSize;
+
+ theUpperLeftCorner.x = m_ColorScroller->GetSize().x;
+ theUpperLeftCorner.y = CStudioPreferences::GetHeaderHeight();
+ theSize.x = m_ToggleScroller->GetPosition().x - theUpperLeftCorner.x;
+ theSize.y = ::abs(m_ToggleScroller->GetPosition().y - m_ToggleScroller->GetSize().y)
+ - theUpperLeftCorner.y;
+
+ return CRct(theUpperLeftCorner, theSize);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.h b/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.h
new file mode 100644
index 00000000..004bf3b7
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TimelineTreeLayout.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_TIMELINE_TREE_LAYOUT_H
+#define INCLUDED_TIMELINE_TREE_LAYOUT_H 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "Control.h"
+#include "TimelineFilter.h"
+#include "Scroller.h"
+#include <vector>
+
+//=============================================================================
+// Forwards
+//=============================================================================
+class CFlowLayout;
+class CScroller;
+class CFilterToolbar;
+class CTimelineRow;
+class CToggleToolbar;
+class CTimeToolbar;
+class CTimelineControl;
+class IDoc;
+class CRenderer;
+class CToggleBlankControl;
+class CColorBlankControl;
+class CTreeBlankControl;
+
+//=============================================================================
+/**
+ * Class for tree control on the timeline palette.
+ */
+class CTimelineTreeLayout : public CControl, public CScrollListener
+{
+ typedef std::vector<CTimelineRow *> TTimelineRowList;
+
+public:
+ CTimelineTreeLayout(CTimelineControl *inTimelineControl, IDoc *inDoc);
+ virtual ~CTimelineTreeLayout();
+
+ // CControl
+ void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override;
+ void SetSize(CPt inSize) override;
+
+ virtual void AddRow(CTimelineRow *inRow);
+
+ /// Returns a filter object so that filter preferences can be set. You must call Filter() in
+ /// order to apply the filters once you make your changes.
+ CFilter *GetFilter() { return &m_Filter; }
+ void Filter();
+
+ void OnScroll(CScroller *inScroller, CPt inScrollAmount) override;
+ void SetScrollPositionY(CScroller *inSource, long inPositionY, bool inAbsolute = true);
+
+ void ClearRows();
+
+ void SetTime(long inTime);
+ long GetTime();
+ void ResetFilter();
+ CRct GetVisibleArea();
+ void RecalcLayout();
+
+protected:
+ CFilter m_Filter;
+ CFilterToolbar
+ *m_FilterToolbar; ///< Control at the top of the timeline containing filter buttons.
+ CTimeToolbar *m_TimeToolbar; ///< Control at the top containing the time display
+ CToggleToolbar
+ *m_ToggleToolbar; ///< Control at the top containing a header for the toggle column.
+
+ CScroller *m_ColorScroller;
+ CFlowLayout *m_ColorList;
+ CColorBlankControl *m_ColorBlankControl;
+
+ CScroller *m_ToggleScroller;
+ CFlowLayout *m_ToggleList;
+ CToggleBlankControl *m_ToggleBlankControl;
+
+ CScroller *m_TreeScroller;
+ CFlowLayout *m_TreeList;
+ CTreeBlankControl *m_TreeBlankControl;
+
+ TTimelineRowList m_Rows;
+ CTimelineControl *m_TimelineControl; ///< Parent control of this control
+
+ bool
+ m_IsScrolling; ///< Flag to not process onScroll that was triggered from a previous onScroll
+};
+#endif // INCLUDED_TIMELINE_TREE_LAYOUT_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.cpp b/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.cpp
new file mode 100644
index 00000000..c9ffff8e
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ToggleBlankControl.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CToggleBlankControl::CToggleBlankControl(CColor inColor)
+ : CBlankControl(inColor)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CToggleBlankControl::~CToggleBlankControl()
+{
+}
+
+//=============================================================================
+/**
+ * Handles custom drawing of the blank control underneath the tree control
+ * on the timeline palette.
+ */
+void CToggleBlankControl::Draw(CRenderer *inRenderer)
+{
+ CBlankControl::Draw(inRenderer);
+
+ // Draw the line on the right side of this control
+ CPt theSize = GetSize();
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(theSize.x - 1, 0);
+ inRenderer->LineTo(theSize.x - 1, theSize.y - 1);
+ inRenderer->PopPen();
+
+ // Draw the line on the left side of this control
+ inRenderer->PushPen(CStudioPreferences::GetPropertyFloorColor());
+ inRenderer->MoveTo(CPt(0, 0));
+ inRenderer->LineTo(CPt(0, theSize.y - 1));
+ inRenderer->PopPen();
+
+ // Draw the highlight
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(1, 0));
+ inRenderer->LineTo(CPt(1, theSize.y - 1));
+ inRenderer->PopPen();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.h b/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.h
new file mode 100644
index 00000000..ac07f7db
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleBlankControl.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TOGGLE_BLANK_CONTROL_H
+#define INCLUDED_TOGGLE_BLANK_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//=============================================================================
+/**
+ * Extends the blank control to draw items specific to the tree view side of the
+ * timeline palette.
+ */
+class CToggleBlankControl : public CBlankControl
+{
+public:
+ CToggleBlankControl(CColor inColor = CStudioPreferences::GetBaseColor());
+ virtual ~CToggleBlankControl();
+ void Draw(CRenderer *inRenderer) override;
+
+protected:
+};
+
+#endif // INCLUDED_TOGGLE_BLANK_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleControl.cpp b/src/Authoring/Studio/Palettes/Timeline/ToggleControl.cpp
new file mode 100644
index 00000000..7abc00d4
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleControl.cpp
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "ToggleControl.h"
+#include "Renderer.h"
+#include "StateRow.h"
+#include "BlankControl.h"
+#include "HotKeys.h"
+#include "Bindings/ITimelineItemBinding.h"
+
+CToggleControl::CToggleControl(CStateRow *inStateRow, ITimelineItemBinding *inTimelineItemBinding)
+ : CBlankToggleControl(inStateRow)
+ , m_TimelineItemBinding(inTimelineItemBinding)
+{
+ m_StateRow = inStateRow;
+ long theLeftOffset = 4;
+
+ m_Shy = new CToggleButton();
+ m_Shy->SetName("ShyToggle");
+ m_Shy->SetUpImage("Toggle-Empty.png");
+ m_Shy->SetDownImage("Toggle-Shy.png");
+ m_Shy->SetPosition(CPt(theLeftOffset, 0));
+ CPt theShySize = m_Shy->GetSize();
+
+ m_Visible = new CToggleButton();
+ m_Visible->SetName("VisibilityToggle");
+ m_Visible->SetUpImage("Toggle-Empty.png");
+ m_Visible->SetDownImage("Toggle-HideShow.png");
+ m_Visible->SetUpDisabledImage("Toggle-Empty.png"); // show empty if disabled
+ m_Visible->SetDisabledImage("Toggle-HideShow-disabled.png");
+ m_Visible->SetPosition(CPt(theShySize.x + 7, 0));
+ CPt theVisibleSize = m_Visible->GetSize();
+
+ m_Locked = new CToggleButton();
+ m_Locked->SetName("LockToggle");
+ m_Locked->SetUpImage("Toggle-Empty.png");
+ m_Locked->SetDownImage("Toggle-Lock.png");
+ m_Locked->SetPosition(CPt(theVisibleSize.x + theShySize.x + 10, 0));
+ CPt theLockedSize = m_Locked->GetSize();
+
+ AddChild(m_Shy);
+ AddChild(m_Visible);
+ AddChild(m_Locked);
+
+ SetAbsoluteSize(CPt(theShySize.x + theVisibleSize.x + theLockedSize.x + 1, theShySize.y));
+
+ // Button down listeners
+ m_Shy->SigToggle.connect(std::bind(&CToggleControl::OnShyClicked, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_Visible->SigToggle.connect(std::bind(&CToggleControl::OnVisibleClicked, this,
+ std::placeholders::_1, std::placeholders::_2));
+ m_Locked->SigToggle.connect(std::bind(&CToggleControl::OnLockClicked, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ ITimelineItem *theTimelineItem = m_TimelineItemBinding->GetTimelineItem();
+ // Initial toggle state of the eye visibility button
+ // Note GetViewToggleOff==FALSE means visible
+ m_Visible->SetToggleState(theTimelineItem->IsVisible());
+
+ // Initial toggle state of the button
+ m_Locked->SetToggleState(theTimelineItem->IsLocked());
+
+ // Initial toggle state of the button
+ m_Shy->SetToggleState(theTimelineItem->IsShy());
+
+ // sk - if anything, this should be checked in the data model level, not here.
+ // The tri-state (on, off, dim) of visibility depends on two bools:
+ // GetViewToggleOff and IsEnabled. Ensure that any bogus 4th permutation
+ // is detected ASAP
+ // ASSERT( !inStateRow->GetState( )->GetViewToggleOff( ) ||
+ // !inStateRow->GetState( )->IsEnabled( ) );
+}
+
+CToggleControl::~CToggleControl()
+{
+ delete m_Shy;
+ delete m_Visible;
+ delete m_Locked;
+}
+
+//==============================================================================
+/**
+ * Handles clicking on the Shy button. Toggles the shy state on the asset. Shy
+ * objects can be filtered out of the timeline.
+ */
+void CToggleControl::OnShyClicked(CToggleButton *, CToggleButton::EButtonState inButtonState)
+{
+ m_TimelineItemBinding->GetTimelineItem()->SetShy(inButtonState
+ == CToggleButton::EBUTTONSTATE_DOWN);
+
+ m_StateRow->Filter(*m_StateRow->GetFilter());
+ CBaseStateRow *theParentRow = m_StateRow->GetParentRow();
+ if (theParentRow != nullptr)
+ theParentRow->OnChildVisibilityChanged();
+}
+
+//==============================================================================
+/**
+ * Handles clicking on the Visible button. Toggles the Visible state on the asset.
+ */
+void CToggleControl::OnVisibleClicked(CToggleButton *inButton,
+ CToggleButton::EButtonState inButtonState)
+{
+ Q_UNUSED(inButton);
+
+ m_TimelineItemBinding->GetTimelineItem()->SetVisible(inButtonState
+ == CToggleButton::EBUTTONSTATE_DOWN);
+ m_StateRow->Filter(*m_StateRow->GetFilter());
+}
+
+//==============================================================================
+/**
+ * Handles clicking on the Lock button. Toggles the Lock state on the asset.
+ */
+void CToggleControl::OnLockClicked(CToggleButton *inButton,
+ CToggleButton::EButtonState inButtonState)
+{
+ Q_UNUSED(inButton);
+
+ m_TimelineItemBinding->GetTimelineItem()->SetLocked(inButtonState
+ == CToggleButton::EBUTTONSTATE_DOWN);
+ m_StateRow->Filter(*m_StateRow->GetFilter());
+}
+
+//==============================================================================
+/**
+ * Refreshes the state of this control accd to the asset state
+ */
+void CToggleControl::Refresh()
+{
+ CBlankToggleControl::Refresh();
+
+ ITimelineItem *theTimelineItem = m_TimelineItemBinding->GetTimelineItem();
+ m_Shy->SetToggleState(theTimelineItem->IsShy());
+ m_Locked->SetToggleState(theTimelineItem->IsLocked());
+
+ bool theEnabled = m_TimelineItemBinding->IsVisibleEnabled();
+ m_Visible->SetEnabled(theEnabled);
+ if (theEnabled) // only valid to update this if the enabled flag is true.
+ m_Visible->SetToggleState(theTimelineItem->IsVisible());
+
+ this->Invalidate();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleControl.h b/src/Authoring/Studio/Palettes/Timeline/ToggleControl.h
new file mode 100644
index 00000000..49caaff8
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleControl.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TOGGLE_CONTROL_H
+#define INCLUDED_TOGGLE_CONTROL_H 1
+
+#pragma once
+
+#include "BlankToggleControl.h"
+#include "ToggleButton.h"
+
+class CStateRow;
+class CBlankControl;
+class ITimelineItemBinding;
+
+class CToggleControl : public CBlankToggleControl
+{
+public:
+ CToggleControl(CStateRow *inStateRow, ITimelineItemBinding *inTimelineItemBinding);
+ virtual ~CToggleControl();
+
+ void OnShyClicked(CToggleButton *inButton, CToggleButton::EButtonState inState);
+ void OnVisibleClicked(CToggleButton *inButton, CToggleButton::EButtonState inState);
+ void OnLockClicked(CToggleButton *inButton, CToggleButton::EButtonState inState);
+
+ void Refresh() override;
+
+protected:
+ CStateRow *m_StateRow;
+
+ CToggleButton *m_Shy;
+ CToggleButton *m_Visible;
+ CToggleButton *m_Locked;
+
+ ITimelineItemBinding *m_TimelineItemBinding;
+};
+#endif // INCLUDED_TOGGLE_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.cpp b/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.cpp
new file mode 100644
index 00000000..7dadde19
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "ToggleToolbar.h"
+#include "SIcon.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+#include "TimelineTreeLayout.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CToggleToolbar::CToggleToolbar(CTimelineTreeLayout *inTreeLayout)
+ : CFlowLayout(new CBlankControl(CStudioPreferences::GetTopRowColor()))
+{
+ m_TreeLayout = inTreeLayout;
+
+ m_Color = CStudioPreferences::GetBaseColor();
+
+ SetFlowDirection(FLOW_HORIZONTAL);
+ SetAlignment(ALIGN_TOP, ALIGN_LEFT);
+ SetLeftMargin(1);
+
+ CProceduralButton<CToggleButton>::SBorderOptions theBorderOptions(false, true, true, true);
+
+ m_FltrShyBtn = new CProceduralButton<CToggleButton>();
+ m_FltrShyBtn->SetUpImage("Toggle-Shy.png");
+ m_FltrShyBtn->SetDownImage("Toggle-Shy.png");
+ m_FltrShyBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrShyBtn->SetAbsoluteSize(m_FltrShyBtn->GetSize());
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY2));
+ m_FltrShyBtn->SigToggle.connect(std::bind(&CToggleToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ AddChild(m_FltrShyBtn);
+ m_FltrShyBtn->SetToggleState(false);
+
+ m_FltrVisibleBtn = new CProceduralButton<CToggleButton>();
+ m_FltrVisibleBtn->SetUpImage("filter-toggle-eye-up.png");
+ m_FltrVisibleBtn->SetDownImage("filter-toggle-eye-down.png");
+ m_FltrVisibleBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrVisibleBtn->SetAbsoluteSize(m_FltrVisibleBtn->GetSize());
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE2));
+ m_FltrVisibleBtn->SigToggle.connect(std::bind(&CToggleToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ AddChild(m_FltrVisibleBtn);
+ m_FltrVisibleBtn->SetToggleState(false);
+
+ m_FltrLockBtn = new CProceduralButton<CToggleButton>();
+ m_FltrLockBtn->SetUpImage("Toggle-Lock.png");
+ m_FltrLockBtn->SetDownImage("Toggle-Lock.png");
+ m_FltrLockBtn->SetBorderVisibilityAll(theBorderOptions);
+ m_FltrLockBtn->SetAbsoluteSize(m_FltrLockBtn->GetSize());
+ m_FltrLockBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_LOCK2));
+ m_FltrLockBtn->SigToggle.connect(std::bind(&CToggleToolbar::OnButtonToggled, this,
+ std::placeholders::_1, std::placeholders::_2));
+ AddChild(m_FltrLockBtn);
+ m_FltrLockBtn->SetToggleState(false);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CToggleToolbar::~CToggleToolbar()
+{
+ delete m_FltrShyBtn;
+ delete m_FltrVisibleBtn;
+ delete m_FltrLockBtn;
+}
+
+//=============================================================================
+/**
+ * Fills in the background color and highlighting for this control.
+ */
+void CToggleToolbar::Draw(CRenderer *inRenderer)
+{
+ CRct theRect(0, 0, GetSize().x, GetSize().y - 1);
+ inRenderer->FillSolidRect(theRect, m_Color);
+ CFlowLayout::Draw(inRenderer);
+ inRenderer->Draw3dRect(theRect, CStudioPreferences::GetButtonShadowColor(),
+ CStudioPreferences::GetButtonShadowColor());
+
+ // Draw the highlight at the bottom
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CPt(0, theRect.size.y));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y));
+ inRenderer->PopPen();
+
+ // Draw the one pixel on the end of the highlight that has to be dark
+ // Apparently there's some problems trying to draw one pixel, so clip the excess
+ inRenderer->PushClippingRect(QRect(0, 0, GetSize().x, GetSize().y));
+ inRenderer->PushPen(CStudioPreferences::GetButtonShadowColor());
+ inRenderer->MoveTo(CPt(theRect.size.x, theRect.size.y));
+ inRenderer->LineTo(CPt(theRect.size.x - 1, theRect.size.y));
+ inRenderer->PopPen();
+ inRenderer->PopClippingRect();
+}
+
+//=============================================================================
+/**
+ * Handles turning a filter on or off in response to a button being pressed.
+ * @param inButton button that generated the event
+ * @param inState new state of the button after being toggled
+ */
+void CToggleToolbar::OnButtonToggled(CToggleButton *inButton, CButtonControl::EButtonState inState)
+{
+ bool theFilterNeedsApplied = (inState == CButtonControl::EBUTTONSTATE_UP);
+
+ if (inButton == m_FltrShyBtn)
+ FilterShy(theFilterNeedsApplied);
+ else if (inButton == m_FltrVisibleBtn)
+ FilterVisible(theFilterNeedsApplied);
+ else if (inButton == m_FltrLockBtn)
+ FilterLocked(theFilterNeedsApplied);
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for shy objects.
+ * @param inFilter true to filter shy objects out of the timeline, false to show
+ * shy objects in the timeline.
+ */
+void CToggleToolbar::FilterShy(bool inFilter)
+{
+ if (inFilter)
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY2));
+ else
+ m_FltrShyBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_SHY1));
+
+ m_TreeLayout->GetFilter()->SetShy(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for visible objects.
+ * @param inFilter true to filter visible objects out of the timeline, false to show
+ * visible objects in the timeline.
+ */
+void CToggleToolbar::FilterVisible(bool inFilter)
+{
+ if (inFilter)
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE2));
+ else
+ m_FltrVisibleBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_VISIBLE1));
+
+ m_TreeLayout->GetFilter()->SetVisible(inFilter);
+ m_TreeLayout->Filter();
+}
+
+//=============================================================================
+/**
+ * Turns filtering on and off for locked objects.
+ * @param inFilter true to filter locked objects out of the timeline, false to show
+ * locked objects in the timeline.
+ */
+void CToggleToolbar::FilterLocked(bool inFilter)
+{
+ if (inFilter)
+ m_FltrLockBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_LOCK2));
+ else
+ m_FltrLockBtn->SetTooltipText(::LoadResourceString(IDS_FLTR_TOOLTIP_LOCK1));
+
+ m_TreeLayout->GetFilter()->SetLocked(inFilter);
+ m_TreeLayout->Filter();
+}
diff --git a/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.h b/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.h
new file mode 100644
index 00000000..a0f95e62
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/ToggleToolbar.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TOGGLE_TOOLBAR_H
+#define INCLUDED_TOGGLE_TOOLBAR_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "FlowLayout.h"
+#include "ProceduralButton.h"
+#include "ToggleButton.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+class CRenderer;
+class CTimelineTreeLayout;
+// class CSIcon;
+
+//=============================================================================
+/**
+ * Control at the top of the time display and a header for the toggle column.
+ */
+class CToggleToolbar : public CFlowLayout
+{
+public:
+ CToggleToolbar(CTimelineTreeLayout *inTreeLayout);
+ virtual ~CToggleToolbar();
+ void Draw(CRenderer *inRenderer) override;
+
+ void FilterShy(bool inFilter);
+ void FilterVisible(bool inFilter);
+ void FilterLocked(bool inFilter);
+ void OnButtonToggled(CToggleButton *inButton, CToggleButton::EButtonState inState);
+
+protected:
+ // CSIcon* m_ShyIcon;
+ // CSIcon* m_VisibilityIcon;
+ // CSIcon* m_LockIcon;
+ CProceduralButton<CToggleButton> *m_FltrShyBtn;
+ CProceduralButton<CToggleButton> *m_FltrVisibleBtn;
+ CProceduralButton<CToggleButton> *m_FltrLockBtn;
+ CTimelineTreeLayout *m_TreeLayout;
+ CColor m_Color;
+};
+
+#endif // INCLUDED_TOGGLE_TOOLBAR_H
diff --git a/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.cpp b/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.cpp
new file mode 100644
index 00000000..b49eabb5
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.cpp
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "TreeBlankControl.h"
+#include "Renderer.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTreeBlankControl::CTreeBlankControl(CColor inColor)
+ : CBlankControl(inColor)
+ , m_LeftEdgeOffset(0)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTreeBlankControl::~CTreeBlankControl()
+{
+}
+
+//=============================================================================
+/**
+ * Handles custom drawing of the blank control underneath the tree control
+ * on the timeline palette.
+ */
+void CTreeBlankControl::Draw(CRenderer *inRenderer)
+{
+ CBlankControl::Draw(inRenderer);
+
+ // Draw the highlight on the left side of this control
+ CPt theSize = GetSize();
+ inRenderer->PushPen(CStudioPreferences::GetButtonHighlightColor());
+ inRenderer->MoveTo(CStudioPreferences::GetRowSize() + m_LeftEdgeOffset, 0);
+ inRenderer->LineTo(CStudioPreferences::GetRowSize() + m_LeftEdgeOffset, theSize.y - 1);
+ inRenderer->PopPen();
+}
+
+//=============================================================================
+/**
+ * xxx
+ */
+void CTreeBlankControl::SetVisiblePositionX(long inPosition)
+{
+ m_LeftEdgeOffset = inPosition;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.h b/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.h
new file mode 100644
index 00000000..0d0df3ab
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/TreeBlankControl.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_TREE_BLANK_CONTROL_H
+#define INCLUDED_TREE_BLANK_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BlankControl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CRenderer;
+
+//=============================================================================
+/**
+ * Extends the blank control to draw items specific to the tree view side of the
+ * timeline palette.
+ */
+class CTreeBlankControl : public CBlankControl
+{
+public:
+ CTreeBlankControl(CColor inColor = CStudioPreferences::GetBaseColor());
+ virtual ~CTreeBlankControl();
+ void Draw(CRenderer *inRenderer) override;
+ void SetVisiblePositionX(long inPosition);
+
+protected:
+ long m_LeftEdgeOffset; ///< Visibility offset of the left edge in case timeline is scrolled
+ ///horizontally
+};
+
+#endif // INCLUDED_TREE_BLANK_CONTROL_H
diff --git a/src/Authoring/Studio/Palettes/controls/BrowserCombo.qml b/src/Authoring/Studio/Palettes/controls/BrowserCombo.qml
new file mode 100644
index 00000000..0e8bcb55
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/BrowserCombo.qml
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.8
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+MouseArea {
+ id: root
+
+ property alias value: value.text
+ property var activeBrowser
+ property bool blockShow: false
+
+ signal showBrowser
+
+ Layout.minimumHeight: _controlBaseHeight
+ Layout.preferredWidth: _valueWidth
+
+ onPressed: {
+ // Block showBrowser event on the mouse press that makes the browser lose focus
+ if (activeBrowser && activeBrowser.visible) {
+ activeBrowser = null;
+ blockShow = true
+ } else {
+ blockShow = false
+ }
+ }
+
+ onClicked: {
+ if (!blockShow)
+ root.showBrowser()
+ }
+
+ Rectangle {
+ anchors.fill: parent
+
+ color: _studioColor2
+
+ StyledLabel {
+ id: value
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignLeft
+ rightPadding: 6 + img.width
+ leftPadding: 6
+ }
+ Image {
+ id: img
+ x: parent.width - sourceSize.width - 2
+ y: (parent.height - sourceSize.height) / 2
+ source: _resDir + "arrow_down.png"
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/FloatTextField.qml b/src/Authoring/Studio/Palettes/controls/FloatTextField.qml
new file mode 100644
index 00000000..bea65443
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/FloatTextField.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+/* Use for: Position, Rotation, Scale, Pivot ... */
+
+TextField {
+ id: floatTextFieldId
+ property alias decimalValue: validator.decimals
+
+ signal wheelEventFinished
+
+ selectByMouse: true
+ text: "0.000"
+ Layout.preferredWidth: _valueWidth / 2
+ Layout.preferredHeight: _controlBaseHeight
+
+ topPadding: 0
+ bottomPadding: 0
+ rightPadding: 6
+
+ horizontalAlignment: TextInput.AlignRight
+ verticalAlignment: TextInput.AlignVCenter
+ validator: DoubleValidator {
+ id: validator
+ decimals: 3
+ locale: "C"
+ }
+
+ selectionColor: _selectionColor
+ selectedTextColor: _textColor
+ font.pixelSize: _fontSize
+ color: _textColor
+ background: Rectangle {
+ color: floatTextFieldId.enabled ? _studioColor2 : "transparent"
+ border.width: floatTextFieldId.activeFocus ? 1 : 0
+ border.color: floatTextFieldId.activeFocus ? _selectionColor : _disabledColor
+ }
+
+ MouseArea {
+ property int _clickedPos
+ id: mouseAreaBaseId
+
+ anchors.fill: parent
+ onPressed: {
+ parent.forceActiveFocus()
+ _clickedPos = parent.positionAt(mouse.x, mouse.y)
+ parent.cursorPosition = _clickedPos
+ }
+ onDoubleClicked: parent.selectAll()
+ onPositionChanged: {
+ parent.cursorPosition = parent.positionAt(mouse.x, mouse.y)
+ parent.select(_clickedPos, parent.cursorPosition)
+ }
+
+ onWheel: {
+ if (!floatTextFieldId.activeFocus) {
+ wheel.accepted = false
+ return
+ }
+ if (wheel.angleDelta.y > 0) {
+ floatTextFieldId.text++
+ } else {
+ floatTextFieldId.text--
+ }
+ wheel.accepted=true
+ floatTextFieldId.wheelEventFinished();
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledComboBox.qml b/src/Authoring/Studio/Palettes/controls/StyledComboBox.qml
new file mode 100644
index 00000000..ff079f41
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledComboBox.qml
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import QtQuick.Window 2.2
+
+ComboBox {
+ id: control
+ Layout.preferredHeight: _controlBaseHeight
+ Layout.preferredWidth: _valueWidth
+ topPadding: 0
+ bottomPadding: 0
+
+ delegate: ItemDelegate {
+ id: itemDelegate
+
+ property bool hasSeparator: itemDelegate.text.endsWith("|separator")
+
+ width: parent.width
+ height: hasSeparator ? _controlBaseHeight + 6 : _controlBaseHeight
+ padding: 0
+ spacing: 0
+ text: {
+ control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
+ : model[control.textRole])
+ : modelData
+ }
+ highlighted: control.highlightedIndex === index
+ hoverEnabled: control.hoverEnabled
+ contentItem: ColumnLayout {
+ anchors.fill: itemDelegate
+ spacing: 0
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 1
+ color: _studioColor3
+ visible: itemDelegate.hasSeparator
+ }
+ StyledLabel {
+ Layout.fillWidth: true
+ rightPadding: control.indicator.width + 6
+ leftPadding: 6
+ text: {
+ hasSeparator ? itemDelegate.text.replace("|separator", "")
+ : itemDelegate.text
+ }
+ visible: itemDelegate.text
+ horizontalAlignment: Text.AlignLeft
+ }
+ }
+ background: Rectangle {
+ anchors.fill: itemDelegate
+ color: hovered ? _selectionColor : _studioColor2
+ }
+ }
+
+ indicator: Image {
+ x: control.width - width - 2
+ y: control.topPadding + (control.availableHeight - height) / 2
+ source: _resDir + "arrow_down.png"
+ sourceSize.width: width
+ sourceSize.height: height
+ }
+
+ contentItem: StyledTextField {
+ text: {
+ var newText = control.editable ? control.editText : control.displayText;
+ var hasSeparator = newText.endsWith("|separator");
+ hasSeparator ? newText.replace("|separator", "") : newText;
+ }
+
+ enabled: control.editable
+ autoScroll: control.editable
+ readOnly: control.popup.visible
+ inputMethodHints: control.inputMethodHints
+ validator: control.validator
+ opacity: 1
+ leftPadding: 6
+ horizontalAlignment: Text.AlignLeft
+ }
+
+ background: Rectangle {
+ color: control.enabled ? _studioColor2 : "transparent"
+ border.width: 0
+ }
+
+ popup: Popup {
+ y: control.height
+ width: control.width
+ height: Math.min(contentItem.implicitHeight,
+ control.Window.height - topMargin - bottomMargin)
+ topMargin: 6
+ bottomMargin: 6
+ padding: 0
+
+ contentItem: ListView {
+ clip: true
+ implicitHeight: contentHeight
+ model: control.popup.visible ? control.delegateModel : null
+ currentIndex: control.highlightedIndex
+ highlightRangeMode: ListView.ApplyRange
+ highlightMoveDuration: 0
+ ScrollIndicator.vertical: ScrollIndicator {
+ id: scrollIndicator
+ contentItem: Rectangle {
+ id: indicator
+
+ implicitWidth: 2
+ implicitHeight: 2
+
+ color: _studioColor3
+ visible: scrollIndicator.size < 1.0
+ opacity: 0.75
+ }
+ }
+ Rectangle {
+ z: 10
+ anchors.fill: parent
+ color: "transparent"
+ border.color: _studioColor3
+ }
+ }
+
+ background: Rectangle {
+ color: _studioColor2
+ }
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledLabel.qml b/src/Authoring/Studio/Palettes/controls/StyledLabel.qml
new file mode 100644
index 00000000..02016069
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledLabel.qml
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+Label {
+ id: styledLabelId
+ font.pixelSize: _fontSize
+ color: _textColor
+ Layout.preferredHeight: _controlBaseHeight
+ Layout.preferredWidth: _idWidth
+ verticalAlignment: Text.AlignVCenter
+ clip: true
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledMenuItem.qml b/src/Authoring/Studio/Palettes/controls/StyledMenuItem.qml
new file mode 100644
index 00000000..361d5876
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledMenuItem.qml
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+MenuItem {
+ id: control
+ hoverEnabled: true
+
+ contentItem: StyledLabel {
+ text: control.text
+ visible: control.text
+ horizontalAlignment: Text.AlignLeft
+ color: control.enabled ? _textColor : _disabledColor
+ }
+ background: Rectangle {
+ implicitWidth: _valueWidth
+ implicitHeight: _controlBaseHeight
+ color: control.hovered ? _selectionColor : _studioColor1
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledMenuSeparator.qml b/src/Authoring/Studio/Palettes/controls/StyledMenuSeparator.qml
new file mode 100644
index 00000000..532fee2c
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledMenuSeparator.qml
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+
+MenuSeparator {
+ id: control
+ padding: 0
+ topPadding: 4
+ bottomPadding: 4
+ leftPadding: 0
+ rightPadding: 0
+ contentItem: Rectangle {
+ width: control.width
+ implicitWidth: control.parent.width - control.leftPadding - control.rightPadding
+ implicitHeight: 1
+ color: _studioColor3
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledTextField.qml b/src/Authoring/Studio/Palettes/controls/StyledTextField.qml
new file mode 100644
index 00000000..da621314
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledTextField.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+import QtQuick.Layouts 1.3
+
+TextField {
+ id: styledTextFieldId
+
+ property alias backgroundColor: textBackground.color
+
+ Layout.preferredWidth: _valueWidth
+ Layout.preferredHeight: _controlBaseHeight
+ horizontalAlignment: TextInput.AlignRight
+ verticalAlignment: TextInput.AlignVCenter
+
+ topPadding: 0
+ bottomPadding: 0
+ rightPadding: 6
+
+ selectByMouse: true
+ selectionColor: _selectionColor
+ selectedTextColor: _textColor
+
+ font.pixelSize: _fontSize
+ color: _textColor
+ background: Rectangle {
+ id: textBackground
+ color: styledTextFieldId.enabled ? _studioColor2 : "transparent"
+ border.width: styledTextFieldId.activeFocus ? 1 : 0
+ border.color: styledTextFieldId.activeFocus ? _selectionColor : _disabledColor
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledToolButton.qml b/src/Authoring/Studio/Palettes/controls/StyledToolButton.qml
new file mode 100644
index 00000000..51654331
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledToolButton.qml
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+
+ToolButton {
+ id: control
+
+ property string enabledImage
+ property string disabledImage
+ property alias toolTipText: toolTip.text
+
+ hoverEnabled: true
+
+ StyledTooltip {
+ id: toolTip
+ visible: control.hovered
+ }
+
+ background: Rectangle {
+ color: control.pressed ? _selectionColor : (hovered ? _studioColor1 : "transparent")
+ border.color: _studioColor1
+ }
+
+ contentItem: Image {
+ source: control.enabled ? _resDir + enabledImage : _resDir + disabledImage
+ }
+}
diff --git a/src/Authoring/Studio/Palettes/controls/StyledTooltip.qml b/src/Authoring/Studio/Palettes/controls/StyledTooltip.qml
new file mode 100644
index 00000000..cae9c59f
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/controls/StyledTooltip.qml
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.1
+
+ToolTip {
+ id: control
+ delay: 500
+ contentItem: StyledLabel {
+ text: control.text
+ }
+
+ background: Rectangle {
+ border.color: _studioColor3
+ color: _studioColor2
+ radius: 2
+ }
+}
diff --git a/src/Authoring/Studio/PreviewHelper.cpp b/src/Authoring/Studio/PreviewHelper.cpp
new file mode 100644
index 00000000..271daf4d
--- /dev/null
+++ b/src/Authoring/Studio/PreviewHelper.cpp
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PreviewHelper.h"
+#include "StudioApp.h"
+#include "Dialogs.h"
+#include "Dispatch.h"
+#include "Doc.h"
+#include "StudioPreferences.h"
+#include "StudioProjectSettings.h"
+#include "StudioProjectVariables.h"
+#include "Core.h"
+#include "UICFileTools.h"
+
+#include <QInputDialog>
+#include <QMessageBox>
+#include <QProcess>
+
+#include "remoteproject.h"
+
+//=============================================================================
+/**
+ * PKC : Yes, we're duplicating functionality found in UICStateApplication
+ * but we want to eliminate as many dependencies on UICState as possible in Studio.
+ */
+
+Q3DStudio::CString CPreviewHelper::GetLaunchFile(const Q3DStudio::CString &inUipPath)
+{
+ Q3DStudio::CFilePath theUipPath(inUipPath);
+
+ Q3DStudio::CString theDir = theUipPath.GetDirectory();
+ Q3DStudio::CString theStem = theUipPath.GetFileStem();
+
+ // Check if a corresponding .UIA file actually exists
+ theDir.append('/');
+ Q3DStudio::CString idealPath = theDir + theStem + Q3DStudio::CString(".uia");
+
+ Q3DStudio::CFilePath theUiaPath(idealPath);
+
+ return (theUiaPath.IsFile()) ? idealPath : inUipPath;
+}
+
+//=============================================================================
+/**
+ * Callback for previewing a presentation.
+ */
+void CPreviewHelper::OnPreview()
+{
+ Q3DStudio::CBuildConfigurations &theConfigurations =
+ g_StudioApp.GetCore()->GetBuildConfigurations();
+ Q3DStudio::CBuildConfiguration *theBuildConfiguration =
+ theConfigurations.GetConfiguration(CStudioPreferences::GetPreviewConfig());
+ if (theBuildConfiguration)
+ PreviewViaConfig(theBuildConfiguration, EXECMODE_PREVIEW);
+}
+
+//=============================================================================
+/**
+ * Callback for deploying a presentation.
+ */
+void CPreviewHelper::OnDeploy(RemoteProject &project)
+{
+ Q3DStudio::CBuildConfigurations &theConfigurations =
+ g_StudioApp.GetCore()->GetBuildConfigurations();
+ Q3DStudio::CBuildConfiguration *theBuildConfiguration =
+ theConfigurations.GetConfiguration(CStudioPreferences::GetPreviewConfig());
+ if (theBuildConfiguration) {
+ // ItemDataPtr != nullptr ==> Build configurations specified NANT pipeline exporter
+ PreviewViaConfig(theBuildConfiguration, EXECMODE_DEPLOY, &project);
+ }
+}
+
+//=============================================================================
+/**
+ * Previewing a presentation using the build configurations loaded.
+ * This involves 2 steps:
+ * 1 Export the presentation using the specified exporter.
+ * 2 Viewing the exported content following the command specified in the configuration.
+ */
+void CPreviewHelper::PreviewViaConfig(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ EExecMode inMode, RemoteProject *project)
+{
+ bool theUsingTempFile;
+ CUICFile theDocument = GetDocumentFile(theUsingTempFile);
+ CCore *theCore = g_StudioApp.GetCore();
+ try {
+ if (theCore->GetDoc()->IsModified()) {
+ theCore->OnSaveDocument(theDocument);
+ }
+
+ DoPreviewViaConfig(inSelectedConfig, theDocument.GetAbsolutePath(),
+ inMode, project);
+ } catch (...) {
+ theCore->GetDispatch()->FireOnProgressEnd();
+ g_StudioApp.GetDialogs()->DisplaySaveReadOnlyFailed(theDocument);
+ }
+}
+
+//=============================================================================
+/**
+ * Launch a viewing app to preview a file.
+ * @param inDocumentFile File to be previewed
+ */
+void CPreviewHelper::DoPreviewViaConfig(Q3DStudio::CBuildConfiguration * /*inSelectedConfig*/,
+ const Q3DStudio::CString &inDocumentFile,
+ EExecMode inMode,
+ RemoteProject *project)
+{
+ using namespace Q3DStudio;
+ Q3DStudio::CString theCommandStr;
+
+ if (inMode == EXECMODE_DEPLOY) {
+ Q_ASSERT(project);
+ project->streamProject(inDocumentFile.GetCharStar());
+ } else if (inMode == EXECMODE_PREVIEW
+ && CStudioPreferences::GetPreviewProperty("PLATFORM") == "PC") {
+ // Quick Preview on PC without going via NANT
+ CFilePath theCurrentPath(CUICFile::GetApplicationDirectory().GetAbsolutePath());
+ CFilePath theViewerDir = CFilePath::fromQString(QApplication::applicationDirPath());
+ if (!theViewerDir.IsDirectory()) {
+ // theMainDir = theCurrentPath.GetDirectory().GetDirectory();
+ // theViewerDir = CFilePath::CombineBaseAndRelative( theMainDir, CFilePath(
+ // L"Runtime/Build/Bin/Win32" ) );
+ theViewerDir = theCurrentPath.GetDirectory(); // Developing directory
+ }
+
+ Q3DStudio::CString theDocumentFile = CPreviewHelper::GetLaunchFile(inDocumentFile);
+#ifdef Q_OS_WIN
+ Q3DStudio::CString theViewerFile = "Qt3DViewer.exe";
+ theCommandStr = theViewerDir + "\\" + theViewerFile;
+#else
+#ifdef Q_OS_MACOS
+ Q3DStudio::CString theViewerFile = "../../../Qt3DViewer.app/Contents/MacOS/Qt3DViewer";
+#else
+ Q3DStudio::CString theViewerFile = "Qt3DViewer";
+#endif
+ theCommandStr = theViewerDir + "/" + theViewerFile;
+#endif
+
+ QProcess *p = new QProcess;
+ auto finished = static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished);
+ QObject::connect(p, finished, p, &QObject::deleteLater);
+ p->start(theCommandStr.toQString(), { theDocumentFile.toQString() });
+
+ if (!p->waitForStarted()) {
+ QMessageBox::critical(nullptr, QObject::tr("Error Launching Viewer"), QObject::tr("%1 failed with error: %2")
+ .arg(theViewerFile.toQString()).arg(p->errorString()));
+ delete p;
+ return;
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Interpret the string by resolving the variables.
+ * @param inSourceString String to be interpreted
+ */
+Q3DStudio::CString CPreviewHelper::InterpretString(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ const Q3DStudio::CString &inDocumentFile,
+ const Q3DStudio::CString &inSourceString)
+{
+ Q3DStudio::CString theReturnString;
+ long theStart = 0; // start index of string
+ long theBeginIndex = 0; // index of '%'
+ long theEndIndex = 0; // index of '%'
+ while (Q3DStudio::CString::ENDOFSTRING != theEndIndex) {
+ theBeginIndex = inSourceString.Find('%', theStart);
+ if (Q3DStudio::CString::ENDOFSTRING != theBeginIndex) {
+ theReturnString += inSourceString.Extract(theStart, theBeginIndex - theStart);
+ // find the corresponding '%'
+ theEndIndex = inSourceString.Find('%', theBeginIndex + 1);
+ if (Q3DStudio::CString::ENDOFSTRING != theEndIndex) {
+ // first, resolve the variable by the toolbar selection
+ Q3DStudio::CString theVariable =
+ inSourceString.Extract(theBeginIndex + 1, theEndIndex - theBeginIndex - 1);
+ Q3DStudio::CString theResolvedVariable;
+ bool theHasResolved = ResolveVariable(inSelectedConfig, inDocumentFile, theVariable,
+ theResolvedVariable);
+
+ if (theHasResolved) {
+ theReturnString += theResolvedVariable;
+ } else {
+ theReturnString += "_NULL_";
+ }
+ theStart = theEndIndex + 1;
+ } else
+ theReturnString += inSourceString.Extract(theBeginIndex);
+ } else {
+ theEndIndex = theBeginIndex;
+ theReturnString += inSourceString.Extract(theStart);
+ }
+ }
+
+ return theReturnString;
+}
+
+//==============================================================================
+/**
+ * Resolves the passed in variable and write out the resolved value if it exists
+ * in the current selected build configuration.
+ * @param inVariable the environment to be resolved
+ * @param outValue the string to receive the resolved value
+ * @return true if the variable exists, else false
+ */
+//==============================================================================
+bool CPreviewHelper::ResolveVariable(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ const Q3DStudio::CString &inDocumentFile,
+ const Q3DStudio::CString &inVariable,
+ Q3DStudio::CString &outValue)
+{
+ Q3DStudio::CString theReturnStr;
+ bool theHasResolved = false;
+
+ // Handle special variable
+ if (inVariable == "BUILDFILE") {
+ if (inSelectedConfig) {
+ theReturnStr = inSelectedConfig->GetPath();
+ theHasResolved = true;
+ }
+ } else if (inVariable == "UIPFILE") {
+ theReturnStr = inDocumentFile;
+ theHasResolved = true;
+ }
+
+ if (!theHasResolved) {
+ Q3DStudio::CString theValue = CStudioPreferences::GetPreviewProperty(inVariable);
+ if (theValue != "") {
+ theReturnStr = theValue;
+ theHasResolved = true;
+ }
+ }
+
+ if (theHasResolved)
+ outValue = InterpretString(inSelectedConfig, inDocumentFile, theReturnStr);
+
+ return theHasResolved;
+}
+
+//=============================================================================
+/**
+ * Gets a file to be previewed or conditioned based on the current document.
+ * If the document has not been saved yet (and thus does not have a name), a temp file is used.
+ * If the document has been saved but it's dirty, a temp file based on the current document is
+ *used (same path but different extension).
+ * @param outUsingTempFile if temp file if used
+ * @return the document file to be previewed or conditioned
+ */
+CUICFile CPreviewHelper::GetDocumentFile(bool &outUsingTempFile)
+{
+ CUICFile theDocument = g_StudioApp.GetCore()->GetDoc()->GetDocumentPath();
+ CUICFile theResult("");
+ theResult = theDocument;
+ outUsingTempFile = false;
+ return theResult;
+}
diff --git a/src/Authoring/Studio/PreviewHelper.h b/src/Authoring/Studio/PreviewHelper.h
new file mode 100644
index 00000000..e68e51d9
--- /dev/null
+++ b/src/Authoring/Studio/PreviewHelper.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PREVIEW_HELPER
+#define INCLUDED_PREVIEW_HELPER 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICFile.h"
+#include "UICString.h"
+#include "remoteproject.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CHotKeys;
+
+namespace Q3DStudio {
+class CBuildConfiguration;
+}
+
+//==============================================================================
+/**
+ * @class CPreviewHelper
+ */
+class CPreviewHelper
+{
+public:
+ enum EExecMode {
+ EXECMODE_PREVIEW, ///< Preview
+ EXECMODE_DEPLOY, ///< Deploy
+ };
+
+public:
+ static void OnPreview();
+ static void OnDeploy(RemoteProject &project);
+ static void PreviewViaConfig(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ EExecMode inMode, RemoteProject *project = 0);
+ static void DoPreviewViaConfig(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ const Q3DStudio::CString &inDocumentFile,
+ EExecMode inMode,
+ RemoteProject *project = 0);
+
+protected:
+ static Q3DStudio::CString InterpretString(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ const Q3DStudio::CString &inDocumentFile,
+ const Q3DStudio::CString &inSourceString);
+ static bool ResolveVariable(Q3DStudio::CBuildConfiguration *inSelectedConfig,
+ const Q3DStudio::CString &inDocumentFile,
+ const Q3DStudio::CString &inVariable, Q3DStudio::CString &outValue);
+ static CUICFile GetDocumentFile(bool &outUsingTempFile);
+ static Q3DStudio::CString GetLaunchFile(const Q3DStudio::CString &inDocFile);
+};
+
+#endif
diff --git a/src/Authoring/Studio/Render/IStudioRenderer.h b/src/Authoring/Studio/Render/IStudioRenderer.h
new file mode 100644
index 00000000..2dabbd08
--- /dev/null
+++ b/src/Authoring/Studio/Render/IStudioRenderer.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_RENDERER
+#define UIC_STUDIO_RENDERER
+#pragma once
+
+#include "IDocSceneGraph.h"
+#include <EASTL/vector.h>
+#include <EASTL/string.h>
+
+#include <QRect>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+QT_END_NAMESPACE
+
+namespace Q3DStudio {
+using qt3ds::QT3DSI32;
+class IStudioRenderer : public IDocSceneGraph
+{
+protected:
+ virtual ~IStudioRenderer() {}
+
+public:
+ friend class std::shared_ptr<IStudioRenderer>;
+
+ virtual bool IsInitialized() = 0;
+
+ virtual void Initialize(QWidget *inWindow) = 0;
+
+ virtual void SetViewRect(const QRect &inRect) = 0;
+
+ virtual void GetEditCameraList(QStringList &outCameras) = 0;
+ virtual void SetEnableEditLight(bool inEnableLight) = 0;
+ virtual bool IsEditLightEnabled() const = 0;
+ virtual bool DoesEditCameraSupportRotation(QT3DSI32 inIndex) = 0;
+ virtual bool AreGuidesEnabled() const = 0;
+ virtual void SetGuidesEnabled(bool val) = 0;
+ virtual bool AreGuidesEditable() const = 0;
+ virtual void SetGuidesEditable(bool val) = 0;
+ // Setting the camera to -1 disables the edit cameras
+ // So setting the camera to 0- (numcameras - 1) will set change the active
+ // edit camera.
+ virtual void SetEditCamera(QT3DSI32 inIndex) = 0;
+ virtual QT3DSI32 GetEditCamera() const = 0;
+ virtual void EditCameraZoomToFit() = 0;
+
+ // This must be safe to call from multiple places
+ virtual void Close() = 0;
+
+ // synchronously render the content
+ virtual void RenderNow() = 0;
+
+ virtual void MakeContextCurrent() = 0;
+ virtual void ReleaseContext() = 0;
+
+ // Uses the global studio app to get the doc and dispatch.
+ static std::shared_ptr<IStudioRenderer> CreateStudioRenderer();
+};
+};
+
+#endif
diff --git a/src/Authoring/Studio/Render/PathWidget.cpp b/src/Authoring/Studio/Render/PathWidget.cpp
new file mode 100644
index 00000000..49fea1ef
--- /dev/null
+++ b/src/Authoring/Studio/Render/PathWidget.cpp
@@ -0,0 +1,512 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "PathWidget.h"
+#include "UICRenderWidgets.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderVertexBuffer.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "render/Qt3DSRenderInputAssembler.h"
+#include "UICRenderPath.h"
+#include "UICRenderPathSubPath.h"
+#include "UICRenderPathManager.h"
+#include "UICRenderContext.h"
+#include "UICRenderShaderCodeGeneratorV2.h"
+
+using namespace uic::widgets;
+
+namespace {
+
+QT3DSVec3 toVec3(QT3DSVec2 inPoint)
+{
+ return QT3DSVec3(inPoint.x, inPoint.y, 0.0f);
+}
+QT3DSVec3 toVec3(QT3DSVec2 inPoint, float z)
+{
+ return QT3DSVec3(inPoint.x, inPoint.y, z);
+}
+
+struct SPointEntry
+{
+ QT3DSVec2 m_Position;
+ QT3DSVec3 m_Color;
+ QT3DSF32 m_ObjectId;
+ SPointEntry(QT3DSVec2 pos, QT3DSVec3 color, size_t objId)
+ : m_Position(pos)
+ , m_Color(color)
+ , m_ObjectId((QT3DSF32)objId)
+ {
+ }
+ SPointEntry() {}
+};
+
+struct SPathWidget : public IPathWidget
+{
+ typedef nvvector<eastl::pair<QT3DSU32, uic::studio::SPathPick::EAnchorProperty>>
+ TReverseAnchorBuffer;
+
+ NVAllocatorCallback &m_Allocator;
+ IUICRenderContext &m_UICContext;
+
+ NVScopedRefCounted<NVRenderVertexBuffer> m_PointVertexBuffer;
+ NVScopedRefCounted<NVRenderInputAssembler> m_PointAssembler;
+ NVScopedRefCounted<NVRenderShaderProgram> m_PointShader;
+ NVScopedRefCounted<NVRenderShaderProgram> m_PointPickShader;
+
+ NVScopedRefCounted<NVRenderVertexBuffer> m_LineVertexBuffer;
+ NVScopedRefCounted<NVRenderInputAssembler> m_LineAssembler;
+ NVScopedRefCounted<NVRenderShaderProgram> m_LineShader;
+
+ nvvector<eastl::pair<QT3DSVec2, QT3DSVec2>> m_LineBuffer;
+ nvvector<SPointEntry> m_PointBuffer;
+ TReverseAnchorBuffer m_AnchorIndexBuffer;
+ SWidgetRenderSetupResult m_RenderSetup;
+ QT3DSVec2 m_PointViewportDimensions;
+ QT3DSMat44 m_PointMVP;
+
+ QT3DSI32 m_RefCount;
+ SPathWidget(NVAllocatorCallback &inAlloc, IUICRenderContext &inRc)
+ : m_Allocator(inAlloc)
+ , m_UICContext(inRc)
+ , m_LineBuffer(inAlloc, "m_LineBuffer")
+ , m_PointBuffer(inAlloc, "m_PointBuffer")
+ , m_AnchorIndexBuffer(inAlloc, "m_AnchorIndexBuffer")
+ , m_LineShader(nullptr)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() override { ++m_RefCount; }
+ void release() override
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0) {
+ NVAllocatorCallback &alloc(m_Allocator);
+ NVDelete(alloc, this);
+ }
+ }
+
+ void SetNode(SNode &inNode) override { m_Node = &inNode; }
+
+ QT3DSVec3 ToGlobalSpace(QT3DSVec3 inPoint) { return m_Node->m_GlobalTransform.transform(inPoint); }
+
+ QT3DSVec3 ToCameraSpace(QT3DSVec3 inPoint)
+ {
+ QT3DSVec3 theGlobalPos = m_Node->m_GlobalTransform.transform(inPoint);
+ return m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse.transform(theGlobalPos);
+ }
+
+ QT3DSVec3 ToCameraSpace(QT3DSVec2 inPoint)
+ {
+ return ToCameraSpace(QT3DSVec3(inPoint.x, inPoint.y, 0.0f));
+ }
+
+ void GeneratePointVertexGeometrySections(IShaderProgramGenerator &inProgramGenerator)
+ {
+ IShaderStageGenerator &vertex(*inProgramGenerator.GetStage(ShaderGeneratorStages::Vertex));
+ IShaderStageGenerator &geometry(
+ *inProgramGenerator.GetStage(ShaderGeneratorStages::Geometry));
+ vertex.AddIncoming("attr_pos", "vec2");
+ vertex.AddIncoming("attr_color", "vec3");
+ vertex.AddIncoming("attr_objid", "float");
+ vertex.AddOutgoing("point_color", "vec3");
+ vertex.AddOutgoing("object_id", "float");
+ vertex.AddUniform("model_view_projection", "mat4");
+ vertex << "void main()" << Endl << "{" << Endl
+ << "\tgl_Position = model_view_projection * vec4( attr_pos.xy, 0.0, 1.0 );" << Endl
+ << "\tpoint_color = attr_color;" << Endl << "\tobject_id = uint(attr_objid);" << Endl
+ << "}" << Endl;
+
+ geometry.AddUniform("viewport_dimensions", "vec2");
+ geometry.AddUniform("pointsize", "float");
+ geometry.AddIncoming("point_color", "vec3");
+ geometry.AddIncoming("object_id", "float");
+ geometry.AddOutgoing("point_colorGE", "vec3");
+ geometry.AddOutgoing("object_idGE", "float");
+ geometry.AddOutgoing("uv_coords", "vec2");
+ geometry.Append("layout (points) in;");
+ geometry.Append("layout (triangle_strip, max_vertices = 4) out;");
+ geometry.Append(
+ "void main() {"
+ "// project points to screen space\n"
+ "\tvec2 p0 = vec2(gl_in[0].gl_Position.xy) / gl_in[0].gl_Position.w;\n"
+ "\tvec2 increments = (pointsize / viewport_dimensions) / 2.0;\n"
+ "\tgl_Position = vec4( p0.x - increments.x, p0.y + increments.y, 0.0, 1.0 );\n"
+ "\tpoint_colorGE = point_color[0];\n"
+ "\tuv_coords = vec2(0.0, 0.0);\n"
+ "\tobject_idGE = object_id[0];\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p0.x + increments.x, p0.y + increments.y, 0.0, 1.0 );\n"
+ "\tpoint_colorGE = point_color[0];\n"
+ "\tuv_coords = vec2(1.0, 0.0);\n"
+ "\tobject_idGE = object_id[0];\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p0.x - increments.x, p0.y - increments.y, 0.0, 1.0 );\n"
+ "\tpoint_colorGE = point_color[0];\n"
+ "\tuv_coords = vec2(0.0, 1.0);\n"
+ "\tobject_idGE = object_id[0];\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p0.x + increments.x, p0.y - increments.y, 0.0, 1.0 );\n"
+ "\tpoint_colorGE = point_color[0];\n"
+ "\tuv_coords = vec2(1.0, 1.0);\n"
+ "\tobject_idGE = object_id[0];\n"
+ "\tEmitVertex();\n"
+ "\tEndPrimitive();\n"
+ "}\n");
+ }
+
+ NVRenderShaderProgram *GetPointShader(IShaderProgramGenerator &inProgramGenerator)
+ {
+ if (m_PointShader)
+ return m_PointShader.mPtr;
+ inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags()
+ | ShaderGeneratorStages::Geometry);
+ GeneratePointVertexGeometrySections(inProgramGenerator);
+
+ IShaderStageGenerator &fragment(
+ *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment));
+ fragment.AddIncoming("point_colorGE", "vec3");
+ fragment.AddIncoming("uv_coords", "vec2");
+ fragment.Append("void main()\n"
+ "{\n"
+ "\tvec2 coords = uv_coords - vec2(.5, .5);\n"
+ "\tfloat coordLen = length( coords );\n"
+ "\tfloat margin = .4;\n"
+ "\tfloat leftover = min( 1.0, (coordLen - margin)/.1 );\n"
+ "\tfloat alpha = coordLen < margin ? 1.0 : mix( 1.0, 0.0, leftover );\n"
+ "\tfragOutput = vec4(point_colorGE, alpha);\n"
+ "}\n");
+
+ m_PointShader = inProgramGenerator.CompileGeneratedShader("path widget point shader");
+ return m_PointShader.mPtr;
+ }
+
+ NVRenderShaderProgram *GetPointPickShader(IShaderProgramGenerator &inProgramGenerator)
+ {
+ if (m_PointPickShader)
+ return m_PointPickShader.mPtr;
+ inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags()
+ | ShaderGeneratorStages::Geometry);
+ GeneratePointVertexGeometrySections(inProgramGenerator);
+
+ IShaderStageGenerator &fragment(
+ *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment));
+ fragment.AddIncoming("object_idGE", "float");
+ fragment.AddIncoming("uv_coords", "vec2");
+ fragment.Append("void main()\n"
+ "{\n"
+ "\tvec2 coords = uv_coords - vec2(.5, .5);\n"
+ "\tfloat coordLen = length( coords );\n"
+ "\tfloat margin = .4;\n"
+ "\tuint object_id = coordLen < margin ? uint(object_idGE + 1) : uint(0);\n"
+ "\tfragOutput.r = float(object_id % 256)/255.0;\n"
+ "\tfragOutput.g = float(object_id / 256)/255.0;\n"
+ //"\tfragOutput.g = float(object_id) / 10.0;\n"
+ "\tfragOutput.b = 0.0;\n"
+ "\tfragOutput.a = 1.0;\n"
+ "}\n");
+ m_PointPickShader =
+ inProgramGenerator.CompileGeneratedShader("path widget point pick shader");
+ return m_PointPickShader.mPtr;
+ }
+
+ NVRenderShaderProgram *GetLineShader(IShaderProgramGenerator &inProgramGenerator)
+ {
+ if (m_LineShader)
+ return m_LineShader.mPtr;
+
+ inProgramGenerator.BeginProgram(IShaderProgramGenerator::DefaultFlags()
+ | ShaderGeneratorStages::Geometry);
+
+ IShaderStageGenerator &vertex(*inProgramGenerator.GetStage(ShaderGeneratorStages::Vertex));
+ IShaderStageGenerator &geometry(
+ *inProgramGenerator.GetStage(ShaderGeneratorStages::Geometry));
+ IShaderStageGenerator &fragment(
+ *inProgramGenerator.GetStage(ShaderGeneratorStages::Fragment));
+
+ vertex.AddIncoming("attr_pos", "vec2");
+ vertex.AddUniform("model_view_projection", "mat4");
+ vertex << "void main()" << Endl << "{" << Endl
+ << "\tgl_Position = model_view_projection * vec4( attr_pos.xy, 0.0, 1.0 );" << Endl
+ << "}" << Endl;
+
+ geometry.AddUniform("viewport_dimensions", "vec2");
+ geometry.AddUniform("linewidth", "float");
+ geometry.AddOutgoing("uv_coords", "vec2");
+ geometry.Append(
+ "layout (lines) in;\n"
+ "layout (triangle_strip, max_vertices = 4) out;\n"
+ "void main()\n"
+ "{\n"
+ "\tvec2 p0 = vec2(gl_in[0].gl_Position.xy) / gl_in[0].gl_Position.w;\n"
+ "\tvec2 p1 = vec2(gl_in[1].gl_Position.xy) / gl_in[1].gl_Position.w;\n"
+ "\tvec2 slope = normalize( p1 - p0 );\n"
+ "\tvec2 tangent = vec2(slope.y, -slope.x);\n"
+ "\tvec2 increments = (linewidth / viewport_dimensions) / 2.0;\n"
+ "\tvec2 tangentVec = vec2( tangent.x * increments.x, tangent.y * increments.y );\n"
+ "\tgl_Position = vec4( p0 - tangentVec, 0.0, 1.0);\n"
+ "\tuv_coords = vec2(0.0, 0.0);\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p0 + tangentVec, 0.0, 1.0);\n"
+ "\tuv_coords = vec2(1.0, 0.0);\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p1 - tangentVec, 0.0, 1.0);\n"
+ "\tuv_coords = vec2(0.0, 1.0);\n"
+ "\tEmitVertex();\n"
+ "\tgl_Position = vec4( p1 + tangentVec, 0.0, 1.0);\n"
+ "\tuv_coords = vec2(1.0, 1.0);\n"
+ "\tEmitVertex();\n"
+ "\tEndPrimitive();\n"
+ "}\n");
+
+ fragment.AddUniform("line_color", "vec3");
+ fragment.AddIncoming("uv_coords", "vec2");
+ fragment.Append("void main()\n"
+ "{\n"
+ "\tvec2 coords = uv_coords - vec2(.5, .5);\n"
+ "\tfloat coordLen = abs(coords.x);\n"
+ "\tfloat margin = .1;\n"
+ "\tfloat leftover = min( 1.0, (coordLen - margin)/.3 );\n"
+ "\tleftover = leftover * leftover;\n"
+ "\tfloat alpha = coordLen < margin ? 1.0 : mix( 1.0, 0.0, leftover );\n"
+ "\tfragOutput = vec4(line_color, alpha);\n"
+ "}\n");
+
+ m_LineShader = inProgramGenerator.CompileGeneratedShader("path widget line shader");
+ return m_LineShader.mPtr;
+ }
+
+ void RenderPointBuffer(NVRenderShaderProgram &inProgram, const QT3DSMat44 &inMVP,
+ NVRenderContext &inRenderContext)
+ {
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(false);
+ inRenderContext.SetDepthWriteEnabled(false);
+ inRenderContext.SetStencilTestEnabled(false);
+ inRenderContext.SetBlendingEnabled(true);
+ inRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument(
+ qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha,
+ qt3ds::render::NVRenderSrcBlendFunc::One,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha));
+ inRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument(
+ NVRenderBlendEquation::Add, NVRenderBlendEquation::Add));
+ inRenderContext.SetActiveShader(&inProgram);
+ inProgram.SetPropertyValue("model_view_projection", inMVP);
+ inProgram.SetPropertyValue("pointsize", (QT3DSF32)15.0f);
+ inProgram.SetPropertyValue("viewport_dimensions", m_PointViewportDimensions);
+ inRenderContext.SetInputAssembler(m_PointAssembler);
+ inRenderContext.Draw(NVRenderDrawMode::Points, m_PointBuffer.size(), 0);
+ }
+
+ void PushPoint(const SPointEntry &inEntry, QT3DSU32 inAnchorIndex,
+ uic::studio::SPathPick::EAnchorProperty inProperty)
+ {
+ QT3DSU32 anchorIndex = (QT3DSU32)m_PointBuffer.size();
+ m_PointBuffer.push_back(inEntry);
+ m_AnchorIndexBuffer.push_back(eastl::make_pair(inAnchorIndex, inProperty));
+ }
+
+ void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override
+ {
+ if (!m_Node)
+ return;
+ SPath &thePath = static_cast<SPath &>(*m_Node);
+ IPathManager &theManager = m_UICContext.GetPathManager();
+
+ QT3DSVec3 anchorColor(0, 1, 0);
+ QT3DSVec3 controlColor(0, 0, 1);
+ m_LineBuffer.clear();
+ m_PointBuffer.clear();
+ // point index -> anchor index
+ m_AnchorIndexBuffer.clear();
+ QT3DSU32 anchorIndex = 0;
+
+ for (SPathSubPath *theSubPath = thePath.m_FirstSubPath; theSubPath;
+ theSubPath = theSubPath->m_NextSubPath) {
+ NVDataRef<uic::render::SPathAnchorPoint> thePathBuffer(
+ theManager.GetPathSubPathBuffer(*theSubPath));
+ if (thePathBuffer.size() == 0)
+ return;
+
+ QT3DSU32 numAnchors = thePathBuffer.size();
+ for (QT3DSU32 idx = 0, end = numAnchors; idx < end; ++idx) {
+ const uic::render::SPathAnchorPoint &theAnchorPoint(thePathBuffer[idx]);
+ if (idx > 0) {
+ QT3DSVec2 incoming(uic::render::IPathManagerCore::GetControlPointFromAngleDistance(
+ theAnchorPoint.m_Position, theAnchorPoint.m_IncomingAngle,
+ theAnchorPoint.m_IncomingDistance));
+ PushPoint(SPointEntry(incoming, controlColor, m_PointBuffer.size()),
+ anchorIndex, uic::studio::SPathPick::IncomingControl);
+ m_LineBuffer.push_back(eastl::make_pair(theAnchorPoint.m_Position, incoming));
+ }
+ PushPoint(SPointEntry(theAnchorPoint.m_Position, anchorColor, m_PointBuffer.size()),
+ anchorIndex, uic::studio::SPathPick::Anchor);
+ if (idx < (numAnchors - 1)) {
+ QT3DSVec2 outgoing(uic::render::IPathManagerCore::GetControlPointFromAngleDistance(
+ theAnchorPoint.m_Position, theAnchorPoint.m_OutgoingAngle,
+ theAnchorPoint.m_OutgoingDistance));
+ PushPoint(SPointEntry(outgoing, controlColor, m_PointBuffer.size()),
+ anchorIndex, uic::studio::SPathPick::OutgoingControl);
+ m_LineBuffer.push_back(eastl::make_pair(theAnchorPoint.m_Position, outgoing));
+ }
+ ++anchorIndex;
+ }
+ }
+
+ m_RenderSetup = SWidgetRenderSetupResult(inWidgetContext, *m_Node,
+ uic::render::RenderWidgetModes::Local);
+
+ m_PointMVP = m_RenderSetup.m_WidgetInfo.m_LayerProjection
+ * m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse * m_Node->m_GlobalTransform;
+
+ NVRenderRect theViewport = inRenderContext.GetViewport();
+ m_PointViewportDimensions = QT3DSVec2((QT3DSF32)theViewport.m_Width, (QT3DSF32)theViewport.m_Height);
+
+ if (!m_LineBuffer.empty()) {
+ QT3DSU32 vertItemSize = sizeof(QT3DSVec2);
+ QT3DSU32 vertBufSize = m_LineBuffer.size() * vertItemSize * 2;
+ if ((!m_LineVertexBuffer) || m_LineVertexBuffer->Size() < vertBufSize) {
+ m_LineVertexBuffer = inRenderContext.CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Dynamic, vertBufSize, vertItemSize,
+ qt3ds::foundation::toU8DataRef(m_LineBuffer.data(), (QT3DSU32)m_LineBuffer.size()));
+ m_LineAssembler = nullptr;
+ } else
+ m_LineVertexBuffer->UpdateBuffer(
+ qt3ds::foundation::toU8DataRef(m_LineBuffer.data(), (QT3DSU32)m_LineBuffer.size()));
+
+ if (m_LineAssembler == nullptr) {
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0),
+ };
+ NVRenderAttribLayout *theAttribLayout =
+ &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 1));
+ m_LineAssembler = inRenderContext.CreateInputAssembler(
+ theAttribLayout, toConstDataRef(&m_LineVertexBuffer.mPtr, 1), nullptr,
+ toConstDataRef(vertItemSize), toConstDataRef((QT3DSU32)0));
+ }
+ inRenderContext.SetInputAssembler(m_LineAssembler);
+ NVRenderShaderProgram *lineShader =
+ GetLineShader(inWidgetContext.GetProgramGenerator());
+ if (lineShader) {
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(false);
+ inRenderContext.SetDepthWriteEnabled(false);
+ inRenderContext.SetStencilTestEnabled(false);
+ inRenderContext.SetBlendingEnabled(true);
+ inRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument(
+ qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha,
+ qt3ds::render::NVRenderSrcBlendFunc::One,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha));
+ inRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument(
+ NVRenderBlendEquation::Add, NVRenderBlendEquation::Add));
+ inRenderContext.SetActiveShader(lineShader);
+ lineShader->SetPropertyValue("model_view_projection", m_PointMVP);
+ lineShader->SetPropertyValue("line_color", QT3DSVec3(1.0, 1.0, 0.0));
+ // Note the line needs to be wide enough to account for anti-aliasing.
+ lineShader->SetPropertyValue("linewidth", 3.0f);
+ lineShader->SetPropertyValue("viewport_dimensions", m_PointViewportDimensions);
+ inRenderContext.Draw(NVRenderDrawMode::Lines, m_LineBuffer.size() * 2, 0);
+ }
+ }
+ {
+ QT3DSU32 vertItemSize = (sizeof(SPointEntry));
+ QT3DSU32 vertBufSize = m_PointBuffer.size() * vertItemSize;
+ if ((!m_PointVertexBuffer) || m_PointVertexBuffer->Size() < vertBufSize) {
+ m_PointVertexBuffer = inRenderContext.CreateVertexBuffer(
+ qt3ds::render::NVRenderBufferUsageType::Dynamic, vertBufSize, vertItemSize,
+ qt3ds::foundation::toU8DataRef(m_PointBuffer.data(), (QT3DSU32)m_PointBuffer.size()));
+ m_PointAssembler = nullptr;
+ } else
+ m_PointVertexBuffer->UpdateBuffer(
+ qt3ds::foundation::toU8DataRef(m_PointBuffer.data(), (QT3DSU32)m_PointBuffer.size()));
+ if (m_PointAssembler == nullptr) {
+ qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 2, 0),
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_color", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3, 8),
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_objid", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 1, 20),
+ };
+ NVRenderAttribLayout *theAttribLayout =
+ &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 3));
+ m_PointAssembler = inRenderContext.CreateInputAssembler(
+ theAttribLayout, toConstDataRef(&m_PointVertexBuffer.mPtr, 1), nullptr,
+ toConstDataRef(vertItemSize), toConstDataRef((QT3DSU32)0));
+ }
+ NVRenderShaderProgram *thePointShader =
+ GetPointShader(inWidgetContext.GetProgramGenerator());
+ GetPointPickShader(inWidgetContext.GetProgramGenerator());
+
+ if (thePointShader) {
+ m_PointMVP = m_RenderSetup.m_WidgetInfo.m_LayerProjection
+ * m_RenderSetup.m_WidgetInfo.m_CameraGlobalInverse * m_Node->m_GlobalTransform;
+
+ RenderPointBuffer(*m_PointShader, m_PointMVP, inRenderContext);
+ }
+ }
+ }
+
+ void RenderPick(const QT3DSMat44 &inProjPreMult, NVRenderContext &inRenderContext,
+ uic::render::SWindowDimensions inWinDimensions) override
+ {
+ if (m_PointAssembler == nullptr || m_PointPickShader == nullptr)
+ return;
+ // The projection premultiplication step moves the viewport around till
+ // it is centered over the mouse and scales everything *post* rendering (to keep appropriate
+ // aspect).
+ QT3DSMat44 theMVP = inProjPreMult * m_PointMVP;
+ m_PointViewportDimensions =
+ QT3DSVec2((QT3DSF32)inWinDimensions.m_Width, (QT3DSF32)inWinDimensions.m_Height);
+
+ RenderPointBuffer(*m_PointPickShader, theMVP, inRenderContext);
+ }
+
+ uic::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override
+ {
+ inPickIndex -= 1;
+
+ if (inPickIndex < m_AnchorIndexBuffer.size())
+ return uic::studio::SPathPick(m_AnchorIndexBuffer[inPickIndex].first,
+ m_AnchorIndexBuffer[inPickIndex].second);
+ else {
+ QT3DS_ASSERT(false);
+ return uic::studio::SPathPick();
+ }
+ }
+};
+}
+
+IPathWidget &IPathWidget::CreatePathWidget(NVAllocatorCallback &inCallback, IUICRenderContext &inRc)
+{
+ return *QT3DS_NEW(inCallback, SPathWidget)(inCallback, inRc);
+}
diff --git a/src/Authoring/Studio/Render/PathWidget.h b/src/Authoring/Studio/Render/PathWidget.h
new file mode 100644
index 00000000..cde1a84a
--- /dev/null
+++ b/src/Authoring/Studio/Render/PathWidget.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_PATH_WIDGET_H
+#define UIC_STUDIO_PATH_WIDGET_H
+#pragma once
+#include "StudioWidget.h"
+#include "UICDMHandles.h"
+#include "StudioPickValues.h"
+
+namespace uic {
+namespace widgets {
+
+ class IPathWidget : public IStudioWidgetBase
+ {
+ public:
+ uic::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override = 0;
+ static IPathWidget &CreatePathWidget(NVAllocatorCallback &inAlloc,
+ IUICRenderContext &inRenderContext);
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Render/StudioPickValues.h b/src/Authoring/Studio/Render/StudioPickValues.h
new file mode 100644
index 00000000..cc811073
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioPickValues.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_PICK_VALUES_H
+#define UIC_STUDIO_PICK_VALUES_H
+#pragma once
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "foundation/Qt3DSUnionCast.h"
+#include "UICDMHandles.h"
+#include "StaticMaxSize.h"
+
+namespace uic {
+namespace studio {
+ using UICDM::CUICDMInstanceHandle;
+ using UICDM::CUICDMGuideHandle;
+
+ struct StudioPickValueTypes
+ {
+ enum Enum {
+ UnknownValueType = 0,
+ Instance,
+ Widget,
+ Guide,
+ Path,
+ };
+ };
+
+ struct SWidgetPick
+ {
+ qt3ds::QT3DSI32 m_WidgetId;
+ SWidgetPick(qt3ds::QT3DSI32 id = 0)
+ : m_WidgetId(id)
+ {
+ }
+ };
+
+ struct SPathPick
+ {
+ enum EAnchorProperty {
+ Anchor = 0,
+ IncomingControl,
+ OutgoingControl,
+ };
+
+ qt3ds::QT3DSU32 m_AnchorIndex;
+ EAnchorProperty m_Property;
+
+ SPathPick()
+ : m_AnchorIndex(0)
+ , m_Property(Anchor)
+ {
+ }
+
+ SPathPick(qt3ds::QT3DSU32 ai, EAnchorProperty p)
+ : m_AnchorIndex(ai)
+ , m_Property(p)
+ {
+ }
+ };
+
+ template <typename TDataType>
+ struct SStudioPickValueTypeMap
+ {
+ };
+
+ template <>
+ struct SStudioPickValueTypeMap<CUICDMInstanceHandle>
+ {
+ static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Instance; }
+ };
+
+ template <>
+ struct SStudioPickValueTypeMap<SWidgetPick>
+ {
+ static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Widget; }
+ };
+
+ template <>
+ struct SStudioPickValueTypeMap<CUICDMGuideHandle>
+ {
+ static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Guide; }
+ };
+
+ template <>
+ struct SStudioPickValueTypeMap<SPathPick>
+ {
+ static StudioPickValueTypes::Enum GetType() { return StudioPickValueTypes::Path; }
+ };
+
+ struct SStudioPickValueTraits
+ {
+ typedef StudioPickValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = Q3DStudio::StaticMaxSize<UICDM::CUICDMInstanceHandle,
+ SWidgetPick,
+ UICDM::CUICDMGuideHandle,
+ SPathPick>::value
+ };
+
+ static TIdType getNoDataId() { return StudioPickValueTypes::UnknownValueType; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SStudioPickValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case StudioPickValueTypes::Instance:
+ return inVisitor(*qt3ds::NVUnionCast<UICDM::CUICDMInstanceHandle *>(inData));
+ case StudioPickValueTypes::Widget:
+ return inVisitor(*qt3ds::NVUnionCast<SWidgetPick *>(inData));
+ case StudioPickValueTypes::Guide:
+ return inVisitor(*qt3ds::NVUnionCast<UICDM::CUICDMGuideHandle *>(inData));
+ case StudioPickValueTypes::Path:
+ return inVisitor(*qt3ds::NVUnionCast<SPathPick *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case StudioPickValueTypes::UnknownValueType:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case StudioPickValueTypes::Instance:
+ return inVisitor(*qt3ds::NVUnionCast<const UICDM::CUICDMInstanceHandle *>(inData));
+ case StudioPickValueTypes::Widget:
+ return inVisitor(*qt3ds::NVUnionCast<const SWidgetPick *>(inData));
+ case StudioPickValueTypes::Guide:
+ return inVisitor(*qt3ds::NVUnionCast<const UICDM::CUICDMGuideHandle *>(inData));
+ case StudioPickValueTypes::Path:
+ return inVisitor(*qt3ds::NVUnionCast<const SPathPick *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case StudioPickValueTypes::UnknownValueType:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SStudioPickValueTraits,
+ SStudioPickValueTraits::TBufferSize>,
+ SStudioPickValueTraits::TBufferSize>
+ TStudioPickValueType;
+
+ struct SStudioPickValue : public TStudioPickValueType
+ {
+ SStudioPickValue() {}
+ SStudioPickValue(CUICDMInstanceHandle inst)
+ : TStudioPickValueType(inst)
+ {
+ }
+ SStudioPickValue(SWidgetPick inst)
+ : TStudioPickValueType(inst)
+ {
+ }
+ SStudioPickValue(CUICDMGuideHandle inst)
+ : TStudioPickValueType(inst)
+ {
+ }
+ SStudioPickValue(SPathPick inst)
+ : TStudioPickValueType(inst)
+ {
+ }
+ SStudioPickValue(const SStudioPickValue &other)
+ : TStudioPickValueType(static_cast<const TStudioPickValueType &>(other))
+ {
+ }
+ SStudioPickValue &operator=(const SStudioPickValue &other)
+ {
+ TStudioPickValueType::operator=(static_cast<const TStudioPickValueType &>(other));
+ return *this;
+ }
+ int GetWidgetId() const
+ {
+ if (getType() == StudioPickValueTypes::Widget)
+ return getData<SWidgetPick>().m_WidgetId;
+ return 0;
+ }
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Render/StudioRenderer.cpp b/src/Authoring/Studio/Render/StudioRenderer.cpp
new file mode 100644
index 00000000..960cc1b6
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioRenderer.cpp
@@ -0,0 +1,883 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioRendererImpl.h"
+#include "StudioRendererTranslation.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+#include "StudioUtils.h"
+
+#include <QDebug>
+
+#ifdef _WIN32
+#pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+#endif
+using namespace uic::studio;
+
+namespace {
+
+const QT3DSU32 g_WheelFactor = 10; // the wheel zoom factor
+
+struct SEditCameraDefinition
+{
+ EditCameraTypes::Enum m_Type;
+ // Directional cameras have a direction they point
+ QT3DSVec3 m_Direction; // not normalized
+ QString m_Name;
+};
+
+SEditCameraDefinition g_EditCameraDefinitions[] = {
+ { EditCameraTypes::Perspective, QT3DSVec3(1, -1, -1), QObject::tr("Perspective View") },
+ { EditCameraTypes::Orthographic, QT3DSVec3(1, -1, -1), QObject::tr("Orthographic View") },
+ { EditCameraTypes::Directional, QT3DSVec3(0, -1, 0), QObject::tr("Top View") },
+ { EditCameraTypes::Directional, QT3DSVec3(0, 1, 0), QObject::tr("Bottom View") },
+ { EditCameraTypes::Directional, QT3DSVec3(1, 0, 0), QObject::tr("Left View") },
+ { EditCameraTypes::Directional, QT3DSVec3(-1, 0, 0), QObject::tr("Right View") },
+ { EditCameraTypes::Directional, QT3DSVec3(0, 0, -1), QObject::tr("Front View") },
+ { EditCameraTypes::Directional, QT3DSVec3(0, 0, 1), QObject::tr("Back View") },
+};
+QT3DSU32 g_NumEditCameras = sizeof(g_EditCameraDefinitions) / sizeof(*g_EditCameraDefinitions);
+
+struct SRendererImpl : public IStudioRenderer,
+ public IDataModelListener,
+ public IReloadListener,
+ public CPresentationChangeListener,
+ public CSceneDragListener,
+ public CToolbarChangeListener
+{
+ typedef eastl::vector<Option<SEditCameraPersistentInformation>> TEditCameraInfoList;
+ std::shared_ptr<CWGLRenderContext> m_RenderContext;
+ NVScopedRefCounted<IUICRenderContext> m_UICContext;
+ QRect m_Rect;
+ CDispatch &m_Dispatch;
+ CDoc &m_Doc;
+ std::shared_ptr<STranslation> m_Translation;
+ CPt m_MouseDownPoint;
+ CPt m_PreviousMousePoint;
+ bool m_HasPresentation;
+ bool m_Closed;
+ CUpdateableDocumentEditor m_UpdatableEditor;
+ MovementTypes::Enum m_LastDragToolMode;
+ bool m_MaybeDragStart;
+ TEditCameraInfoList m_EditCameraInformation;
+ QT3DSI32 m_EditCameraIndex;
+ SEditCameraPersistentInformation m_MouseDownCameraInformation;
+ SStudioPickValue m_PickResult;
+ bool m_RenderRequested;
+ int m_LastToolMode;
+ bool m_GuidesEnabled;
+ UICDM::TSignalConnectionPtr m_SelectionSignal;
+
+ SRendererImpl()
+ : m_Dispatch(*g_StudioApp.GetCore()->GetDispatch())
+ , m_Doc(*g_StudioApp.GetCore()->GetDoc())
+ , m_HasPresentation(false)
+ , m_Closed(false)
+ , m_UpdatableEditor(m_Doc)
+ , m_LastDragToolMode(MovementTypes::Unknown)
+ , m_MaybeDragStart(false)
+ , m_EditCameraIndex(-1)
+ , m_RenderRequested(false)
+ , m_LastToolMode(0)
+ , m_GuidesEnabled(true)
+ {
+ m_Dispatch.AddReloadListener(this);
+ m_Dispatch.AddDataModelListener(this);
+ m_Dispatch.AddPresentationChangeListener(this);
+ m_SelectionSignal =
+ m_Dispatch.ConnectSelectionChange(std::bind(&SRendererImpl::OnSelectionChange, this));
+ m_Dispatch.AddSceneDragListener(this);
+ m_Dispatch.AddToolbarChangeListener(this);
+ }
+ ~SRendererImpl()
+ {
+ Close();
+ m_Dispatch.RemoveDataModelListener(this);
+ m_Dispatch.RemovePresentationChangeListener(this);
+ m_Dispatch.RemoveSceneDragListener(this);
+ m_Dispatch.RemoveToolbarChangeListener(this);
+ }
+
+ // IDocSceneGraph
+ QT3DSVec3 GetIntendedPosition(UICDM::CUICDMInstanceHandle inHandle, CPt inPoint) override
+ {
+ if (m_Translation)
+ return m_Translation->GetIntendedPosition(inHandle, inPoint);
+
+ return QT3DSVec3(0, 0, 0);
+ }
+
+ ITextRenderer *GetTextRenderer() override
+ {
+ if (m_UICContext.mPtr)
+ return m_UICContext->GetTextRenderer();
+ return NULL;
+ }
+ // The buffer manager may not be available
+ IBufferManager *GetBufferManager() override
+ {
+ if (m_UICContext.mPtr)
+ return &m_UICContext->GetBufferManager();
+ return NULL;
+ }
+
+ IPathManager *GetPathManager() override
+ {
+ if (m_UICContext.mPtr)
+ return &m_UICContext->GetPathManager();
+ return NULL;
+ }
+
+ qt3ds::foundation::IStringTable *GetRenderStringTable() override
+ {
+ if (m_UICContext.mPtr)
+ return &m_UICContext->GetStringTable();
+ return NULL;
+ }
+
+ bool IsInitialized() override { return m_UICContext.mPtr != NULL; }
+
+ void Initialize(QWidget *inWindow) override
+ {
+ if (m_Closed)
+ return;
+ QT3DS_ASSERT(!m_RenderContext);
+ QT3DS_ASSERT(m_UICContext.mPtr == NULL);
+ try {
+ m_RenderContext = std::make_shared<CWGLRenderContext>(inWindow);
+
+ Q3DStudio::CString theResourcePath = Q3DStudio::CString::fromQString(resourcePath());
+ NVScopedRefCounted<uic::render::IUICRenderContextCore> theCore =
+ uic::render::IUICRenderContextCore::Create(
+ m_RenderContext->GetRenderContext().GetFoundation(),
+ m_RenderContext->GetRenderContext().GetStringTable());
+
+ // Create text renderer
+ uic::render::ITextRendererCore &theTextRenderer(
+ uic::render::ITextRendererCore::CreateQtTextRenderer(
+ m_RenderContext->GetRenderContext().GetFoundation(),
+ m_RenderContext->GetRenderContext().GetStringTable()));
+ theCore->SetTextRendererCore(theTextRenderer);
+
+ m_UICContext = theCore->CreateRenderContext(
+ m_RenderContext->GetRenderContext(),
+ m_RenderContext->GetRenderContext().GetStringTable().RegisterStr(
+ theResourcePath.c_str()));
+
+ // Allow the artist to interact with the top level objects alone.
+ m_UICContext->GetRenderer().PickRenderPlugins(false);
+
+ SetupTextRenderer();
+
+ m_UICContext->SetAuthoringMode(true);
+
+ InitializePointerTags(m_UICContext->GetStringTable());
+ SetViewRect(m_Rect);
+#ifdef KDAB_TEMPORARILY_REMOVE
+ // KDAB_TODO the below call asserts on windows
+ m_RenderContext->GetRenderContext().SetClearColor(QT3DSVec4(0, 0, 0, 1));
+#endif
+ if (m_HasPresentation)
+ CreateTranslator();
+
+ // Notify that renderer has been initialized
+ m_Dispatch.FireOnRendererInitialized();
+ } catch (...) {
+ m_UICContext = nullptr;
+ m_RenderContext = std::shared_ptr<CWGLRenderContext>();
+ throw;
+ }
+ }
+
+ void SetViewRect(const QRect &inRect) override
+ {
+ if (m_RenderContext)
+ m_RenderContext->resized();
+
+ m_Rect = inRect;
+ if (IsInitialized()) {
+ m_RenderContext->BeginRender();
+ NVRenderContext &theContext = m_RenderContext->GetRenderContext();
+ theContext.SetViewport(qt3ds::render::NVRenderRect(0, 0, inRect.width(),
+ inRect.height()));
+ SetTranslationViewport();
+ m_RenderContext->EndRender();
+ }
+ }
+ // Request that this object renders. May be ignored if a transaction
+ // is ongoing so we don't get multiple rendering per transaction.
+ void RequestRender() override
+ {
+ if (m_RenderContext)
+ m_RenderContext->requestRender();
+ }
+
+ void RenderRequestedRender()
+ {
+ if (m_RenderRequested) {
+ m_RenderContext->requestRender();
+ }
+ }
+
+ void RenderNow() override
+ {
+ Render();
+ }
+
+ void MakeContextCurrent() override
+ {
+ m_RenderContext->BeginRender();
+ }
+
+ void ReleaseContext()
+ {
+ m_RenderContext->EndRender();
+ }
+
+ void Render()
+ {
+ m_RenderRequested = false;
+ if (!m_Closed && IsInitialized()) {
+ m_RenderContext->BeginRender();
+ if (m_Translation)
+ m_Translation->PreRender();
+ NVRenderContext &theContext = m_RenderContext->GetRenderContext();
+ theContext.SetDepthWriteEnabled(true);
+ theContext.Clear(qt3ds::render::NVRenderClearFlags(
+ qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth));
+ if (m_Translation) {
+ m_Translation->Render(m_PickResult.GetWidgetId(), m_GuidesEnabled);
+ }
+ m_RenderContext->EndRender();
+ }
+ }
+ void GetEditCameraList(QStringList &outCameras) override
+ {
+ outCameras.clear();
+ for (QT3DSU32 idx = 0; idx < g_NumEditCameras; ++idx)
+ outCameras.push_back(g_EditCameraDefinitions[idx].m_Name);
+ }
+ void SetEnableEditLight(bool inEnableLight) override
+ {
+ CStudioPreferences::SetEditViewFillMode(inEnableLight);
+ if (m_Translation)
+ m_Translation->m_EditLightEnabled = inEnableLight;
+ RequestRender();
+ }
+
+ bool DoesEditCameraSupportRotation(QT3DSI32 inIndex) override
+ {
+ if (inIndex >= 0 && inIndex < (QT3DSI32)g_NumEditCameras)
+ return g_EditCameraDefinitions[inIndex].m_Type != EditCameraTypes::Directional;
+ return false;
+ }
+
+ bool AreGuidesEnabled() const override { return m_GuidesEnabled; }
+
+ void SetGuidesEnabled(bool val) override { m_GuidesEnabled = val; }
+
+ bool AreGuidesEditable() const override { return m_Doc.IsValid() ? m_Doc.GetDocumentReader().AreGuidesEditable() : false; }
+
+ void SetGuidesEditable(bool val) override { if (m_Doc.IsValid()) m_Doc.GetDocumentReader().SetGuidesEditable(val); }
+
+ // Setting the camera to -1 disables the edit cameras
+ // So setting the camera to 0- (numcameras - 1) will set change the active
+ // edit camera.
+ void SetEditCamera(QT3DSI32 inIndex) override
+ {
+ QT3DSI32 oldIndex = m_EditCameraIndex;
+ m_EditCameraIndex = NVMin(inIndex, (QT3DSI32)g_NumEditCameras);
+ // save the old edit camera information
+ if (oldIndex != m_EditCameraIndex && m_Translation && m_Translation->m_EditCameraEnabled) {
+ while (m_EditCameraInformation.size() <= (QT3DSU32)oldIndex)
+ m_EditCameraInformation.push_back(Empty());
+
+ m_EditCameraInformation[oldIndex] = m_Translation->m_EditCameraInfo;
+ }
+
+ ApplyEditCameraIndex();
+ RequestRender();
+ }
+
+ QT3DSI32 GetEditCamera() const override
+ {
+ if (m_EditCameraIndex >= 0 && m_EditCameraIndex < (QT3DSI32)g_NumEditCameras)
+ return m_EditCameraIndex;
+ return -1;
+ }
+
+ bool IsEditLightEnabled() const override
+ {
+ return GetEditCamera() >= 0 && CStudioPreferences::GetEditViewFillMode();
+ }
+
+ void EditCameraZoomToFit() override
+ {
+ UICDM::CUICDMInstanceHandle theInstance = m_Doc.GetSelectedInstance();
+ if (!m_Translation || m_Translation->m_EditCameraEnabled == false)
+ return;
+ // If we aren't pointed at a node then bounce up the asset graph till we are.
+ while (theInstance.Valid() && m_Translation->GetOrCreateTranslator(theInstance)
+ && GraphObjectTypes::IsNodeType(
+ m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type)
+ == false) {
+ theInstance = m_Translation->m_AssetGraph.GetParent(theInstance);
+ }
+ // If we still aren't pointed at a node then use the active layer.
+ if (theInstance.Valid() == false
+ || m_Translation->GetOrCreateTranslator(theInstance) == nullptr
+ || GraphObjectTypes::IsNodeType(
+ m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type)
+ == false)
+ theInstance = m_Doc.GetActiveLayer();
+
+ // If we *still* aren't pointed at a node then bail.
+ if (m_Translation->GetOrCreateTranslator(theInstance) == nullptr
+ || GraphObjectTypes::IsNodeType(
+ m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject().m_Type)
+ == false)
+ return;
+
+ SNode &theNode = static_cast<SNode &>(
+ m_Translation->GetOrCreateTranslator(theInstance)->GetGraphObject());
+ qt3ds::NVBounds3 theBounds;
+ theBounds.setEmpty();
+ if (theNode.m_Type == GraphObjectTypes::Layer) {
+ SNode *theEditLayer = m_Translation->GetEditCameraLayer();
+ if (theEditLayer) {
+ for (SNode *theChild = theEditLayer->m_FirstChild; theChild;
+ theChild = theChild->m_NextSibling) {
+ qt3ds::NVBounds3 childBounds = theChild->GetBounds(
+ m_UICContext->GetBufferManager(), m_UICContext->GetPathManager());
+ if (childBounds.isEmpty() == false) {
+ childBounds.transform(theChild->m_GlobalTransform);
+ theBounds.include(childBounds);
+ }
+ }
+ }
+ } else
+ theBounds =
+ theNode.GetBounds(m_UICContext->GetBufferManager(), m_UICContext->GetPathManager());
+ QT3DSVec3 theCenter = theNode.m_GlobalTransform.transform(theBounds.getCenter());
+ // Center the edit camera so that it points directly at the bounds center point
+ m_Translation->m_EditCameraInfo.m_Position = theCenter;
+ // Now we need to adjust the camera's zoom such that the view frustum contains the bounding
+ // box.
+ // But to do that I need to figure out what the view frustum is at -600 units from the near
+ // clip plane
+
+ QT3DSVec3 theExtents = theBounds.getExtents();
+ // get the largest extent and then some addition so things fit nicely in the viewport.
+ QT3DSF32 theMaxPossibleRadius = theExtents.magnitude();
+ // easiest case, the viewport dimensions map directly to the
+ m_Translation->m_EditCameraInfo.m_ViewRadius = theMaxPossibleRadius;
+ RequestRender();
+ }
+
+ // This must be safe to call from multiple places
+ void Close() override
+ {
+ m_Closed = true;
+ m_Translation = std::shared_ptr<STranslation>();
+ m_UICContext = nullptr;
+ m_RenderContext = std::shared_ptr<CWGLRenderContext>();
+ }
+
+ // Data model listener
+
+ // Fired before a large group of notifications come out so views can
+ // only refresh their view once.
+ void OnBeginDataModelNotifications() override {}
+ // Fired after a large gruop of notifications (onInstancePropertyChanged, etc) come out
+ // so views can be careful about refreshing their data and there view
+ void OnEndDataModelNotifications() override { Render(); }
+
+ // Fired during 3d drag or mouse move events (or keyframe drag) or likewise
+ // events so that views that need to update based on the new data can.
+ void OnImmediateRefreshInstanceSingle(UICDM::CUICDMInstanceHandle inInstance) override
+ {
+ if (m_Translation) {
+ m_Translation->MarkDirty(inInstance);
+ // Pass to translation system
+ Render();
+ }
+ }
+ // Same thing, but fired when more than one instance is being refreshed.
+ void OnImmediateRefreshInstanceMultiple(UICDM::CUICDMInstanceHandle *inInstance,
+ long inInstanceCount) override
+ {
+ // Pass to translation system
+ if (m_Translation) {
+ m_Translation->MarkDirty(inInstance, inInstanceCount);
+ // Pass to translation system
+ Render();
+ }
+ Render();
+ }
+
+ void OnReloadEffectInstance(UICDM::CUICDMInstanceHandle inInstance) override
+ {
+ if (m_Translation)
+ m_Translation->ReleaseEffect(inInstance);
+ }
+
+ void ApplyEditCameraIndex()
+ {
+ if (!m_Translation)
+ return;
+ if (m_EditCameraIndex < 0 || m_EditCameraIndex >= (QT3DSI32)g_NumEditCameras)
+ m_Translation->m_EditCameraEnabled = false;
+ else {
+ const SEditCameraDefinition &theDefinition(g_EditCameraDefinitions[m_EditCameraIndex]);
+
+ while ((size_t)m_EditCameraIndex >= m_EditCameraInformation.size())
+ m_EditCameraInformation.push_back(Empty());
+
+ Option<SEditCameraPersistentInformation> &theCameraInfo =
+ m_EditCameraInformation[m_EditCameraIndex];
+
+ if (!theCameraInfo.hasValue()) {
+ theCameraInfo = SEditCameraPersistentInformation();
+ // TODO - consider resizing clip planes to scene so we use the depth buffer more
+ // accurately
+ // or consider requesting a larger depth buffer from the windowing system.
+ // Setup the camera
+ theCameraInfo->m_Direction = theDefinition.m_Direction;
+ theCameraInfo->m_CameraType = theDefinition.m_Type;
+ }
+
+ m_Translation->m_EditCameraEnabled = true;
+ m_Translation->m_EditCameraInfo = theCameraInfo;
+ }
+ }
+
+ void SetTranslationViewport()
+ {
+ if (m_Translation) {
+ m_Translation->SetViewport(QT3DSF32(m_Rect.right() - m_Rect.left()),
+ QT3DSF32(m_Rect.bottom() - m_Rect.top()));
+ }
+ }
+
+ void CreateTranslator()
+ {
+ if (!m_Translation) {
+ if (m_UICContext.mPtr) {
+ m_Translation = std::make_shared<STranslation>(std::ref(*this),
+ std::ref(*m_UICContext.mPtr));
+ m_Translation->m_EditLightEnabled = CStudioPreferences::GetEditViewFillMode();
+ ApplyEditCameraIndex();
+ SetTranslationViewport();
+ }
+ }
+ }
+
+ void SetupTextRenderer()
+ {
+ if (m_UICContext.mPtr && m_UICContext->GetTextRenderer()) {
+ m_UICContext->GetTextRenderer()->ClearProjectFontDirectories();
+ Q3DStudio::CString theDocDir = m_Doc.GetDocumentDirectory();
+ if (theDocDir.Length()) {
+ // Add the installed font folders from the res dir.
+ Q3DStudio::CString thePath(Q3DStudio::CString::fromQString(
+ resourcePath() + QStringLiteral("/Font")));
+ m_UICContext->GetTextRenderer()->AddSystemFontDirectory(
+ m_UICContext->GetStringTable().RegisterStr(thePath.c_str()));
+ m_UICContext->GetTextRenderer()->AddProjectFontDirectory(
+ m_UICContext->GetStringTable().RegisterStr(theDocDir.c_str()));
+ }
+ }
+ }
+
+ //==========================================================================
+ /**
+ * New presentation is being created.
+ */
+ void OnNewPresentation() override
+ {
+ OnClosingPresentation();
+ m_HasPresentation = true;
+ // Reset edit camera information.
+ m_EditCameraInformation.clear();
+ // Rebuild translation
+ CreateTranslator();
+ SetupTextRenderer();
+ RequestRender();
+ }
+
+ //==========================================================================
+ /**
+ * The current presentation is being closed.
+ */
+ void OnClosingPresentation() override
+ {
+ // Destroy translation
+ m_Translation = std::shared_ptr<STranslation>();
+ m_HasPresentation = false;
+ }
+
+ void OnSelectionChange() { RequestRender(); }
+
+ UICDM::CUICDMInstanceHandle GetAnchorPointFromPick(SPathPick &inPick)
+ {
+ return m_Translation->GetAnchorPoint(inPick);
+ }
+
+ //==========================================================================
+ // CSceneDragListener
+ //==========================================================================
+ void OnSceneMouseDown(SceneDragSenderType::Enum inSenderType, QPoint inPoint, int) override
+ {
+ if (m_Translation == NULL)
+ return;
+
+ inPoint.setX(inPoint.x() * devicePixelRatio());
+ inPoint.setY(inPoint.y() * devicePixelRatio());
+
+ m_PickResult = SStudioPickValue();
+ TranslationSelectMode::Enum theSelectMode = TranslationSelectMode::Group;
+ switch (g_StudioApp.GetSelectMode()) {
+ case STUDIO_SELECTMODE_ENTITY:
+ theSelectMode = TranslationSelectMode::Single;
+ break;
+ case STUDIO_SELECTMODE_GROUP:
+ theSelectMode = TranslationSelectMode::Group;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ if (inSenderType == SceneDragSenderType::SceneWindow
+ && m_Translation->GetPickArea(inPoint) == PickTargetAreas::Presentation) {
+ m_RenderContext->BeginRender();
+ m_PickResult = m_Translation->Pick(inPoint, theSelectMode);
+ m_RenderContext->EndRender();
+ // If we definitely did not pick a widget.
+ if (m_PickResult.getType() == StudioPickValueTypes::Instance) {
+ UICDM::CUICDMInstanceHandle theHandle(m_PickResult.getData<CUICDMInstanceHandle>());
+
+ if (theHandle != m_Doc.GetSelectedInstance())
+ m_Doc.SelectUICDMObject(theHandle);
+ } else if (m_PickResult.getType() == StudioPickValueTypes::Guide)
+ m_Doc.NotifySelectionChanged(m_PickResult.getData<UICDM::CUICDMGuideHandle>());
+ else if (m_PickResult.getType() == StudioPickValueTypes::Path) {
+ SPathPick thePick = m_PickResult.getData<SPathPick>();
+ UICDM::CUICDMInstanceHandle theAnchorHandle =
+ m_Translation->GetAnchorPoint(thePick);
+ if (theAnchorHandle.Valid() && theAnchorHandle != m_Doc.GetSelectedInstance()) {
+ m_Doc.SelectUICDMObject(theAnchorHandle);
+ }
+ }
+ RequestRender();
+ } else {
+ qt3ds::foundation::Option<UICDM::SGuideInfo> pickResult =
+ m_Translation->PickRulers(inPoint);
+ if (pickResult.hasValue() == false)
+ m_Translation->PrepareForDrag();
+ else {
+ Q3DStudio::IDocumentEditor &docEditor(
+ m_UpdatableEditor.EnsureEditor(L"Create Guide", __FILE__, __LINE__));
+ CUICDMGuideHandle newGuide = docEditor.CreateGuide(*pickResult);
+ m_PickResult = SStudioPickValue(newGuide);
+ m_Doc.NotifySelectionChanged(newGuide);
+ }
+ }
+ m_LastDragToolMode = MovementTypes::Unknown;
+ m_MaybeDragStart = true;
+ m_MouseDownPoint = inPoint;
+ m_PreviousMousePoint = inPoint;
+ m_MouseDownCameraInformation = m_Translation->m_EditCameraInfo;
+ m_LastToolMode = g_StudioApp.GetToolMode();
+ }
+
+ void OnSceneMouseDrag(SceneDragSenderType::Enum, QPoint inPoint, int inToolMode,
+ int inFlags) override
+ {
+ if (m_Translation == NULL)
+ return;
+
+ inPoint.setX(inPoint.x() * devicePixelRatio());
+ inPoint.setY(inPoint.y() * devicePixelRatio());
+
+ if (m_MaybeDragStart) {
+ // Dragging in the first 5 pixels will be ignored to avoid unconsciously accidental
+ // moves
+ CPt theDragDistance = inPoint - m_MouseDownPoint;
+ if (m_PickResult.getType() == StudioPickValueTypes::Widget
+ || inToolMode != STUDIO_TOOLMODE_SCALE) {
+ if (theDragDistance.x * theDragDistance.x + theDragDistance.y * theDragDistance.y
+ <= 25)
+ return;
+ } else {
+ if (abs(theDragDistance.y) <= 5)
+ return;
+ }
+ }
+
+ m_MaybeDragStart = false;
+
+ // If the tool mode changes then we throw out the last widget pick if there was one.
+ if (m_LastToolMode != inToolMode)
+ m_PickResult = SStudioPickValue();
+ m_LastToolMode = inToolMode;
+
+ // General dragging
+ if (m_PickResult.getType() == StudioPickValueTypes::Instance
+ || m_PickResult.getType()
+ == StudioPickValueTypes::UnknownValueType) // matte drag and widget drag
+ {
+ // Not sure what right-click drag does in the scene.
+ bool isEditCamera = m_Translation->m_EditCameraEnabled;
+ int theCameraToolMode = isEditCamera ? (inToolMode & (STUDIO_CAMERATOOL_MASK)) : 0;
+ bool rightClick = (inFlags & CHotKeys::MOUSE_RBUTTON) != 0;
+
+ if (theCameraToolMode == 0) {
+ if (m_Doc.GetDocumentReader().IsInstance(m_Doc.GetSelectedInstance())) {
+ bool rightClick = (inFlags & CHotKeys::MOUSE_RBUTTON) != 0;
+ MovementTypes::Enum theMovement(MovementTypes::Unknown);
+
+ switch (inToolMode) {
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ case STUDIO_TOOLMODE_MOVE:
+ if (rightClick)
+ theMovement = MovementTypes::TranslateAlongCameraDirection;
+ else
+ theMovement = MovementTypes::Translate;
+ break;
+ case STUDIO_TOOLMODE_SCALE:
+ if (rightClick)
+ theMovement = MovementTypes::ScaleZ;
+ else
+ theMovement = MovementTypes::Scale;
+ break;
+ case STUDIO_TOOLMODE_ROTATE:
+ if (rightClick)
+ theMovement = MovementTypes::RotationAboutCameraDirection;
+ else
+ theMovement = MovementTypes::Rotation;
+ break;
+ }
+
+ if (theMovement != MovementTypes::Unknown) {
+ bool theLockToAxis = (inFlags & CHotKeys::MODIFIER_SHIFT) != 0;
+
+ if (m_LastDragToolMode != MovementTypes::Unknown
+ && theMovement != m_LastDragToolMode) {
+ m_UpdatableEditor.RollbackEditor();
+ m_MouseDownPoint = inPoint;
+ }
+
+ m_LastDragToolMode = theMovement;
+
+ switch (theMovement) {
+ case MovementTypes::TranslateAlongCameraDirection:
+ m_Translation->TranslateSelectedInstanceAlongCameraDirection(
+ m_MouseDownPoint, inPoint, m_UpdatableEditor);
+ break;
+ case MovementTypes::Translate:
+ m_Translation->TranslateSelectedInstance(
+ m_MouseDownPoint, inPoint, m_UpdatableEditor, theLockToAxis);
+ break;
+ case MovementTypes::ScaleZ:
+ m_Translation->ScaleSelectedInstanceZ(m_MouseDownPoint, inPoint,
+ m_UpdatableEditor);
+ break;
+ case MovementTypes::Scale:
+ m_Translation->ScaleSelectedInstance(m_MouseDownPoint, inPoint,
+ m_UpdatableEditor);
+ break;
+ case MovementTypes::Rotation:
+ m_Translation->RotateSelectedInstance(m_MouseDownPoint,
+ m_PreviousMousePoint, inPoint,
+ m_UpdatableEditor, theLockToAxis);
+ break;
+ case MovementTypes::RotationAboutCameraDirection:
+ m_Translation->RotateSelectedInstanceAboutCameraDirectionVector(
+ m_PreviousMousePoint, inPoint, m_UpdatableEditor);
+ break;
+ }
+ }
+ }
+ } else {
+ QT3DSF32 theXDistance = static_cast<QT3DSF32>(inPoint.x() - m_MouseDownPoint.x);
+ QT3DSF32 theYDistance = static_cast<QT3DSF32>(inPoint.y() - m_MouseDownPoint.y);
+ QT3DSF32 theSubsetXDistance = static_cast<QT3DSF32>(inPoint.x() - m_PreviousMousePoint.x);
+ QT3DSF32 theSubsetYDistance = static_cast<QT3DSF32>(inPoint.y() - m_PreviousMousePoint.y);
+
+ // Edit cameras are not implemented.
+ switch (theCameraToolMode) {
+ case STUDIO_TOOLMODE_CAMERA_PAN: {
+ QT3DSVec3 theXAxis =
+ m_Translation->m_EditCamera.m_GlobalTransform.column0.getXYZ();
+ QT3DSVec3 theYAxis =
+ m_Translation->m_EditCamera.m_GlobalTransform.column1.getXYZ();
+ QT3DSVec3 theXChange = -1.0f * theXAxis * theXDistance;
+ QT3DSVec3 theYChange = theYAxis * theYDistance;
+ QT3DSVec3 theDiff = theXChange + theYChange;
+ m_Translation->m_EditCameraInfo.m_Position =
+ m_MouseDownCameraInformation.m_Position + theDiff;
+ RequestRender();
+ } break;
+ case STUDIO_TOOLMODE_CAMERA_ZOOM: {
+ QT3DSF32 theMultiplier = 1.0f + theSubsetYDistance / 40.0f;
+ m_Translation->m_EditCameraInfo.m_ViewRadius =
+ NVMax(.0001f, m_Translation->m_EditCameraInfo.m_ViewRadius * theMultiplier);
+ RequestRender();
+ } break;
+ case STUDIO_TOOLMODE_CAMERA_ROTATE: {
+ if (m_Translation->m_EditCameraInfo.SupportsRotation()) {
+ if (rightClick == false) {
+ QT3DSVec3 theXAxis = QT3DSVec3(1, 0, 0);
+ QT3DSVec3 theYAxis = QT3DSVec3(0, 1, 0);
+ // Rotate about the center; we will just rotation the direction vector.
+ QT3DSQuat theXRotation(-1.0f * theSubsetXDistance * g_RotationScaleFactor
+ / 20.0f,
+ theYAxis);
+ QT3DSQuat theYRotation(-1.0f * theSubsetYDistance * g_RotationScaleFactor
+ / 20.0f,
+ theXAxis);
+ m_Translation->m_EditCameraInfo.m_Rotation =
+ m_MouseDownCameraInformation.m_Rotation
+ * (theXRotation * theYRotation);
+ } else {
+ QT3DSVec3 theZAxis = QT3DSVec3(0, 0, 1);
+ QT3DSQuat theZRotation(
+ -1.0f * theYDistance * g_RotationScaleFactor / 20.0f, theZAxis);
+ m_Translation->m_EditCameraInfo.m_Rotation =
+ m_MouseDownCameraInformation.m_Rotation * theZRotation;
+ }
+ // Rotations need to be incremental and relative else things don't rotate
+ // intuitively.
+ m_MouseDownCameraInformation.m_Rotation =
+ m_Translation->m_EditCameraInfo.m_Rotation;
+ RequestRender();
+ }
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ } // if ( m_PickResult.m_WidgetId.hasValue() == false )
+
+ // We need to do widget-specific dragging.
+ else if (m_PickResult.getType() == StudioPickValueTypes::Widget) {
+ m_Translation->PerformWidgetDrag(m_PickResult.GetWidgetId(), m_MouseDownPoint,
+ m_PreviousMousePoint, inPoint, m_UpdatableEditor);
+ } else if (m_PickResult.getType() == StudioPickValueTypes::Guide) {
+ m_Translation->PerformGuideDrag(m_PickResult.getData<CUICDMGuideHandle>(), inPoint,
+ m_UpdatableEditor);
+ } else if (m_PickResult.getType() == StudioPickValueTypes::Path) {
+ SPathPick thePick = m_PickResult.getData<SPathPick>();
+ m_Translation->PerformPathDrag(thePick, m_MouseDownPoint, m_PreviousMousePoint, inPoint,
+ m_UpdatableEditor);
+ }
+ m_PreviousMousePoint = inPoint;
+ }
+
+ void OnSceneMouseUp(SceneDragSenderType::Enum) override
+ {
+ m_MaybeDragStart = false;
+ CUICDMGuideHandle theSelectedGuide;
+ if (m_PickResult.getType() == StudioPickValueTypes::Guide) {
+ theSelectedGuide = m_PickResult.getData<CUICDMGuideHandle>();
+ m_Translation->CheckGuideInPresentationRect(theSelectedGuide, m_UpdatableEditor);
+ }
+ m_UpdatableEditor.CommitEditor();
+ m_PickResult = SStudioPickValue();
+ if (m_Translation)
+ m_Translation->EndDrag();
+ if (theSelectedGuide.GetHandleValue()) {
+ // Get rid of selection if things aren't editable.
+ if (m_Doc.GetDocumentReader().AreGuidesEditable())
+ m_Doc.NotifySelectionChanged(theSelectedGuide);
+ else
+ m_Doc.NotifySelectionChanged();
+ }
+ RequestRender();
+ }
+
+ void OnSceneMouseDblClick(SceneDragSenderType::Enum inSenderType, QPoint inPoint) override
+ {
+ if (inSenderType == SceneDragSenderType::SceneWindow && m_Translation) {
+ inPoint.setX(inPoint.x() * devicePixelRatio());
+ inPoint.setY(inPoint.y() * devicePixelRatio());
+ m_RenderContext->BeginRender();
+ SStudioPickValue theResult(
+ m_Translation->Pick(inPoint, TranslationSelectMode::NestedComponentSingle));
+ m_RenderContext->EndRender();
+
+ if (theResult.getType() == StudioPickValueTypes::Instance)
+ m_Doc.SelectAndNavigateToUICDMObject(theResult.getData<CUICDMInstanceHandle>());
+ else if (theResult.getType() == StudioPickValueTypes::Path) {
+ SPathPick thePickValue = theResult.getData<SPathPick>();
+ UICDM::CUICDMInstanceHandle theAnchorHandle =
+ m_Translation->GetAnchorPoint(thePickValue);
+ if (theAnchorHandle.Valid() && theAnchorHandle != m_Doc.GetSelectedInstance()) {
+ m_Doc.SelectUICDMObject(theAnchorHandle);
+ }
+ }
+ }
+ }
+
+ void OnSceneMouseWheel(SceneDragSenderType::Enum inSenderType, short inDelta,
+ int inToolMode) override
+ {
+ ASSERT(inSenderType == SceneDragSenderType::Matte);
+ if (inToolMode == STUDIO_TOOLMODE_CAMERA_ZOOM && m_Translation) {
+ QT3DSF32 theMultiplier = 1.0f - inDelta / static_cast<QT3DSF32>(120 * g_WheelFactor);
+ m_Translation->m_EditCameraInfo.m_ViewRadius =
+ NVMax(.0001f, m_Translation->m_EditCameraInfo.m_ViewRadius * theMultiplier);
+ RequestRender();
+ }
+ }
+
+ void OnNudge(ENudgeDirection inNudgeDirection, int inToolMode, int inFlags) override
+ {
+ if (m_Translation)
+ m_Translation->OnNudge(inNudgeDirection, inToolMode, inFlags, m_UpdatableEditor);
+ }
+
+ void OnNudgeDone() override
+ {
+ if (m_Translation)
+ m_Translation->OnNudgeFinished();
+ m_UpdatableEditor.CommitEditor();
+ }
+
+ void OnToolbarChange() override { RequestRender(); }
+};
+}
+
+std::shared_ptr<IStudioRenderer> IStudioRenderer::CreateStudioRenderer()
+{
+ return std::make_shared<SRendererImpl>();
+}
diff --git a/src/Authoring/Studio/Render/StudioRendererImpl.h b/src/Authoring/Studio/Render/StudioRendererImpl.h
new file mode 100644
index 00000000..86ce4f0b
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioRendererImpl.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_RENDERER_IMPL_H
+#define UIC_STUDIO_RENDERER_IMPL_H
+#pragma once
+#include "IStudioRenderer.h"
+#include "WGLRenderContext.h"
+#include "UICDMDataTypes.h"
+#include "UICDMSignals.h"
+#include "UICRenderContext.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentReader.h"
+#include "UICFileTools.h"
+#include "render/Qt3DSRenderContext.h"
+#include "foundation/Qt3DSVec4.h"
+#include "DispatchListeners.h"
+#include "Dispatch.h"
+#include "Core.h"
+#include "foundation/Qt3DSInvasiveSet.h"
+#include "UICRenderer.h"
+#include "UICRenderScene.h"
+#include "UICRenderNode.h"
+#include "UICRenderLayer.h"
+#include "UICRenderModel.h"
+#include "UICRenderDefaultMaterial.h"
+#include "UICRenderLight.h"
+#include "UICRenderCamera.h"
+#include "UICRenderImage.h"
+#include "UICRenderPresentation.h"
+#include "StudioProjectSettings.h"
+#include "UICRenderUIPSharedTranslation.h"
+#include "UICRenderBufferManager.h"
+#include "StudioFullSystem.h"
+#include "UICDMSignals.h"
+#include "CoreConst.h"
+#include "IDocumentEditor.h"
+#include "foundation/Qt3DSPlane.h"
+#include "foundation/Qt3DSQuat.h"
+#include "UICTextRenderer.h"
+#include "foundation/Qt3DSOption.h"
+#include "foundation/Qt3DSMathUtils.h"
+#include "UICRenderEffect.h"
+#include "UICRenderPath.h"
+#include "UICRenderPathSubPath.h"
+
+namespace uic {
+namespace studio {
+ using Q3DStudio::IDocumentReader;
+ using Q3DStudio::CUpdateableDocumentEditor;
+ using Q3DStudio::TIdentifier;
+ using Q3DStudio::IStudioRenderer;
+ using qt3ds::foundation::NVScopedRefCounted;
+ using qt3ds::QT3DSVec3;
+ using qt3ds::QT3DSQuat;
+ using qt3ds::QT3DSF32;
+ using qt3ds::QT3DSMat44;
+ using qt3ds::QT3DSMat33;
+ using qt3ds::QT3DSI32;
+ using qt3ds::QT3DSVec2;
+ using qt3ds::QT3DSVec4;
+ using qt3ds::NVMax;
+ using qt3ds::NVMin;
+ using qt3ds::QT3DSU32;
+ using qt3ds::foundation::Empty;
+ using qt3ds::foundation::InvasiveSet;
+ using qt3ds::foundation::nvhash_map;
+ using qt3ds::foundation::nvvector;
+ using qt3ds::foundation::rotationArc;
+ using qt3ds::foundation::Option;
+ using qt3ds::foundation::Empty;
+ using uic::render::IUICRenderContext;
+ using uic::render::SScene;
+ using uic::render::SLayer;
+ using uic::render::SNode;
+ using uic::render::SGraphObject;
+ using uic::render::SLight;
+ using uic::render::SCamera;
+ using uic::render::SDefaultMaterial;
+ using uic::render::SImage;
+ using uic::render::SModel;
+ using uic::render::SText;
+ using uic::render::GraphObjectTypes;
+ using uic::render::SRay;
+ using uic::render::ITextRenderer;
+ using uic::render::SEffect;
+ using uic::render::IEffectSystem;
+ using uic::render::SDynamicObject;
+ using uic::render::SCustomMaterial;
+ using uic::render::IDynamicObjectSystem;
+ using uic::render::ICustomMaterialSystem;
+ using uic::render::IBufferManager;
+ using uic::render::IPathManager;
+ using uic::render::SPath;
+ using uic::render::SPathSubPath;
+ using uic::render::SReferencedMaterial;
+ using qt3ds::render::CRegisteredString;
+ using qt3ds::render::IStringTable;
+ using UICDM::SFloat3;
+ using UICDM::SLong4;
+ using UICDM::SComposerObjectDefinitions;
+ using UICDM::CUICDMInstanceHandle;
+ using UICDM::CUICDMPropertyHandle;
+}
+}
+#endif
diff --git a/src/Authoring/Studio/Render/StudioRendererTranslation.cpp b/src/Authoring/Studio/Render/StudioRendererTranslation.cpp
new file mode 100644
index 00000000..7efca4a4
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioRendererTranslation.cpp
@@ -0,0 +1,3827 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioRendererImpl.h"
+#include "StudioRendererTranslation.h"
+#include "UICRenderEffectSystem.h"
+#include "foundation/StrConvertUTF.h"
+#include "UICFileTools.h"
+#include "UICRenderUIPLoader.h"
+#include "UICRenderWidgets.h"
+#include "foundation/Qt3DSBounds3.h"
+#include "UICRenderResourceManager.h"
+#include "render/Qt3DSRenderFrameBuffer.h"
+#include "UICRenderCamera.h"
+#include "foundation/Qt3DSPlane.h"
+#include "UICRenderRotationHelper.h"
+#include "UICRenderPluginGraphObject.h"
+#include "UICRenderPlugin.h"
+#include "StudioCoreSystem.h"
+#include "UICDMDataCore.h"
+#include "UICRenderPluginPropertyValue.h"
+#include "UICRenderEffectSystem.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "UICRenderMaterialHelpers.h"
+#include "UICRenderDynamicObjectSystem.h"
+#include "UICRenderCustomMaterialSystem.h"
+#include "UICRenderReferencedMaterial.h"
+#include "UICRenderPixelGraphicsTypes.h"
+#include "UICRenderPixelGraphicsRenderer.h"
+#include "UICRenderPathManager.h"
+#include "PathWidget.h"
+#include "UICRenderLightmaps.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+#include <boost/bind.hpp>
+
+#pragma warning(disable : 4100) // unreferenced formal parameter
+
+using namespace uic::studio;
+QT3DSU32 uic::studio::g_GraphObjectTranslatorTag;
+using uic::render::SPGGraphObject;
+using uic::render::SPGRect;
+using uic::render::SPGVertLine;
+using uic::render::SPGHorzLine;
+using qt3ds::render::NVRenderRectF;
+using qt3ds::render::NVRenderRect;
+
+namespace {
+using namespace UICDM;
+struct STranslatorUICDMParser
+{
+ STranslation &m_Context;
+ CUICDMInstanceHandle m_InstanceHandle;
+ STranslatorUICDMParser(STranslation &inContext, CUICDMInstanceHandle inInstance)
+ : m_Context(inContext)
+ , m_InstanceHandle(inInstance)
+ {
+ }
+ CUICDMInstanceHandle GetInstanceHandle() { return m_InstanceHandle; }
+
+ template <typename TDataType>
+ inline Option<TDataType> GetPropertyValue(UICDM::CUICDMPropertyHandle inProperty)
+ {
+ Option<SValue> theValue =
+ m_Context.m_Reader.GetRawInstancePropertyValue(GetInstanceHandle(), inProperty);
+ if (theValue.hasValue())
+ return UICDM::get<TDataType>(*theValue);
+ return Empty();
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, QT3DSF32 &outValue)
+ {
+ Option<float> theValue = GetPropertyValue<float>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = theValue.getValue();
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, QT3DSU32 &outValue)
+ {
+ auto theValue = GetPropertyValue<qt3ds::QT3DSI32>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = NVMax(theValue.getValue(), 0);
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, QT3DSI32 &outValue)
+ {
+ auto theValue = GetPropertyValue<qt3ds::QT3DSI32>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = *theValue;
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseRadianProperty(CUICDMPropertyHandle inProperty, QT3DSF32 &outValue)
+ {
+ if (ParseProperty(inProperty, outValue)) {
+ TORAD(outValue);
+ return true;
+ }
+ return false;
+ }
+ bool ParseRadianProperty(CUICDMPropertyHandle inProperty, QT3DSVec3 &outValue)
+ {
+ if (ParseProperty(inProperty, outValue)) {
+ TORAD(outValue.x);
+ TORAD(outValue.y);
+ TORAD(outValue.z);
+ return true;
+ }
+ return false;
+ }
+ bool ParseOpacityProperty(CUICDMPropertyHandle inProperty, QT3DSF32 &outValue)
+ {
+ if (ParseProperty(inProperty, outValue)) {
+ outValue = (1.0f / 100.0f) * outValue;
+ return true;
+ }
+ return false;
+ }
+ bool ParseRotationOrder(CUICDMPropertyHandle inProperty, QT3DSU32 &outValue)
+ {
+ qt3ds::render::CRegisteredString temp;
+ if (ParseProperty(inProperty, temp)) {
+ outValue = uic::render::MapRotationOrder(temp);
+ return true;
+ }
+ return false;
+ }
+ bool ParseOrientation(CUICDMPropertyHandle inProperty, uic::render::NodeFlags &outValue)
+ {
+ qt3ds::render::CRegisteredString temp;
+ if (ParseProperty(inProperty, temp)) {
+ bool isLeftHanded = strcmp(temp.c_str(), "Left Handed") == 0;
+ outValue.SetLeftHanded(isLeftHanded);
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, bool &outValue)
+ {
+ Option<bool> theValue = GetPropertyValue<bool>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = theValue.getValue();
+ return true;
+ }
+ return false;
+ }
+ bool ParseProperty(CUICDMPropertyHandle inProperty, QT3DSVec2 &outValue)
+ {
+ Option<UICDM::SFloat2> theValue = GetPropertyValue<UICDM::SFloat2>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = QT3DSVec2(theValue->m_Floats[0], theValue->m_Floats[1]);
+ return true;
+ }
+ return false;
+ }
+ bool ParseProperty(CUICDMPropertyHandle inProperty, QT3DSVec3 &outValue)
+ {
+ Option<SFloat3> theValue = GetPropertyValue<SFloat3>(inProperty);
+ if (theValue.hasValue()) {
+ outValue = QT3DSVec3(theValue->m_Floats[0], theValue->m_Floats[1], theValue->m_Floats[2]);
+ return true;
+ }
+ return false;
+ }
+ bool ParseProperty(CUICDMPropertyHandle inProperty, qt3ds::render::CRegisteredString &outValue)
+ {
+ Option<UICDM::TDataStrPtr> theValue = GetPropertyValue<UICDM::TDataStrPtr>(inProperty);
+ if (theValue.hasValue() && *theValue) {
+ qt3ds::render::IStringTable &theStrTable(m_Context.m_UICContext.GetStringTable());
+ outValue = theStrTable.RegisterStr((*theValue)->GetData());
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseAndResolveSourcePath(UICDM::CUICDMPropertyHandle inProperty,
+ qt3ds::render::CRegisteredString &outValue)
+ {
+ if (ParseProperty(inProperty, outValue)) {
+ if (outValue.IsValid() && outValue.c_str()[0] != '#') {
+ Q3DStudio::CFilePath theDirectory = m_Context.m_Doc.GetDocumentDirectory();
+ Q3DStudio::CFilePath theResolvedPath =
+ Q3DStudio::CFilePath::CombineBaseAndRelative(theDirectory, outValue.c_str());
+ outValue =
+ m_Context.m_UICContext.GetStringTable().RegisterStr(theResolvedPath.c_str());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ template <typename TEnumType>
+ bool ParseEnumProperty(UICDM::CUICDMPropertyHandle inProperty, TEnumType &ioValue)
+ {
+ qt3ds::render::CRegisteredString temp;
+ if (ParseProperty(inProperty, temp)) {
+ uic::render::SEnumNameMap *theNameMap(uic::render::SEnumParseMap<TEnumType>::GetMap());
+ for (uic::render::SEnumNameMap *theIter = theNameMap;
+ theIter->m_Name && *theIter->m_Name; ++theIter) {
+ // hack to match advanced overlay types, whose name start with a '*'
+ const char8_t *p = temp;
+ if (*p == '*')
+ ++p;
+ if (strcmp(p, theIter->m_Name) == 0) {
+ ioValue = (TEnumType)theIter->m_Enum;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool ParseNodeFlagsProperty(UICDM::CUICDMPropertyHandle inProperty,
+ uic::render::NodeFlags &outValue,
+ uic::render::NodeFlagValues::Enum theFlag)
+ {
+ bool temp = false;
+ if (ParseProperty(inProperty, temp)) {
+ outValue.ClearOrSet(temp, theFlag);
+ return true;
+ }
+ return false;
+ }
+ bool ParseNodeFlagsInverseProperty(UICDM::CUICDMPropertyHandle inProperty,
+ uic::render::NodeFlags &outValue,
+ uic::render::NodeFlagValues::Enum theFlag)
+ {
+ bool temp = false;
+ if (ParseProperty(inProperty, temp)) {
+ outValue.ClearOrSet(!temp, theFlag);
+ return true;
+ }
+ return false;
+ }
+ bool ParseProperty(CUICDMPropertyHandle inProperty, uic::render::SImage *&ioImage)
+ {
+ Option<SLong4> theData = GetPropertyValue<SLong4>(inProperty);
+ if (theData.hasValue()) {
+ UICDM::CUICDMInstanceHandle theInstance(
+ m_Context.m_Reader.GetInstanceForGuid(*theData));
+ SGraphObjectTranslator *imageTranslator = m_Context.GetOrCreateTranslator(theInstance);
+ if (imageTranslator
+ && imageTranslator->GetGraphObject().m_Type
+ == uic::render::GraphObjectTypes::Image) {
+ SImage *theNewImage = static_cast<SImage *>(&imageTranslator->GetGraphObject());
+ ioImage = theNewImage;
+ } else
+ ioImage = nullptr;
+ return true;
+ }
+ return false;
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, uic::render::SGraphObject *&ioObjRef)
+ {
+ Option<SObjectRefType> theData = GetPropertyValue<SObjectRefType>(inProperty);
+ if (theData.hasValue()) {
+ UICDM::CUICDMInstanceHandle theInstance(
+ m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData));
+ SGraphObjectTranslator *theItemTranslator =
+ m_Context.GetOrCreateTranslator(theInstance);
+ if (theItemTranslator)
+ ioObjRef = &theItemTranslator->GetGraphObject();
+ }
+ return true;
+ }
+
+ bool ParseProperty(CUICDMPropertyHandle inProperty, uic::render::SNode *&ioNodePtr)
+ {
+ Option<SObjectRefType> theData = GetPropertyValue<SObjectRefType>(inProperty);
+ SNode *theNewNodePtr = nullptr;
+ if (theData.hasValue()) {
+ UICDM::CUICDMInstanceHandle theInstance(
+ m_Context.m_Reader.GetInstanceForObjectRef(m_InstanceHandle, *theData));
+ SGraphObjectTranslator *theItemTranslator =
+ m_Context.GetOrCreateTranslator(theInstance);
+ if (theItemTranslator) {
+ SGraphObject &theObject = theItemTranslator->GetGraphObject();
+ if (GraphObjectTypes::IsNodeType(theObject.m_Type))
+ theNewNodePtr = &static_cast<SNode &>(theObject);
+ }
+ }
+ ioNodePtr = theNewNodePtr;
+ return true;
+ }
+};
+
+// Define parse tables
+#define Scene_ClearColor m_Scene.m_BackgroundColor
+#define Scene_UseClearColor m_Scene.m_BgColorEnable
+#define Node_Rotation m_Node.m_Rotation
+#define Node_Position m_Node.m_Position
+#define Node_Scale m_Node.m_Scale
+#define Node_Pivot m_Node.m_Pivot
+#define Node_LocalOpacity m_Node.m_Opacity
+#define Node_RotationOrder m_Node.m_RotationOrder
+#define Node_LeftHanded m_Node.m_Orientation
+#define Layer_TemporalAAEnabled m_Layer.m_TemporalAA
+#define Layer_LayerEnableDepthTest m_Layer.m_DisableDepthTest
+#define Layer_LayerEnableDepthPrePass m_Layer.m_DisableDepthPrepass
+#define Layer_ClearColor m_Layer.m_BackgroundColor
+#define Layer_Background m_Layer.m_Background
+#define Layer_BlendType m_Layer.m_BlendType
+#define Layer_Size m_Layer.m_Size
+#define Layer_Location m_Layer.m_Location
+#define Layer_ProgressiveAAMode m_Layer.m_ProgressiveAA
+#define Layer_MultisampleAAMode m_Layer.m_MultisampleAA
+#define Layer_HorizontalFieldValues m_Layer.m_HorizontalFieldValues
+#define Layer_Left m_Layer.m_Left
+#define Layer_LeftUnits m_Layer.m_LeftUnits
+#define Layer_Width m_Layer.m_Width
+#define Layer_WidthUnits m_Layer.m_WidthUnits
+#define Layer_Right m_Layer.m_Right
+#define Layer_RightUnits m_Layer.m_RightUnits
+#define Layer_VerticalFieldValues m_Layer.m_VerticalFieldValues
+#define Layer_Top m_Layer.m_Top
+#define Layer_TopUnits m_Layer.m_TopUnits
+#define Layer_Height m_Layer.m_Height
+#define Layer_HeightUnits m_Layer.m_HeightUnits
+#define Layer_Bottom m_Layer.m_Bottom
+#define Layer_BottomUnits m_Layer.m_BottomUnits
+#define Layer_AoStrength m_Layer.m_AoStrength
+#define Layer_AoDistance m_Layer.m_AoDistance
+#define Layer_AoSoftness m_Layer.m_AoSoftness
+#define Layer_AoBias m_Layer.m_AoBias
+#define Layer_AoSamplerate m_Layer.m_AoSamplerate
+#define Layer_AoDither m_Layer.m_AoDither
+#define Layer_ShadowStrength m_Layer.m_ShadowStrength
+#define Layer_ShadowDist m_Layer.m_ShadowDist
+#define Layer_ShadowSoftness m_Layer.m_ShadowSoftness
+#define Layer_ShadowBias m_Layer.m_ShadowBias
+#define Layer_LightProbe m_Layer.m_LightProbe
+#define Layer_ProbeBright m_Layer.m_ProbeBright
+#define Layer_FastIbl m_Layer.m_FastIbl
+#define Layer_ProbeHorizon m_Layer.m_ProbeHorizon
+#define Layer_ProbeFov m_Layer.m_ProbeFov
+#define Layer_LightProbe2 m_Layer.m_LightProbe2
+#define Layer_Probe2Fade m_Layer.m_Probe2Fade
+#define Layer_Probe2Window m_Layer.m_Probe2Window
+#define Layer_Probe2Pos m_Layer.m_Probe2Pos
+#define Layer_TexturePath m_Asset.m_SourcePath
+#define Camera_ClipNear m_Camera.m_ClipNear
+#define Camera_ClipFar m_Camera.m_ClipFar
+#define Camera_FOV m_Camera.m_Fov
+#define Camera_Orthographic m_Camera.m_Orthographic
+#define Camera_ScaleMode m_Camera.m_ScaleMode
+#define Camera_ScaleAnchor m_Camera.m_ScaleAnchor
+#define Light_LightType m_Light.m_LightType
+#define Light_Scope m_Light.m_Scope
+#define Light_DiffuseColor m_Light.m_LightColor
+#define Light_SpecularColor m_Light.m_SpecularColor
+#define Light_AmbientColor m_Light.m_AmbientColor
+#define Light_Brightness m_Light.m_Brightness
+#define Light_LinearFade m_Light.m_LinearFade
+#define Light_ExponentialFade m_Light.m_ExpFade
+#define Light_AreaWidth m_Light.m_AreaWidth
+#define Light_AreaHeight m_Light.m_AreaHeight
+#define Light_CastShadow m_Light.m_CastShadow
+#define Light_ShadowBias m_Light.m_ShadowBias
+#define Light_ShadowFactor m_Light.m_ShadowFactor
+#define Light_ShadowMapRes m_Light.m_ShadowMapRes
+#define Light_ShadowMapFar m_Light.m_ShadowMapFar
+#define Light_ShadowFilter m_Light.m_ShadowFilter
+#define Model_MeshPath m_Asset.m_SourcePath
+#define Model_TessellationMode m_Model.m_Tessellation
+#define Model_EdgeTess m_Model.m_EdgeTess
+#define Model_InnerTess m_Model.m_InnerTess
+#define Lightmaps_LightmapIndirect m_Lightmaps.m_LightmapIndirect
+#define Lightmaps_LightmapRadiosity m_Lightmaps.m_LightmapRadiosity
+#define Lightmaps_LightmapShadow m_Lightmaps.m_LightmapShadow
+#define MaterialBase_IblProbe m_MaterialBase.m_IblProbe
+#define Material_Lighting m_Material.m_ShaderLighting
+#define Material_BlendMode m_Material.m_BlendMode
+#define Material_DiffuseColor m_Material.m_DiffuseColor
+#define Material_DiffuseMaps_0 m_Material.m_DiffuseMap1
+#define Material_DiffuseMaps_1 m_Material.m_DiffuseMap2
+#define Material_DiffuseMaps_2 m_Material.m_DiffuseMap3
+#define Material_EmissivePower m_Material.m_EmissivePower
+#define Material_EmissiveColor m_Material.m_EmissiveColor
+#define Material_EmissiveMap m_Material.m_EmissiveMap
+#define Material_EmissiveMap2 m_Material.m_EmissiveMap2
+#define Material_SpecularReflection m_Material.m_SpecularReflection
+#define Material_SpecularMap m_Material.m_SpecularMap
+#define Material_SpecularModel m_Material.m_SpecularModel
+#define Material_SpecularTint m_Material.m_SpecularTint
+#define Material_IOR m_Material.m_IOR
+#define Material_FresnelPower m_Material.m_FresnelPower
+#define Material_SpecularAmount m_Material.m_SpecularAmount
+#define Material_SpecularRoughness m_Material.m_SpecularRoughness
+#define Material_Opacity m_Material.m_Opacity
+#define Material_OpacityMap m_Material.m_OpacityMap
+#define Material_BumpMap m_Material.m_BumpMap
+#define Material_BumpAmount m_Material.m_BumpAmount
+#define Material_NormalMap m_Material.m_NormalMap
+#define Material_DisplacementMap m_Material.m_DisplacementMap
+#define Material_DisplaceAmount m_Material.m_DisplaceAmount
+#define Material_TranslucencyMap m_Material.m_TranslucencyMap
+#define Material_TranslucentFalloff m_Material.m_TranslucentFalloff
+#define Material_DiffuseLightWrap m_Material.m_DiffuseLightWrap
+#define Material_ReferencedMaterial m_ReferencedMaterial.m_ReferencedMaterial
+#define Image_ImagePath m_Asset.m_SourcePath
+#define Image_OffscreenRendererId m_Image.m_SubPresentation
+#define Image_Scale_X m_Image.m_RepeatU
+#define Image_Scale_Y m_Image.m_RepeatV
+#define Image_Pivot_X m_Image.m_PivotU
+#define Image_Pivot_Y m_Image.m_PivotV
+#define Image_Rotation m_Image.m_RotationUV
+#define Image_Position_X m_Image.m_PositionU
+#define Image_Position_Y m_Image.m_PositionV
+#define Image_MappingMode m_Image.m_TextureMapping
+#define Image_HorizontalTilingMode m_Image.m_TilingU
+#define Image_VerticalTilingMode m_Image.m_TilingV
+#define Text_Text m_Text.m_TextString
+#define Text_Font m_Text.m_Font
+#define Text_FontSize m_Text.m_Size
+#define Text_HorizontalAlignment m_Text.m_HorzAlign
+#define Text_VerticalAlignment m_Text.m_VertAlign
+#define Text_Leading m_Text.m_Leading
+#define Text_Tracking m_Text.m_Tracking
+#define Text_TextColor m_Text.m_TextColor
+#define Text_EnableAcceleratedFont m_Text.m_EnableAcceleratedFont
+#define Path_Width m_Path.m_Width
+#define Path_LinearError m_Path.m_LinearError
+#define Path_InnerTessAmount m_Path.m_InnerTessAmount
+#define Path_EdgeTessAmount m_Path.m_EdgeTessAmount
+#define Path_Opacity m_Path.m_Opacity
+#define Path_BeginCapping m_Path.m_BeginCap
+#define Path_BeginCapOffset m_Path.m_BeginCapOffset
+#define Path_BeginCapOpacity m_Path.m_BeginCapOpacity
+#define Path_BeginCapWidth m_Path.m_BeginCapWidth
+#define Path_EndCapping m_Path.m_EndCap
+#define Path_EndCapOffset m_Path.m_EndCapOffset
+#define Path_EndCapOpacity m_Path.m_EndCapOpacity
+#define Path_EndCapWidth m_Path.m_EndCapWidth
+#define Path_PathType m_Path.m_PathType
+#define Path_PaintStyle m_Path.m_PaintStyle
+#define Path_PathBuffer m_Asset.m_SourcePath
+#define SubPath_Closed m_SubPath.m_Closed
+
+// Fill in implementations for the actual parse tables.
+#define HANDLE_UIC_RENDER_PROPERTY(type, name, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_VEC3_PROPERTY(type, name, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_REAL_VEC2_PROPERTY(type, name, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_COLOR_PROPERTY(type, name, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_RADIAN_PROPERTY(type, name, dirty) \
+ theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_VEC3_RADIAN_PROPERTY(type, name, dirty) \
+ theParser.ParseRadianProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_OPACITY_PROPERTY(type, name, dirty) \
+ theParser.ParseOpacityProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_ROTATION_ORDER_PROPERTY(type, name, dirty) \
+ theParser.ParseRotationOrder(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_NODE_ORIENTATION_PROPERTY(type, name, dirty) \
+ theParser.ParseOrientation(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags);
+#define HANDLE_UIC_RENDER_DEPTH_TEST_PROPERTY(type, name, dirty) \
+ if (theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name)) \
+ theItem.m_##name = !theItem.m_##name;
+#define HANDLE_UIC_NODE_FLAGS_PROPERTY(type, name, dirty) \
+ theParser.ParseNodeFlagsProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_Flags, \
+ uic::render::NodeFlagValues::name);
+#define HANDLE_UIC_NODE_FLAGS_INVERSE_PROPERTY(type, name, dirty) \
+ theParser.ParseNodeFlagsInverseProperty(inContext.m_ObjectDefinitions.type##_##name, \
+ theItem.m_Flags, uic::render::NodeFlagValues::name);
+#define HANDLE_UIC_RENDER_ENUM_PROPERTY(type, name, dirty) \
+ theParser.ParseEnumProperty(inContext.m_ObjectDefinitions.type##_##name, theItem.m_##name);
+#define HANDLE_UIC_RENDER_SOURCEPATH_PROPERTY(type, name, dirty) \
+ theParser.ParseAndResolveSourcePath(inContext.m_ObjectDefinitions.type##_##name, \
+ theItem.m_##name);
+#define HANDLE_UIC_RENDER_ARRAY_PROPERTY(type, name, index, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##index, \
+ theItem.m_##name[index]);
+#define HANDLE_UIC_RENDER_VEC2_PROPERTY(type, name, dirty) \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##X, \
+ theItem.m_##name.x); \
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.type##_##name##_##Y, theItem.m_##name.y);
+#define HANDLE_UIC_RENDER_COLOR_VEC3_PROPERTY( \
+ type, name, dirty) // noop by intention already handled by HANDLE_UIC_RENDER_COLOR_PROPERTY
+#define HANDLE_UIC_RENDER_TRANSFORM_VEC3_PROPERTY( \
+ type, name, dirty) // noop by intention already handled by HANDLE_UIC_RENDER_VEC3_PROPERTY
+
+struct SSceneTranslator : public SGraphObjectTranslator
+{
+ SSceneTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SScene)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SScene &theItem = static_cast<SScene &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_SCENE_PROPERTIES
+ SLayer *theCurrentLayer = nullptr;
+ theItem.m_FirstChild = nullptr;
+ for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle());
+ idx < end; ++idx) {
+ SGraphObjectTranslator::PushTranslation(inContext);
+ UICDM::CUICDMInstanceHandle theLayer =
+ inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx);
+ SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theLayer);
+ if (theTranslator
+ && theTranslator->GetGraphObject().m_Type == uic::render::GraphObjectTypes::Layer) {
+ SLayer *theLayerObj = static_cast<SLayer *>(&theTranslator->GetGraphObject());
+ theLayerObj->m_NextSibling = nullptr;
+ if (theItem.m_FirstChild == nullptr)
+ theItem.m_FirstChild = theLayerObj;
+ else
+ theCurrentLayer->m_NextSibling = theLayerObj;
+ theCurrentLayer = theLayerObj;
+ }
+ }
+ }
+ void ClearChildren() override
+ {
+ SScene &theItem = static_cast<SScene &>(GetGraphObject());
+ SLayer *theLastChild = nullptr;
+ for (SLayer *theChild = theItem.m_FirstChild; theChild;
+ theChild = static_cast<SLayer *>(theChild->m_NextSibling)) {
+ if (theLastChild)
+ theLastChild->m_NextSibling = nullptr;
+ theChild->m_Parent = nullptr;
+ theLastChild = theChild;
+ }
+ theItem.m_FirstChild = nullptr;
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (inChild.m_Type != GraphObjectTypes::Layer) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ SScene &theItem = static_cast<SScene &>(GetGraphObject());
+ SLayer &theLayer = static_cast<SLayer &>(inChild);
+ theItem.AddChild(theLayer);
+ }
+ void SetActive(bool /*inActive*/) override
+ {
+ // How could we not be active?
+ }
+};
+
+struct SNodeTranslator : public SGraphObjectTranslator
+{
+ SNodeTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)())
+ {
+ Initialize();
+ }
+ SNodeTranslator(UICDM::CUICDMInstanceHandle inInstance, SNode &inNode)
+ : SGraphObjectTranslator(inInstance, inNode)
+ {
+ Initialize();
+ }
+ void Initialize()
+ {
+ SNode &theNode = static_cast<SNode &>(GetGraphObject());
+ // Ensure the global transform is valid because we use this before we render sometimes.
+ theNode.m_GlobalTransform = QT3DSMat44::createIdentity();
+ }
+ static inline bool IsNodeType(uic::render::GraphObjectTypes::Enum inType)
+ {
+ return uic::render::GraphObjectTypes::IsNodeType(inType);
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SGraphObjectTranslator::PushTranslation(inContext);
+ SNode &theItem = static_cast<SNode &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_NODE_PROPERTIES
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId,
+ theItem.m_SkeletonId);
+ bool ignoresParent = false;
+ if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent,
+ ignoresParent))
+ theItem.m_Flags.SetIgnoreParentTransform(ignoresParent);
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (GraphObjectTypes::IsNodeType(inChild.m_Type) == false) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ SNode &theItem = static_cast<SNode &>(GetGraphObject());
+ SNode &theChild = static_cast<SNode &>(inChild);
+ theItem.AddChild(theChild);
+ theItem.MarkDirty(uic::render::NodeTransformDirtyFlag::TransformIsDirty);
+ theChild.MarkDirty(uic::render::NodeTransformDirtyFlag::TransformIsDirty);
+ }
+ void ClearChildren() override
+ {
+ SNode &theItem = static_cast<SNode &>(GetGraphObject());
+ SNode *theLastChild = nullptr;
+ for (SNode *theChild = theItem.m_FirstChild; theChild; theChild = theChild->m_NextSibling) {
+ if (theLastChild)
+ theLastChild->m_NextSibling = nullptr;
+ theLastChild = theChild;
+ theChild->m_Parent = nullptr;
+ }
+ theItem.m_FirstChild = nullptr;
+ }
+
+ void SetActive(bool inActive) override
+ {
+ SNode &theNode = static_cast<SNode &>(GetGraphObject());
+ if (inActive != theNode.m_Flags.IsActive()) {
+ theNode.m_Flags.SetActive(inActive);
+ theNode.MarkDirty(uic::render::NodeTransformDirtyFlag::TransformIsDirty);
+ }
+ }
+};
+
+struct SLayerTranslator : public SNodeTranslator
+{
+ SLayerTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLayer)())
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ SLayer &theItem = static_cast<SLayer &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_LAYER_PROPERTIES
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Layer.m_AoSamplerate,
+ theItem.m_AoSamplerate);
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (GraphObjectTypes::IsNodeType(inChild.m_Type)) {
+ SNodeTranslator::AppendChild(inChild);
+ } else if (inChild.m_Type == GraphObjectTypes::Effect) {
+ SLayer &theItem = static_cast<SLayer &>(GetGraphObject());
+ theItem.AddEffect(static_cast<SEffect &>(inChild));
+ } else if (inChild.m_Type == GraphObjectTypes::RenderPlugin) {
+ SLayer &theItem = static_cast<SLayer &>(GetGraphObject());
+ theItem.m_RenderPlugin = &static_cast<uic::render::SRenderPlugin &>(inChild);
+ }
+ }
+ void ClearChildren() override
+ {
+ SNodeTranslator::ClearChildren();
+ SLayer &theItem = static_cast<SLayer &>(GetGraphObject());
+ SEffect *theLastChild = nullptr;
+ for (SEffect *theChild = theItem.m_FirstEffect; theChild;
+ theChild = theChild->m_NextEffect) {
+ if (theLastChild)
+ theLastChild->m_NextEffect = nullptr;
+ theLastChild = theChild;
+ theChild->m_Layer = nullptr;
+ }
+ theItem.m_FirstEffect = nullptr;
+ theItem.m_RenderPlugin = nullptr;
+ // Don't clear the light probe properties because those are added/removed as part of the
+ // normal
+ // property scan, they aren't added as generic children.
+ }
+};
+struct SLightTranslator : public SNodeTranslator
+{
+ SLightTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SLight)())
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ SLight &theItem = static_cast<SLight &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_LIGHT_PROPERTIES
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Light.m_ShadowMapRes,
+ theItem.m_ShadowMapRes);
+ }
+ void AppendChild(SGraphObject &inChild) override { SNodeTranslator::AppendChild(inChild); }
+ void ClearChildren() override { SNodeTranslator::ClearChildren(); }
+};
+struct SCameraTranslator : public SNodeTranslator
+{
+ SCameraTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SCamera)())
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ SCamera &theItem = static_cast<SCamera &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_CAMERA_PROPERTIES
+ }
+};
+struct SModelTranslator : public SNodeTranslator
+{
+ SModelTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SModel)())
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ SModel &theItem = static_cast<SModel &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_MODEL_PROPERTIES
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Model.m_PoseRoot,
+ theItem.m_SkeletonRoot);
+
+ theItem.m_FirstMaterial = nullptr;
+ for (long idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle());
+ idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theItemHandle =
+ inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx);
+ SGraphObjectTranslator *theTranslator = inContext.GetOrCreateTranslator(theItemHandle);
+ if (theTranslator && IsMaterial(theTranslator->GetGraphObject())) {
+ SGraphObject *theMaterial = &theTranslator->GetGraphObject();
+ SetNextMaterialSibling(*theMaterial, nullptr);
+ theItem.AddMaterial(*theMaterial);
+ }
+ }
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (GraphObjectTypes::IsNodeType(inChild.m_Type)) {
+ SNodeTranslator::AppendChild(inChild);
+ } else if (IsMaterial(inChild)) {
+ SModel &theItem = static_cast<SModel &>(GetGraphObject());
+ theItem.AddMaterial(inChild);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ void ClearChildren() override
+ {
+ SModel &theItem = static_cast<SModel &>(GetGraphObject());
+ SNodeTranslator::ClearChildren();
+
+ SGraphObject *theLastMaterial = nullptr;
+ for (SGraphObject *theMaterial = theItem.m_FirstMaterial; theMaterial;
+ theMaterial = uic::render::GetNextMaterialSibling(theMaterial)) {
+ if (theLastMaterial)
+ uic::render::SetNextMaterialSibling(*theLastMaterial, nullptr);
+ theLastMaterial = theMaterial;
+ }
+ theItem.m_FirstMaterial = nullptr;
+ }
+};
+
+static SFloat2 ToFloat2(const Option<SValue> &inValue)
+{
+ if (inValue.hasValue())
+ return inValue->getData<SFloat2>();
+ return SFloat2();
+}
+
+static float ToFloat(const Option<SValue> &inValue)
+{
+ if (inValue.hasValue())
+ return inValue->getData<QT3DSF32>();
+ return 0.0f;
+}
+
+struct SPathSubPathTranslator : public SGraphObjectTranslator
+{
+ eastl::vector<uic::render::SPathAnchorPoint> m_PathBuffer;
+ SPathSubPathTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SPathSubPath)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SPathSubPath &theItem = static_cast<SPathSubPath &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_PATH_SUBPATH_PROPERTIES
+ m_PathBuffer.clear();
+ Q3DStudio::IDocumentReader &theReader(inContext.m_Doc.GetDocumentReader());
+ QT3DSU32 anchorCount = 0;
+ for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle());
+ idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theAnchor =
+ inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx);
+ if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint")
+ ++anchorCount;
+ }
+ QT3DSU32 anchorIdx = 0;
+ for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(GetInstanceHandle());
+ idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theAnchor =
+ inContext.m_AssetGraph.GetChild(GetInstanceHandle(), idx);
+ if (theReader.GetObjectTypeName(theAnchor) == L"PathAnchorPoint") {
+ SFloat2 theAnchorPos = ToFloat2(theReader.GetInstancePropertyValue(
+ theAnchor,
+ inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property));
+ float theIncomingAngle = ToFloat(theReader.GetInstancePropertyValue(
+ theAnchor,
+ inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property));
+ float theIncomingDistance = ToFloat(theReader.GetInstancePropertyValue(
+ theAnchor,
+ inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property));
+ float theOutgoingDistance = ToFloat(theReader.GetInstancePropertyValue(
+ theAnchor,
+ inContext.m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property));
+ uic::render::SPathAnchorPoint thePoint(QT3DSVec2(theAnchorPos[0], theAnchorPos[1]),
+ theIncomingAngle, theIncomingAngle + 180.0f,
+ theIncomingDistance, theOutgoingDistance);
+ m_PathBuffer.push_back(thePoint);
+ ++anchorIdx;
+ }
+ }
+ inContext.m_UICContext.GetPathManager().SetPathSubPathData(
+ theItem,
+ qt3ds::foundation::toConstDataRef(m_PathBuffer.begin(), (QT3DSU32)m_PathBuffer.size()));
+ }
+
+ void AppendChild(SGraphObject &) override {}
+
+ void ClearChildren() override {}
+
+ void SetActive(bool /*inActive*/) override {}
+};
+
+struct SPathTranslator : public SNodeTranslator
+{
+ SPathTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SPath)())
+ {
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (GraphObjectTypes::IsMaterialType(inChild.m_Type)) {
+ SPath &theItem = static_cast<SPath &>(GetGraphObject());
+ theItem.AddMaterial(&inChild);
+ theItem.m_Flags.SetDirty(true);
+ } else if (inChild.m_Type == GraphObjectTypes::PathSubPath) {
+ SPath &theItem = static_cast<SPath &>(GetGraphObject());
+ theItem.AddSubPath(static_cast<SPathSubPath &>(inChild));
+ theItem.m_Flags.SetDirty(true);
+ } else {
+ SNodeTranslator::AppendChild(inChild);
+ }
+ }
+
+ void ClearChildren() override
+ {
+ SNodeTranslator::ClearChildren();
+ SPath &theItem = static_cast<SPath &>(GetGraphObject());
+ theItem.ClearMaterials();
+ theItem.ClearSubPaths();
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ SPath &theItem = static_cast<SPath &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_PATH_PROPERTIES
+ }
+};
+
+struct SDefaultMaterialTranslator : public SGraphObjectTranslator
+{
+ SDefaultMaterialTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SDefaultMaterial)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SGraphObjectTranslator::PushTranslation(inContext);
+ SDefaultMaterial &theItem = static_cast<SDefaultMaterial &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_MATERIAL_PROPERTIES
+
+ // UICDM::CUICDMInstanceHandle parent = inContext.m_AssetGraph.GetParent(
+ // GetInstanceHandle() );
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect,
+ theItem.m_Lightmaps.m_LightmapIndirect);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity,
+ theItem.m_Lightmaps.m_LightmapRadiosity);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow,
+ theItem.m_Lightmaps.m_LightmapShadow);
+ }
+
+ void AppendChild(SGraphObject &) override {}
+ void ClearChildren() override {}
+
+ void SetActive(bool /*inActive*/) override {}
+};
+
+struct SImageTranslator : public SGraphObjectTranslator
+{
+ SImageTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SImage)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SGraphObjectTranslator::PushTranslation(inContext);
+ SImage &theItem = static_cast<SImage &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_IMAGE_PROPERTIES
+
+ theItem.m_Flags.SetDirty(true);
+ theItem.m_Flags.SetTransformDirty(true);
+ }
+ void AppendChild(SGraphObject &child) override
+ {
+ SImage &theItem = static_cast<SImage &>(GetGraphObject());
+ if (child.m_Type == GraphObjectTypes::RenderPlugin)
+ theItem.m_RenderPlugin = &static_cast<uic::render::SRenderPlugin &>(child);
+ }
+ void ClearChildren() override
+ {
+ SImage &theItem = static_cast<SImage &>(GetGraphObject());
+ theItem.m_RenderPlugin = nullptr;
+ }
+
+ void SetActive(bool /*inActive*/) override {}
+};
+
+struct STextTranslator : public SNodeTranslator
+{
+ STextTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SNodeTranslator(inInstance, *QT3DS_NEW(inAlloc, SText)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SNodeTranslator::PushTranslation(inContext);
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ SText &theItem = static_cast<SText &>(GetGraphObject());
+ ITERATE_UIC_RENDER_TEXT_PROPERTIES
+ theItem.m_Flags.SetTextDirty(true);
+ }
+};
+
+inline qt3ds::QT3DSVec2 ToRenderType(const UICDM::SFloat2 &inType)
+{
+ return qt3ds::QT3DSVec2(inType.m_Floats[0], inType.m_Floats[1]);
+}
+inline qt3ds::QT3DSVec3 ToRenderType(const UICDM::SFloat3 &inType)
+{
+ return qt3ds::QT3DSVec3(inType.m_Floats[0], inType.m_Floats[1], inType.m_Floats[2]);
+}
+
+struct SDynamicObjectTranslator : public SGraphObjectTranslator
+{
+ typedef eastl::vector<eastl::pair<QT3DSU32, int>> TIdxToPropertyMap;
+ eastl::basic_string<qt3ds::foundation::TWCharEASTLConverter::TCharType> m_ConvertStr;
+ TIdxToPropertyMap m_PropertyMap;
+
+ SDynamicObjectTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &,
+ SDynamicObject &inObject)
+ : SGraphObjectTranslator(inInstance, inObject)
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SDynamicObject &theItem = static_cast<SDynamicObject &>(GetGraphObject());
+ IDynamicObjectSystem &theSystem = inContext.m_UICContext.GetDynamicObjectSystem();
+ using namespace uic::render::dynamic;
+ using qt3ds::foundation::NVConstDataRef;
+ NVConstDataRef<SPropertyDefinition> theProperties =
+ theSystem.GetProperties(theItem.m_ClassName);
+ if (m_PropertyMap.size() == 0) {
+ for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) {
+ const SPropertyDefinition &theDefinition(theProperties[idx]);
+ qt3ds::foundation::ConvertUTF(theDefinition.m_Name.c_str(), 0, m_ConvertStr);
+ const wchar_t *thePropName =
+ reinterpret_cast<const wchar_t *>(m_ConvertStr.c_str());
+ UICDM::CUICDMPropertyHandle theProperty =
+ inContext.m_Reader.FindProperty(GetInstanceHandle(), thePropName);
+ if (theProperty.Valid())
+ m_PropertyMap.push_back(eastl::make_pair(idx, theProperty.GetHandleValue()));
+ }
+ }
+ for (TIdxToPropertyMap::iterator theIter = m_PropertyMap.begin(), end = m_PropertyMap.end();
+ theIter != end; ++theIter) {
+ const SPropertyDefinition &theDefinition(theProperties[theIter->first]);
+ UICDM::CUICDMPropertyHandle theProperty = theIter->second;
+ Option<UICDM::SValue> theValueOpt =
+ inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), theProperty);
+ if (theValueOpt.hasValue()) {
+ UICDM::SValue &theValue(*theValueOpt);
+ switch (UICDM::GetValueType(theValue)) {
+ case UICDM::DataModelDataType::Long:
+ if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSI32)
+ theItem.SetPropertyValue(theDefinition, UICDM::get<qt3ds::QT3DSI32>(theValue));
+ else {
+ QT3DS_ASSERT(false);
+ }
+ break;
+ case UICDM::DataModelDataType::Bool:
+ if (theDefinition.m_DataType
+ == qt3ds::render::NVRenderShaderDataTypes::QT3DSRenderBool)
+ theItem.SetPropertyValue(theDefinition, UICDM::get<bool>(theValue));
+ else {
+ QT3DS_ASSERT(false);
+ }
+ break;
+ case UICDM::DataModelDataType::Float:
+ if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSF32)
+ theItem.SetPropertyValue(theDefinition, UICDM::get<float>(theValue));
+ else {
+ QT3DS_ASSERT(false);
+ }
+ break;
+ case UICDM::DataModelDataType::Float2:
+ if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec2)
+ theItem.SetPropertyValue(
+ theDefinition, ToRenderType(UICDM::get<UICDM::SFloat2>(theValue)));
+ else {
+ QT3DS_ASSERT(false);
+ }
+ break;
+ case UICDM::DataModelDataType::Float3:
+ if (theDefinition.m_DataType == qt3ds::render::NVRenderShaderDataTypes::QT3DSVec3)
+ theItem.SetPropertyValue(
+ theDefinition, ToRenderType(UICDM::get<UICDM::SFloat3>(theValue)));
+ else {
+ QT3DS_ASSERT(false);
+ }
+ break;
+ // Could be either an enum or a texture.
+ case UICDM::DataModelDataType::String: {
+ UICDM::TDataStrPtr theData = UICDM::get<UICDM::TDataStrPtr>(theValue);
+ if (theData) {
+ eastl::string theStr;
+ qt3ds::render::ConvertWideUTF(theData->GetData(), 0, theStr);
+ eastl::string theWorkspace;
+ theItem.SetPropertyValue(
+ theDefinition, theStr.c_str(),
+ inContext.m_Doc.GetDocumentDirectory().GetCharStar(), theWorkspace,
+ inContext.m_UICContext.GetStringTable());
+ }
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+
+ void AppendChild(SGraphObject &) override {}
+ void ClearChildren() override {}
+};
+
+struct SEffectTranslator : public SDynamicObjectTranslator
+{
+ // TODO - move this map to inContext and have it looked up by name.
+ IEffectSystem *m_EffectSystem;
+
+ SEffectTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc,
+ SEffect &inEffect)
+ : SDynamicObjectTranslator(inInstance, inAlloc, inEffect)
+ , m_EffectSystem(nullptr)
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ m_EffectSystem = &inContext.m_UICContext.GetEffectSystem();
+ SDynamicObjectTranslator::PushTranslation(inContext);
+ }
+
+ void SetActive(bool inActive) override
+ {
+ SEffect &theItem = static_cast<SEffect &>(GetGraphObject());
+ if (m_EffectSystem)
+ theItem.SetActive(inActive, *m_EffectSystem);
+ else
+ theItem.m_Flags.SetActive(inActive);
+ }
+};
+struct SCustomMaterialTranslator : public SDynamicObjectTranslator
+{
+ ICustomMaterialSystem *m_MaterialSystem;
+
+ SCustomMaterialTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ qt3ds::NVAllocatorCallback &inAlloc, SCustomMaterial &inMaterial)
+ : SDynamicObjectTranslator(inInstance, inAlloc, inMaterial)
+ , m_MaterialSystem(nullptr)
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SDynamicObjectTranslator::PushTranslation(inContext);
+ SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_CUSTOM_MATERIAL_PROPERTIES
+
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect,
+ theItem.m_Lightmaps.m_LightmapIndirect);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity,
+ theItem.m_Lightmaps.m_LightmapRadiosity);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow,
+ theItem.m_Lightmaps.m_LightmapShadow);
+ }
+
+ void SetActive(bool inActive) override
+ {
+ if (m_MaterialSystem) {
+ SCustomMaterial &theItem = static_cast<SCustomMaterial &>(GetGraphObject());
+ if (inActive != theItem.m_Flags.IsActive()) {
+ theItem.m_Flags.SetActive(inActive);
+ m_MaterialSystem->OnMaterialActivationChange(theItem, inActive);
+ }
+ }
+ }
+};
+struct SReferencedMaterialTranslator : public SGraphObjectTranslator
+{
+ SReferencedMaterialTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SReferencedMaterial)())
+ {
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ SGraphObjectTranslator::PushTranslation(inContext);
+ SReferencedMaterial &theItem = static_cast<SReferencedMaterial &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ ITERATE_UIC_RENDER_REFERENCED_MATERIAL_PROPERTIES
+
+ theItem.m_Dirty.SetDirty();
+ if (theItem.m_ReferencedMaterial == &theItem) {
+ qCCritical(qt3ds::INVALID_OPERATION,
+ "Referenced material is referencing itself.");
+ } else if (theItem.m_ReferencedMaterial
+ && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::DefaultMaterial) {
+ SDefaultMaterial *theDefaultItem =
+ static_cast<SDefaultMaterial *>(theItem.m_ReferencedMaterial);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect,
+ theDefaultItem->m_Lightmaps.m_LightmapIndirect);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity,
+ theDefaultItem->m_Lightmaps.m_LightmapRadiosity);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow,
+ theDefaultItem->m_Lightmaps.m_LightmapShadow);
+ } else if (theItem.m_ReferencedMaterial
+ && theItem.m_ReferencedMaterial->m_Type == GraphObjectTypes::CustomMaterial) {
+ SCustomMaterial *theDefaultItem =
+ static_cast<SCustomMaterial *>(theItem.m_ReferencedMaterial);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapIndirect,
+ theDefaultItem->m_Lightmaps.m_LightmapIndirect);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapRadiosity,
+ theDefaultItem->m_Lightmaps.m_LightmapRadiosity);
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Lightmaps.m_LightmapShadow,
+ theDefaultItem->m_Lightmaps.m_LightmapShadow);
+ }
+ }
+
+ void AppendChild(SGraphObject &) override {}
+
+ void ClearChildren() override {}
+
+ void SetActive(bool /*inActive*/) override {}
+};
+using uic::render::SRenderPlugin;
+using uic::render::SRenderPropertyValueUpdate;
+using uic::render::IRenderPluginClass;
+using uic::render::SRenderPluginPropertyDeclaration;
+struct SRenderPluginPropertyUpdateFactory
+{
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, float value,
+ const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &)
+ {
+ ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value));
+ }
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, qt3ds::QT3DSI32 value,
+ const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &)
+ {
+ ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value));
+ }
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, bool value,
+ const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &)
+ {
+ ioUpdates.push_back(SRenderPropertyValueUpdate(theDec.m_Name, value));
+ }
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, UICDM::TDataStrPtr value,
+ const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &,
+ qt3ds::foundation::IStringTable &strTable)
+ {
+ if (value) {
+ ioUpdates.push_back(
+ SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value->GetData())));
+ }
+ }
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates, UICDM::SStringRef value,
+ const SRenderPluginPropertyDeclaration &theDec, IRenderPluginClass &,
+ qt3ds::foundation::IStringTable &strTable)
+ {
+ ioUpdates.push_back(
+ SRenderPropertyValueUpdate(theDec.m_Name, strTable.RegisterStr(value.m_Id)));
+ }
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates,
+ const UICDM::SFloat2 &value, const SRenderPluginPropertyDeclaration &theDec,
+ IRenderPluginClass &inClass)
+ {
+ ioUpdates.push_back(SRenderPropertyValueUpdate(
+ inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0]));
+ ioUpdates.push_back(SRenderPropertyValueUpdate(
+ inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1]));
+ }
+
+ static void Add(eastl::vector<SRenderPropertyValueUpdate> &ioUpdates,
+ const UICDM::SFloat3 &value, const SRenderPluginPropertyDeclaration &theDec,
+ IRenderPluginClass &inClass)
+ {
+ ioUpdates.push_back(SRenderPropertyValueUpdate(
+ inClass.GetPropertyValueInfo(theDec.m_StartOffset).first, value.m_Floats[0]));
+ ioUpdates.push_back(SRenderPropertyValueUpdate(
+ inClass.GetPropertyValueInfo(theDec.m_StartOffset + 1).first, value.m_Floats[1]));
+ ioUpdates.push_back(SRenderPropertyValueUpdate(
+ inClass.GetPropertyValueInfo(theDec.m_StartOffset + 2).first, value.m_Floats[2]));
+ }
+};
+struct SRenderPluginTranslator : public SGraphObjectTranslator
+{
+ eastl::vector<SRenderPropertyValueUpdate> m_PropertyUpdates;
+
+ SRenderPluginTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SRenderPlugin)())
+ {
+ }
+
+ void PushTranslation(STranslation &inContext) override
+ {
+ SRenderPlugin &theItem = static_cast<SRenderPlugin &>(GetGraphObject());
+ // First, get the instance via resolving the source path.
+ CUICDMPropertyHandle sourcepath =
+ inContext.m_Reader.FindProperty(GetInstanceHandle(), L"sourcepath");
+ Option<SValue> theSourcePath =
+ inContext.m_Reader.GetInstancePropertyValue(GetInstanceHandle(), sourcepath);
+ UICDM::TDataStrPtr theData = theSourcePath->getData<UICDM::TDataStrPtr>();
+ if (!theData)
+ return;
+
+ Q3DStudio::CFilePath theFullPath = inContext.m_Doc.GetResolvedPathToDoc(theData->GetData());
+ qt3ds::foundation::IStringTable &theStrTable = inContext.m_UICContext.GetStringTable();
+ theItem.m_PluginPath = theStrTable.RegisterStr(theFullPath);
+ uic::render::IRenderPluginInstance *theInstance =
+ inContext.m_UICContext.GetRenderPluginManager().GetOrCreateRenderPluginInstance(
+ theItem.m_PluginPath, &theItem);
+
+ // Couldn't load the instance, so we can't render the instance.
+ if (theInstance == nullptr)
+ return;
+ // Grab the instance's parent and get the properties that are specific to just that
+ // instance.
+ TInstanceHandleList derivationParents;
+ std::shared_ptr<IDataCore> theDataCore =
+ inContext.m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetDataCore();
+ theDataCore->GetInstanceParents(GetInstanceHandle(), derivationParents);
+ if (derivationParents.size() == 0)
+ return;
+ TPropertyHandleList theSpecificProperties;
+ theDataCore->GetInstanceProperties(derivationParents[0], theSpecificProperties);
+ eastl::string propStem;
+ eastl::string propname;
+ m_PropertyUpdates.clear();
+ uic::render::IRenderPluginClass &theClass = theInstance->GetPluginClass();
+ using uic::render::SRenderPluginPropertyDeclaration;
+ if (theClass.GetRegisteredProperties().size() == 0) {
+ for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) {
+ SUICDMPropertyDefinition theProperty =
+ theDataCore->GetProperty(theSpecificProperties[idx]);
+ UICDM::AdditionalMetaDataType::Value theMetaType =
+ inContext.m_StudioSystem.GetActionMetaData()->GetAdditionalMetaDataType(
+ GetInstanceHandle(), theSpecificProperties[idx]);
+ CRegisteredString thePropName(theStrTable.RegisterStr(theProperty.m_Name.c_str()));
+ switch (theProperty.m_Type) {
+ case DataModelDataType::Float:
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Float));
+ break;
+ case DataModelDataType::Float2:
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Vector2));
+ break;
+ case DataModelDataType::Float3:
+ if (theMetaType != AdditionalMetaDataType::Color) {
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Vector3));
+ } else {
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Color));
+ }
+ break;
+ case DataModelDataType::Long:
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Long));
+ break;
+ case DataModelDataType::String:
+ case DataModelDataType::StringRef:
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::String));
+ break;
+ case DataModelDataType::Bool:
+ theClass.RegisterProperty(SRenderPluginPropertyDeclaration(
+ thePropName, uic::render::SRenderPluginPropertyTypes::Boolean));
+ break;
+ default:
+ // Unsupported plugin property.
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+ for (size_t idx = 0, end = theSpecificProperties.size(); idx < end; ++idx) {
+ Option<SValue> thePropValueOpt = inContext.m_Reader.GetInstancePropertyValue(
+ GetInstanceHandle(), theSpecificProperties[idx]);
+ if (thePropValueOpt.hasValue()) {
+ SValue &thePropValue = thePropValueOpt.getValue();
+ SUICDMPropertyDefinition theProperty =
+ theDataCore->GetProperty(theSpecificProperties[idx]);
+ SRenderPluginPropertyDeclaration theDeclaration(theClass.GetPropertyDeclaration(
+ theStrTable.RegisterStr(theProperty.m_Name.c_str())));
+
+ switch (thePropValue.getType()) {
+ case DataModelDataType::None:
+ QT3DS_ASSERT(false);
+ break;
+ case DataModelDataType::Float:
+ SRenderPluginPropertyUpdateFactory::Add(
+ m_PropertyUpdates, thePropValue.getData<float>(), theDeclaration, theClass);
+ break;
+ case DataModelDataType::Float2:
+ SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates,
+ thePropValue.getData<SFloat2>(),
+ theDeclaration, theClass);
+ break;
+ case DataModelDataType::Float3:
+ SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates,
+ thePropValue.getData<SFloat3>(),
+ theDeclaration, theClass);
+ break;
+ case DataModelDataType::Long:
+ SRenderPluginPropertyUpdateFactory::Add(
+ m_PropertyUpdates, thePropValue.getData<qt3ds::QT3DSI32>(), theDeclaration, theClass);
+ break;
+ case DataModelDataType::String:
+ SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates,
+ thePropValue.getData<TDataStrPtr>(),
+ theDeclaration, theClass, theStrTable);
+ break;
+ case DataModelDataType::Bool:
+ SRenderPluginPropertyUpdateFactory::Add(
+ m_PropertyUpdates, thePropValue.getData<bool>(), theDeclaration, theClass);
+ break;
+ case DataModelDataType::StringRef:
+ SRenderPluginPropertyUpdateFactory::Add(m_PropertyUpdates,
+ thePropValue.getData<SStringRef>(),
+ theDeclaration, theClass, theStrTable);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+ theInstance->Update(NVConstDataRef<SRenderPropertyValueUpdate>(m_PropertyUpdates.data(),
+ m_PropertyUpdates.size()));
+ }
+ void AppendChild(SGraphObject &) override {}
+ void ClearChildren() override {}
+ void SetActive(bool inActive) override
+ {
+ SRenderPlugin &theItem = static_cast<SRenderPlugin &>(GetGraphObject());
+ theItem.m_Flags.SetActive(inActive);
+ }
+};
+
+struct SAliasTranslator : public SGraphObjectTranslator
+{
+ SGraphObjectTranslator *m_ReferenceTree;
+ CUICDMInstanceHandle m_ReferencedInstance;
+ SAliasTranslator(UICDM::CUICDMInstanceHandle inInstance, qt3ds::NVAllocatorCallback &inAlloc)
+ : SGraphObjectTranslator(inInstance, *QT3DS_NEW(inAlloc, SNode)())
+ , m_ReferenceTree(nullptr)
+ {
+ }
+ void RecurseAndCreateTranslators(STranslation &inContext,
+ UICDM::CUICDMInstanceHandle inInstance)
+ {
+ for (QT3DSI32 idx = 0, end = inContext.m_AssetGraph.GetChildCount(inInstance); idx < end;
+ ++idx) {
+ UICDM::CUICDMInstanceHandle theChild = inContext.m_AssetGraph.GetChild(inInstance, idx);
+ inContext.GetOrCreateTranslator(theChild, m_InstanceHandle);
+ RecurseAndCreateTranslators(inContext, theChild);
+ }
+ }
+ void PushTranslation(STranslation &inContext) override
+ {
+ STranslatorUICDMParser theParser(inContext, GetInstanceHandle());
+ Option<SObjectRefType> theData = theParser.GetPropertyValue<SObjectRefType>(
+ inContext.m_ObjectDefinitions.m_Alias.m_ReferencedNode);
+ m_ReferencedInstance = CUICDMInstanceHandle();
+ m_ReferenceTree = nullptr;
+ ((SNode *)m_GraphObject)->m_Flags.SetDirty(true);
+ if (theData.hasValue()) {
+ m_ReferencedInstance =
+ inContext.m_Reader.GetInstanceForObjectRef(GetInstanceHandle(), *theData);
+ if (inContext.m_Reader.IsInstance(m_ReferencedInstance)) {
+ m_ReferenceTree =
+ inContext.GetOrCreateTranslator(m_ReferencedInstance, m_InstanceHandle);
+ if (m_ReferenceTree
+ && !GraphObjectTypes::IsNodeType(m_ReferenceTree->GetGraphObject().m_Type)) {
+ QT3DS_ASSERT(false);
+ m_ReferenceTree = nullptr;
+ m_ReferencedInstance = CUICDMInstanceHandle();
+ } else {
+ RecurseAndCreateTranslators(inContext, m_ReferencedInstance);
+ }
+ }
+ }
+ }
+
+ void AfterRenderGraphIsBuilt(STranslation &inContext) override
+ {
+ SNode &theItem = static_cast<SNode &>(GetGraphObject());
+ STranslatorUICDMParser theParser(inContext, m_InstanceHandle);
+ ITERATE_UIC_RENDER_NODE_PROPERTIES
+ theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_BoneId,
+ theItem.m_SkeletonId);
+ bool ignoresParent = false;
+ if (theParser.ParseProperty(inContext.m_ObjectDefinitions.m_Node.m_IgnoresParent,
+ ignoresParent))
+ theItem.m_Flags.SetIgnoreParentTransform(ignoresParent);
+ theItem.m_Flags.SetDirty(true);
+ }
+ void AppendChild(SGraphObject &inObject) override
+ {
+ if (m_ReferenceTree)
+ m_ReferenceTree->AppendChild(inObject);
+ }
+ void ClearChildren() override
+ {
+ if (m_ReferenceTree)
+ m_ReferenceTree->ClearChildren();
+ }
+ void SetActive(bool inActive) override
+ {
+ SNode &theItem = static_cast<SNode &>(GetGraphObject());
+ theItem.m_Flags.SetActive(inActive);
+ }
+ SGraphObject &GetGraphObject() override
+ {
+ if (m_ReferenceTree)
+ return *m_ReferenceTree->m_GraphObject;
+ return *m_GraphObject;
+ }
+ UICDM::CUICDMInstanceHandle GetSceneGraphInstanceHandle() override
+ {
+ if (m_ReferencedInstance.Valid())
+ return m_ReferencedInstance;
+ return m_InstanceHandle;
+ }
+ CUICDMInstanceHandle GetInstanceHandle() override { return m_InstanceHandle; }
+
+ SGraphObject &GetNonAliasedGraphObject() override { return *m_GraphObject; }
+};
+}
+
+void SGraphObjectTranslator::PushTranslation(STranslation &inTranslatorContext)
+{
+ Q3DStudio::CString theId = inTranslatorContext.m_Reader.GetFileId(GetInstanceHandle());
+ if (theId.size())
+ GetGraphObject().m_Id =
+ inTranslatorContext.m_UICContext.GetStringTable().RegisterStr(theId.c_str());
+}
+
+bool STranslation::IncludeNode(const SNode &inNode)
+{
+ SGraphObjectTranslator *theTranslator = inNode.m_UserData.DynamicCast<SGraphObjectTranslator>();
+ if (theTranslator
+ && m_Doc.GetDocumentReader().IsCurrentlyActive(theTranslator->GetInstanceHandle()))
+ return true;
+ return false;
+}
+
+void STranslation::ReleaseEffect(UICDM::CUICDMInstanceHandle inInstance)
+{
+ if (m_Reader.IsInstance(inInstance) == false)
+ return;
+
+ UICDM::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance);
+ UICDM::CUICDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance);
+
+ if (theType == NULL && theParentClass.Valid())
+ theType = m_ObjectDefinitions.GetType(theParentClass);
+
+ if (theType == UICDM::ComposerObjectTypes::Effect) {
+ IEffectSystem &theSystem = m_UICContext.GetEffectSystem();
+ if (theParentClass.Valid()) {
+ Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass);
+ CRegisteredString theNameStr =
+ m_UICContext.GetStringTable().RegisterStr(theInstanceName);
+
+ if (theSystem.IsEffectRegistered(theNameStr)) {
+ TInstanceToTranslatorMap::iterator theTranslatorList =
+ m_TranslatorMap.find(inInstance);
+ if (theTranslatorList != m_TranslatorMap.end())
+ m_TranslatorMap.erase(theTranslatorList);
+ theSystem.SetEffectRequiresCompilation(theNameStr, true);
+ }
+ }
+ }
+}
+
+SGraphObjectTranslator *STranslation::CreateTranslator(UICDM::CUICDMInstanceHandle inInstance)
+{
+ SGraphObjectTranslator *theNewTranslator = nullptr;
+ UICDM::ComposerObjectTypes::Enum theType = m_ObjectDefinitions.GetType(inInstance);
+ UICDM::CUICDMInstanceHandle theParentClass = m_Reader.GetFirstBaseClass(inInstance);
+ if (theType == NULL && theParentClass.Valid())
+ theType = m_ObjectDefinitions.GetType(theParentClass);
+
+ // For the subset of possible instances, pick out the valid translators.
+ switch (theType) {
+ case UICDM::ComposerObjectTypes::Group:
+ case UICDM::ComposerObjectTypes::Component:
+ case UICDM::ComposerObjectTypes::Node:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SNodeTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Scene:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SSceneTranslator)(inInstance, m_Allocator);
+ m_Scene = static_cast<SScene *>(&theNewTranslator->GetGraphObject());
+ m_Scene->m_Presentation = &m_Presentation;
+ break;
+ case UICDM::ComposerObjectTypes::Layer:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SLayerTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Light:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SLightTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Camera:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SCameraTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Model:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SModelTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Image:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SImageTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Text:
+ theNewTranslator = QT3DS_NEW(m_Allocator, STextTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Material:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SDefaultMaterialTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::ReferencedMaterial:
+ theNewTranslator =
+ QT3DS_NEW(m_Allocator, SReferencedMaterialTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Alias:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SAliasTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Path:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SPathTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::SubPath:
+ theNewTranslator = QT3DS_NEW(m_Allocator, SPathSubPathTranslator)(inInstance, m_Allocator);
+ break;
+ case UICDM::ComposerObjectTypes::Effect: {
+ IEffectSystem &theSystem = m_UICContext.GetEffectSystem();
+ if (theParentClass.Valid()) {
+ Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass);
+ CRegisteredString theNameStr =
+ m_UICContext.GetStringTable().RegisterStr(theInstanceName);
+
+ if (theSystem.IsEffectRegistered(theNameStr)
+ && theSystem.DoesEffectRequireCompilation(theNameStr)) {
+ theSystem.UnregisterEffect(theNameStr);
+ }
+
+ if (!theSystem.IsEffectRegistered(theNameStr)) {
+ // We assume the effect has already been registered and such.
+ UICDM::IMetaData &theMetaData(*m_StudioSystem.GetActionMetaData());
+ Q3DStudio::CString theInstancePath = m_Reader.GetSourcePath(theParentClass);
+ Option<UICDM::SMetaDataEffect> theMetaEffect =
+ theMetaData.GetEffectBySourcePath(
+ m_UICContext.GetStringTable().GetNarrowStr(theInstancePath));
+ if (theMetaEffect.hasValue()) {
+ uic::render::IUIPLoader::CreateEffectClassFromMetaEffect(
+ theNameStr, m_UICContext.GetFoundation(), theSystem, theMetaEffect,
+ m_UICContext.GetStringTable());
+ theSystem.SetEffectRequiresCompilation(theNameStr, true);
+ }
+ }
+
+ if (theSystem.IsEffectRegistered(theNameStr)) {
+ theNewTranslator = QT3DS_NEW(m_Allocator, SEffectTranslator)(
+ inInstance, m_Allocator,
+ *theSystem.CreateEffectInstance(theNameStr, m_Allocator));
+ }
+ }
+ } break;
+ case UICDM::ComposerObjectTypes::CustomMaterial: {
+ ICustomMaterialSystem &theSystem = m_UICContext.GetCustomMaterialSystem();
+ if (theParentClass.Valid()) {
+ Q3DStudio::CString theInstanceName = m_Reader.GetName(theParentClass);
+ CRegisteredString theNameStr =
+ m_UICContext.GetStringTable().RegisterStr(theInstanceName);
+ if (!theSystem.IsMaterialRegistered(theNameStr)) {
+ // We assume the effect has already been registered and such.
+ UICDM::IMetaData &theMetaData(*m_StudioSystem.GetActionMetaData());
+ Q3DStudio::CString theInstancePath = m_Reader.GetSourcePath(theParentClass);
+ Option<UICDM::SMetaDataCustomMaterial> theMaterialData =
+ theMetaData.GetMaterialBySourcePath(
+ m_UICContext.GetStringTable().GetNarrowStr(theInstancePath));
+ if (theMaterialData.hasValue()) {
+ uic::render::IUIPLoader::CreateMaterialClassFromMetaMaterial(
+ theNameStr, m_UICContext.GetFoundation(), theSystem, theMaterialData,
+ m_UICContext.GetStringTable());
+ }
+ }
+ if (theSystem.IsMaterialRegistered(theNameStr)) {
+ theNewTranslator = QT3DS_NEW(m_Allocator, SCustomMaterialTranslator)(
+ inInstance, m_Allocator,
+ *theSystem.CreateCustomMaterial(theNameStr, m_Allocator));
+ static_cast<SCustomMaterialTranslator *>(theNewTranslator)->m_MaterialSystem =
+ &theSystem;
+ }
+ }
+ } break;
+ case UICDM::ComposerObjectTypes::RenderPlugin: {
+ theNewTranslator = QT3DS_NEW(m_Allocator, SRenderPluginTranslator)(inInstance, m_Allocator);
+ } break;
+ }
+ return theNewTranslator;
+}
+
+bool CompareTranslator(const STranslation::THandleTranslatorPair &first,
+ const STranslation::THandleTranslatorPair &second)
+{
+ return first.first == second.first;
+}
+
+struct STranslatorPredicate
+{
+ CUICDMInstanceHandle m_Instance;
+ STranslatorPredicate(CUICDMInstanceHandle &ins)
+ : m_Instance(ins)
+ {
+ }
+ bool operator()(const STranslation::THandleTranslatorPair &first) const
+ {
+ return first.first == m_Instance;
+ }
+};
+
+Option<STranslation::THandleTranslatorPair>
+FindTranslator(STranslation::THandleTranslatorPairList &inList,
+ CUICDMInstanceHandle inInstance = CUICDMInstanceHandle())
+{
+ STranslation::THandleTranslatorPairList::iterator iter =
+ eastl::find_if(inList.begin(), inList.end(), STranslatorPredicate(inInstance));
+ if (iter != inList.end())
+ return *iter;
+ return Empty();
+}
+
+SGraphObjectTranslator *STranslation::GetOrCreateTranslator(UICDM::CUICDMInstanceHandle inInstance)
+{
+ return GetOrCreateTranslator(inInstance, CUICDMInstanceHandle());
+}
+
+SGraphObjectTranslator *
+STranslation::GetOrCreateTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inAliasInstance)
+{
+ TInstanceToTranslatorMap::iterator theTranslatorList =
+ m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList())).first;
+ THandleTranslatorPairList &theList = theTranslatorList->second;
+ Option<STranslation::THandleTranslatorPair> theExistingTranslator =
+ FindTranslator(theList, inAliasInstance);
+
+ if (theExistingTranslator.hasValue()) {
+ return theExistingTranslator->second;
+ }
+ if (m_Reader.IsInstance(inInstance) == false)
+ return nullptr;
+
+ SGraphObjectTranslator *theNewTranslator = CreateTranslator(inInstance);
+ if (theNewTranslator != nullptr) {
+ theNewTranslator->m_AliasInstanceHandle = inAliasInstance;
+ m_DirtySet.insert(*theNewTranslator);
+ theList.push_back(THandleTranslatorPair(inAliasInstance, theNewTranslator));
+ }
+
+ return theNewTranslator;
+}
+
+STranslation::THandleTranslatorPairList &
+STranslation::GetTranslatorsForInstance(UICDM::CUICDMInstanceHandle inInstance)
+{
+ return m_TranslatorMap.insert(eastl::make_pair(inInstance, THandleTranslatorPairList()))
+ .first->second;
+}
+
+UICDM::CUICDMInstanceHandle STranslation::GetAnchorPoint(QT3DSU32 inAnchorIndex)
+{
+ SGraphObjectTranslator *thePathTranslator =
+ static_cast<SGraphObjectTranslator *>(m_PathWidget->GetNode().m_UserData.m_UserData);
+ if (thePathTranslator == nullptr)
+ return UICDM::CUICDMInstanceHandle();
+ UICDM::CUICDMInstanceHandle thePathHandle = thePathTranslator->GetInstanceHandle();
+ QT3DSU32 theAnchorIndex = 0;
+ for (QT3DSI32 idx = 0, end = m_AssetGraph.GetChildCount(thePathHandle); idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theChildInstance = m_AssetGraph.GetChild(thePathHandle, idx);
+ if (m_Doc.GetDocumentReader().GetObjectTypeName(theChildInstance) == L"SubPath") {
+ QT3DSI32 numAnchors = m_AssetGraph.GetChildCount(theChildInstance);
+ QT3DSU32 endIndex = theAnchorIndex + (QT3DSU32)numAnchors;
+ if (endIndex > inAnchorIndex) {
+ return m_AssetGraph.GetChild(theChildInstance, inAnchorIndex - theAnchorIndex);
+ } else
+ theAnchorIndex = endIndex;
+ }
+ }
+ return UICDM::CUICDMInstanceHandle();
+}
+
+UICDM::CUICDMInstanceHandle STranslation::GetAnchorPoint(SPathPick &inPick)
+{
+ return GetAnchorPoint(inPick.m_AnchorIndex);
+}
+
+namespace uic {
+namespace studio {
+ struct SEditCameraLayerTranslator : public SLayerTranslator
+ {
+ SEditCameraLayerTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ qt3ds::NVAllocatorCallback &inAlloc)
+ : SLayerTranslator(inInstance, inAlloc)
+ {
+ }
+ void PushTranslation(STranslation &) override
+ {
+ SLayer &theItem = static_cast<SLayer &>(GetGraphObject());
+ theItem.m_Flags.SetActive(true);
+ }
+ void AppendChild(SGraphObject &inChild) override
+ {
+ if (GraphObjectTypes::IsNodeType(inChild.m_Type)) {
+ SNodeTranslator::AppendChild(inChild);
+ }
+ }
+ };
+}
+}
+
+STranslation::STranslation(IStudioRenderer &inRenderer, IUICRenderContext &inContext)
+ : m_Renderer(inRenderer)
+ , m_UICContext(inContext)
+ , m_Doc(*g_StudioApp.GetCore()->GetDoc())
+ , m_Reader(m_Doc.GetDocumentReader())
+ , m_ObjectDefinitions(
+ m_Doc.GetStudioSystem()->GetClientDataModelBridge()->GetObjectDefinitions())
+ , m_StudioSystem(*m_Doc.GetStudioSystem())
+ , m_FullSystem(*m_Doc.GetStudioSystem()->GetFullSystem())
+ , m_AssetGraph(*m_Doc.GetAssetGraph())
+ , m_Allocator(inContext.GetRenderContext().GetFoundation())
+ , m_TranslatorMap(inContext.GetAllocator(), "STranslation::m_TranslatorMap")
+ , m_DirtySet(inContext.GetAllocator(), "STranslation::m_DirtySet")
+ , m_Scene(nullptr)
+ , m_SignalConnections(inContext.GetAllocator(), "STranslation::m_SignalConnections")
+ , m_ComponentSecondsDepth(0)
+ , m_KeyRepeat(0)
+ , m_EditCameraEnabled(false)
+ , m_EditLightEnabled(false)
+ , m_Viewport(0, 0)
+ , m_EditCameraLayerTranslator(nullptr)
+ , m_PixelBuffer(inContext.GetAllocator(), "STranslation::m_PixelBuffer")
+ , m_GuideAllocator(inContext.GetAllocator(), "STranslation::m_GuideAllocator")
+{
+ m_EditCamera.m_Flags.SetActive(true);
+ m_EditLight.m_Flags.SetActive(true);
+ UICDM::CUICDMInstanceHandle theScene = m_AssetGraph.GetRoot(0);
+ m_GraphIterator.ClearResults();
+ m_AssetGraph.GetDepthFirst(m_GraphIterator, theScene);
+ for (; !m_GraphIterator.IsDone(); ++m_GraphIterator) {
+ UICDM::CUICDMInstanceHandle theInstance(m_GraphIterator.GetCurrent());
+ GetOrCreateTranslator(theInstance);
+ }
+ UICDM::IStudioFullSystemSignalProvider *theProvider = m_FullSystem.GetSignalProvider();
+ m_SignalConnections.push_back(
+ theProvider->ConnectInstanceCreated(boost::bind(&STranslation::MarkDirty, this, _1)));
+ m_SignalConnections.push_back(theProvider->ConnectInstanceDeleted(
+ boost::bind(&STranslation::ReleaseTranslation, this, _1)));
+ m_SignalConnections.push_back(
+ theProvider->ConnectInstancePropertyValue(boost::bind(&STranslation::MarkDirty, this, _1)));
+ m_SignalConnections.push_back(m_AssetGraph.ConnectChildAdded(
+ boost::bind(&STranslation::MarkGraphInstanceDirty, this, _1, _2)));
+ m_SignalConnections.push_back(m_AssetGraph.ConnectChildMoved(
+ boost::bind(&STranslation::MarkGraphInstanceDirty, this, _1, _2)));
+ m_SignalConnections.push_back(m_AssetGraph.ConnectChildRemoved(
+ boost::bind(&STranslation::MarkGraphInstanceDirty, this, _1, _2)));
+ m_SignalConnections.push_back(theProvider->ConnectBeginComponentSeconds(
+ boost::bind(&STranslation::MarkBeginComponentSeconds, this, _1)));
+ m_SignalConnections.push_back(theProvider->ConnectComponentSeconds(
+ boost::bind(&STranslation::MarkComponentSeconds, this, _1)));
+
+ ::CColor color = CStudioPreferences::GetRulerBackgroundColor(); // Rectangles under tick marks
+ m_rectColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+ color = CStudioPreferences::GetRulerTickColor(); // Tick marks
+ m_lineColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+ color = CStudioPreferences::GetGuideColor();
+ m_guideColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+ color = CStudioPreferences::GetGuideSelectedColor();
+ m_selectedGuideColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+ color = CStudioPreferences::GetGuideFillColor(); // Not sure what this is used for
+ m_guideFillColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+ color = CStudioPreferences::GetGuideFillSelectedColor(); // Not sure what this is used for
+ m_selectedGuideFillColor = QT3DSVec4(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f,
+ 1.f);
+}
+
+void STranslation::BuildRenderGraph(SGraphObjectTranslator &inParent,
+ CUICDMInstanceHandle inAliasHandle)
+{
+ SGraphObjectTranslator &theParentTranslator(inParent);
+ theParentTranslator.ClearChildren();
+ if (m_EditCameraEnabled
+ && theParentTranslator.GetGraphObject().m_Type == GraphObjectTypes::Layer) {
+ theParentTranslator.AppendChild(m_EditCamera);
+ if (m_EditLightEnabled) {
+ m_EditLight.m_Parent = &m_EditCamera;
+ m_EditCamera.m_FirstChild = &m_EditLight;
+ } else {
+ m_EditCamera.m_FirstChild = nullptr;
+ m_EditLight.m_Parent = nullptr;
+ }
+ }
+ // Alias handles propagate down the scene graph.
+ if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle())
+ inAliasHandle = inParent.GetInstanceHandle();
+ for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle());
+ idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theChild(
+ m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx));
+ SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle);
+ if (theTranslator == nullptr)
+ continue;
+
+ // We we have edit cameras active, we only render the active layer and we remove any cameras
+ // in the active layer. Furthermore if our edit light is active, then we also remove any
+ // active lights in the layer.
+ if (m_EditCameraEnabled) {
+ if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Layer) {
+ if (theChild == m_Doc.GetActiveLayer()) {
+ if (m_EditCameraLayerTranslator == nullptr)
+ m_EditCameraLayerTranslator =
+ QT3DS_NEW(m_Allocator, SEditCameraLayerTranslator)(theChild, m_Allocator);
+ else
+ m_EditCameraLayerTranslator->GetInstanceHandle() = theChild;
+ theTranslator = m_EditCameraLayerTranslator;
+ theParentTranslator.AppendChild(theTranslator->GetGraphObject());
+ BuildRenderGraph(*m_EditCameraLayerTranslator);
+ }
+ } else {
+ theParentTranslator.AppendChild(theTranslator->GetGraphObject());
+ BuildRenderGraph(theChild, inAliasHandle);
+
+ if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Effect)
+ theTranslator->SetActive(false);
+ else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Camera)
+ theTranslator->SetActive(false);
+ else if (theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Light
+ && m_EditLightEnabled == true)
+ theTranslator->SetActive(false);
+ else
+ theTranslator->SetActive(m_Reader.IsCurrentlyActive(theChild));
+ }
+ } else // Else build the graph and it will be an exact copy of the asset graph.
+ {
+ theParentTranslator.AppendChild(theTranslator->GetGraphObject());
+ if (m_Reader.IsCurrentlyActive(theChild)) {
+ BuildRenderGraph(theChild, inAliasHandle);
+ theTranslator->SetActive(true);
+ } else {
+ theTranslator->SetActive(false);
+ DeactivateScan(*theTranslator, inAliasHandle);
+ }
+ }
+ }
+ if (GraphObjectTypes::Layer == theParentTranslator.GetGraphObject().m_Type)
+ m_UICContext.GetRenderer().ChildrenUpdated(
+ static_cast<SLayer &>(theParentTranslator.GetGraphObject()));
+
+ // Allow certain nodes to override their children.
+ theParentTranslator.AfterRenderGraphIsBuilt(*this);
+}
+
+void STranslation::DeactivateScan(SGraphObjectTranslator &inParent,
+ CUICDMInstanceHandle inAliasHandle)
+{
+ SGraphObjectTranslator &theParentTranslator(inParent);
+ // Alias handles propagate down the scene graph.
+ if (inParent.GetInstanceHandle() != inParent.GetSceneGraphInstanceHandle())
+ inAliasHandle = inParent.GetInstanceHandle();
+ for (long idx = 0, end = m_AssetGraph.GetChildCount(inParent.GetSceneGraphInstanceHandle());
+ idx < end; ++idx) {
+ UICDM::CUICDMInstanceHandle theChild(
+ m_AssetGraph.GetChild(inParent.GetSceneGraphInstanceHandle(), idx));
+ SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theChild, inAliasHandle);
+ if (theTranslator == nullptr)
+ continue;
+ theTranslator->SetActive(false);
+ DeactivateScan(*theTranslator, inAliasHandle);
+ }
+}
+
+// We build the render graph every time we render. This may seem wasteful
+void STranslation::BuildRenderGraph(UICDM::CUICDMInstanceHandle inParent,
+ CUICDMInstanceHandle inAliasHandle)
+{
+ SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(inParent, inAliasHandle);
+ if (theParentTranslator == nullptr)
+ return;
+ if (m_Reader.IsCurrentlyActive(inParent) == false) {
+ theParentTranslator->SetActive(false);
+ return;
+ }
+ BuildRenderGraph(*theParentTranslator, inAliasHandle);
+}
+
+void STranslation::ReleaseTranslation(Q3DStudio::TIdentifier inInstance)
+{
+ m_TranslatorMap.erase(inInstance);
+}
+
+void STranslation::MarkDirty(UICDM::CUICDMInstanceHandle inInstance)
+{
+ // Anchor points are not handled individually.
+ if (m_Reader.GetObjectTypeName(inInstance) == L"PathAnchorPoint")
+ inInstance = m_AssetGraph.GetParent(inInstance);
+ GetOrCreateTranslator(inInstance);
+
+ THandleTranslatorPairList &theTranslators = GetTranslatorsForInstance(inInstance);
+ for (size_t idx = 0, end = theTranslators.size(); idx < end; ++idx) {
+ m_DirtySet.insert(*theTranslators[(eastl::allocator::size_type)idx].second);
+ }
+ RequestRender();
+}
+
+void STranslation::PreRender()
+{
+ // Run through the entire asset graph and mark active or inactive if we have an
+ // associated render representation.
+ // If we cache all the components and some of their state then we don't have to do this
+ // but for now it is more stable to run through the graph.
+ // There is always one root, the scene.
+ TIdentifier theRoot = m_AssetGraph.GetRoot(0);
+ ClearDirtySet();
+ BuildRenderGraph(theRoot);
+ m_UICContext.SetScaleMode(uic::render::ScaleModes::ExactSize);
+ m_UICContext.SetMatteColor(QT3DSVec4(.13f, .13f, .13f, 1.0f));
+ QT3DSVec2 theViewportDims(GetViewportDimensions());
+ // Ensure the camera points where it should
+ if (m_EditCameraEnabled) {
+ m_EditCameraInfo.ApplyToCamera(m_EditCamera, GetViewportDimensions());
+ m_EditLight.MarkDirty(uic::render::NodeTransformDirtyFlag::TransformIsDirty);
+ }
+
+ if (m_Scene) {
+ CStudioProjectSettings *theSettings = m_Doc.GetCore()->GetStudioProjectSettings();
+ CPt thePresSize = theSettings->GetPresentationSize();
+ // The presentation sizes are used for when we have to render a layer offscreen. If their
+ // width and height
+ // isn't set, then they use the presentation dimensions.
+ m_Presentation.m_PresentationDimensions =
+ QT3DSVec2((QT3DSF32)thePresSize.x, (QT3DSF32)thePresSize.y);
+ QT3DSVec2 theViewportDims(GetViewportDimensions());
+ m_UICContext.SetWindowDimensions(
+ uic::render::SWindowDimensions((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y));
+ m_UICContext.SetPresentationDimensions(
+ uic::render::SWindowDimensions((QT3DSU32)m_Presentation.m_PresentationDimensions.x,
+ (QT3DSU32)m_Presentation.m_PresentationDimensions.y));
+
+ // set if we draw geometry in wireframe mode
+ m_UICContext.SetWireframeMode(CStudioPreferences::IsWireframeModeOn());
+
+ if (m_EditCameraEnabled) {
+ m_Presentation.m_PresentationDimensions = theViewportDims;
+ m_UICContext.SetPresentationDimensions(
+ uic::render::SWindowDimensions((QT3DSU32)theViewportDims.x, (QT3DSU32)theViewportDims.y));
+ ::CColor theEditCameraBackground = CStudioPreferences::GetEditViewBackgroundColor();
+ m_UICContext.SetSceneColor(QT3DSVec4(theEditCameraBackground.GetRed() / 255.0f,
+ theEditCameraBackground.GetGreen() / 255.0f,
+ theEditCameraBackground.GetBlue() / 255.0f, 1.0f));
+ } else {
+
+ TIdentifier theRoot = m_AssetGraph.GetRoot(0);
+ SGraphObjectTranslator *theSceneTranslator = GetOrCreateTranslator(theRoot);
+ if (theSceneTranslator) {
+ SScene &theScene = static_cast<SScene &>(theSceneTranslator->GetGraphObject());
+ if (theScene.m_UseClearColor)
+ m_UICContext.SetSceneColor(QT3DSVec4(theScene.m_ClearColor, 1.0f));
+ else
+ m_UICContext.SetSceneColor(QT3DSVec4(QT3DSVec3(0.0f), 1.0f));
+ }
+ }
+ }
+ if (m_EditCameraEnabled == false && g_StudioApp.IsAuthorZoom()) {
+ if (m_Presentation.m_PresentationDimensions.x > theViewportDims.x
+ || m_Presentation.m_PresentationDimensions.y > theViewportDims.y) {
+ m_UICContext.SetScaleMode(uic::render::ScaleModes::FitSelected);
+ }
+ }
+}
+
+static void CreatePixelRect(STranslation &inTranslation, QT3DSF32 left, QT3DSF32 right, QT3DSF32 bottom,
+ QT3DSF32 top, QT3DSVec4 color)
+{
+ SPGRect *theRect = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGRect)();
+ theRect->m_Left = left;
+ theRect->m_Right = right;
+ theRect->m_Top = top;
+ theRect->m_Bottom = bottom;
+ theRect->m_FillColor = color;
+ inTranslation.m_GuideContainer.push_back(theRect);
+}
+
+static void CreatePixelVertLine(STranslation &inTranslation, QT3DSF32 inXPos, QT3DSF32 inBottom,
+ QT3DSF32 inTop, QT3DSVec4 color)
+{
+ SPGVertLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGVertLine)();
+ theLine->m_X = inXPos;
+ theLine->m_Bottom = inBottom;
+ theLine->m_Top = inTop;
+ theLine->m_LineColor = color;
+ inTranslation.m_GuideContainer.push_back(theLine);
+}
+
+static void CreatePixelHorzLine(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 inLeft,
+ QT3DSF32 inRight, QT3DSVec4 color)
+{
+ SPGHorzLine *theLine = QT3DS_NEW(inTranslation.m_GuideAllocator, SPGHorzLine)();
+ theLine->m_Y = inYPos;
+ theLine->m_Left = inLeft;
+ theLine->m_Right = inRight;
+ theLine->m_LineColor = color;
+ inTranslation.m_GuideContainer.push_back(theLine);
+}
+
+static void CreateTopBottomTickMarks(STranslation &inTranslation, QT3DSF32 posX, QT3DSF32 innerBottom,
+ QT3DSF32 innerTop, QT3DSF32 outerBottom, QT3DSF32 outerTop,
+ QT3DSF32 lineHeight, QT3DSVec4 lineColor)
+{
+ CreatePixelVertLine(inTranslation, posX, innerBottom - lineHeight, innerBottom, lineColor);
+ CreatePixelVertLine(inTranslation, posX, innerTop, innerTop + lineHeight, lineColor);
+}
+
+static void DrawTickMarksOnHorizontalRects(STranslation &inTranslation, QT3DSF32 innerLeft,
+ QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop,
+ QT3DSF32 outerBottom, QT3DSF32 outerTop, QT3DSVec4 lineColor)
+{
+ QT3DSF32 centerPosX = floor(innerLeft + (innerRight - innerLeft) / 2.0f + .5f);
+ CreateTopBottomTickMarks(inTranslation, centerPosX, innerBottom, innerTop, outerBottom,
+ outerTop, 15, lineColor);
+ for (QT3DSU32 incrementor = 10;
+ (centerPosX + incrementor) < innerRight && (centerPosX - incrementor) > innerLeft;
+ incrementor += 10) {
+ QT3DSF32 rightEdge = centerPosX + incrementor;
+ QT3DSF32 leftEdge = centerPosX - incrementor;
+ QT3DSF32 lineHeight = 0;
+ if (incrementor % 100 == 0)
+ lineHeight = 11;
+ else if (incrementor % 20)
+ lineHeight = 4;
+ else
+ lineHeight = 2;
+
+ if (rightEdge < innerRight)
+ CreateTopBottomTickMarks(inTranslation, rightEdge, innerBottom, innerTop, outerBottom,
+ outerTop, lineHeight, lineColor);
+ if (leftEdge > innerLeft)
+ CreateTopBottomTickMarks(inTranslation, leftEdge, innerBottom, innerTop, outerBottom,
+ outerTop, lineHeight, lineColor);
+ }
+}
+
+static void CreateLeftRightTickMarks(STranslation &inTranslation, QT3DSF32 inYPos, QT3DSF32 innerLeft,
+ QT3DSF32 innerRight, QT3DSF32 outerLeft, QT3DSF32 outerRight,
+ QT3DSF32 lineLength, QT3DSVec4 lineColor)
+{
+ CreatePixelHorzLine(inTranslation, inYPos, innerLeft - lineLength, innerLeft, lineColor);
+ CreatePixelHorzLine(inTranslation, inYPos, innerRight, innerRight + lineLength, lineColor);
+}
+
+static void DrawTickMarksOnVerticalRects(STranslation &inTranslation, QT3DSF32 innerLeft,
+ QT3DSF32 innerRight, QT3DSF32 innerBottom, QT3DSF32 innerTop,
+ QT3DSF32 outerLeft, QT3DSF32 outerRight, QT3DSVec4 lineColor)
+{
+ QT3DSF32 centerPosY = floor(innerBottom + (innerTop - innerBottom) / 2.0f + .5f);
+ CreateLeftRightTickMarks(inTranslation, centerPosY, innerLeft, innerRight, outerLeft,
+ outerRight, 15, lineColor);
+ for (QT3DSU32 incrementor = 10;
+ (centerPosY + incrementor) < innerTop && (centerPosY - incrementor) > innerBottom;
+ incrementor += 10) {
+ QT3DSF32 topEdge = centerPosY + incrementor;
+ QT3DSF32 bottomEdge = centerPosY - incrementor;
+ QT3DSF32 lineHeight = 0;
+ if (incrementor % 100 == 0)
+ lineHeight = 11;
+ else if (incrementor % 20)
+ lineHeight = 4;
+ else
+ lineHeight = 2;
+
+ if (topEdge < innerTop)
+ CreateLeftRightTickMarks(inTranslation, topEdge, innerLeft, innerRight, outerLeft,
+ outerRight, lineHeight, lineColor);
+ if (bottomEdge > innerBottom)
+ CreateLeftRightTickMarks(inTranslation, bottomEdge, innerLeft, innerRight, outerLeft,
+ outerRight, lineHeight, lineColor);
+ }
+}
+
+class IGuideElementFactory
+{
+protected:
+ virtual ~IGuideElementFactory() {}
+public:
+ virtual void CreateLine(QT3DSF32 inPos) = 0;
+ virtual void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) = 0;
+};
+
+static void CreateGuide(IGuideElementFactory &inFactory, QT3DSF32 inPos, QT3DSF32 inWidth)
+{
+ QT3DSF32 halfWidth = inWidth / 2.0f;
+ QT3DSF32 leftLine = floor(inPos + 1.0f - halfWidth);
+ inFactory.CreateLine(leftLine);
+ // Then we are done if not enough width
+ if (inWidth < 2.0f)
+ return;
+
+ QT3DSF32 rightLine = leftLine + inWidth - 1;
+ inFactory.CreateLine(rightLine);
+
+ if (inWidth < 3.0f)
+ return;
+ QT3DSF32 rectStart = leftLine + 1;
+ QT3DSF32 rectStop = rectStart + inWidth - 2.0f;
+ inFactory.CreateRect(rectStart, rectStop);
+}
+
+struct SHorizontalGuideFactory : public IGuideElementFactory
+{
+ STranslation &m_Translation;
+ QT3DSF32 m_Start;
+ QT3DSF32 m_Stop;
+ QT3DSVec4 m_LineColor;
+ QT3DSVec4 m_FillColor;
+ SHorizontalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor,
+ QT3DSVec4 fillColor)
+ : m_Translation(trans)
+ , m_Start(start)
+ , m_Stop(stop)
+ , m_LineColor(lineColor)
+ , m_FillColor(fillColor)
+ {
+ }
+ void CreateLine(QT3DSF32 inPos) override
+ {
+ CreatePixelHorzLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor);
+ }
+
+ void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override
+ {
+ CreatePixelRect(m_Translation, m_Start, m_Stop, inPosMin, inPosMax, m_FillColor);
+ }
+};
+
+struct SVerticalGuideFactory : public IGuideElementFactory
+{
+ STranslation &m_Translation;
+ QT3DSF32 m_Start;
+ QT3DSF32 m_Stop;
+ QT3DSVec4 m_LineColor;
+ QT3DSVec4 m_FillColor;
+ SVerticalGuideFactory(STranslation &trans, QT3DSF32 start, QT3DSF32 stop, QT3DSVec4 lineColor,
+ QT3DSVec4 fillColor)
+ : m_Translation(trans)
+ , m_Start(start)
+ , m_Stop(stop)
+ , m_LineColor(lineColor)
+ , m_FillColor(fillColor)
+ {
+ }
+ void CreateLine(QT3DSF32 inPos) override
+ {
+ CreatePixelVertLine(m_Translation, inPos, m_Start, m_Stop, m_LineColor);
+ }
+
+ void CreateRect(QT3DSF32 inPosMin, QT3DSF32 inPosMax) override
+ {
+ CreatePixelRect(m_Translation, inPosMin, inPosMax, m_Start, m_Stop, m_FillColor);
+ }
+};
+
+void STranslation::Render(int inWidgetId, bool inDrawGuides)
+{
+ // For now, we just render.
+ // Next step will be to get the bounding boxes and such setup.
+ // but we will want a custom renderer to do that.
+ if (m_Scene) {
+ // Note that begin frame is called before we allocate the bounding box and axis widgets so
+ // that we can take advantage of the renderer's per-frame-allocator.
+ m_UICContext.BeginFrame();
+ // Render the bounding boxes and extra widgets.
+ // This is called *before* the render because these sort of appendages need to be added
+ // to the layer renderables.
+ UICDM::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances();
+
+ // Don't show the bounding box or pivot for the component we are *in* the component
+ SGraphObjectTranslator *theTranslator = nullptr;
+ long theToolMode = g_StudioApp.GetToolMode();
+ bool isEditCamera = m_EditCameraEnabled;
+ int theCameraToolMode = isEditCamera ? (theToolMode & (STUDIO_CAMERATOOL_MASK)) : 0;
+ long theModifiers = CHotKeys::GetCurrentKeyModifiers();
+ bool shouldDisplayWidget = false;
+ if (theCameraToolMode == 0) {
+ switch (theToolMode) {
+ default:
+ break;
+ case STUDIO_TOOLMODE_MOVE:
+ case STUDIO_TOOLMODE_ROTATE:
+ case STUDIO_TOOLMODE_SCALE:
+ shouldDisplayWidget = true;
+ break;
+ };
+ }
+
+ SDisableUseClearColor color(*this, isEditCamera);
+ bool selectedPath = false;
+
+ for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd;
+ ++selectedIdx) {
+ UICDM::CUICDMInstanceHandle theInstance = theHandles[selectedIdx];
+ if (theInstance
+ != m_Doc.GetDocumentReader().GetComponentForSlide(m_Doc.GetActiveSlide())) {
+ if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance)
+ == L"PathAnchorPoint") {
+ theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance));
+ shouldDisplayWidget = false;
+ }
+ theTranslator = GetOrCreateTranslator(theInstance);
+ // Get the tool mode right now.
+ if (theTranslator) {
+ GraphObjectTypes::Enum theType(theTranslator->GetGraphObject().m_Type);
+ if (CStudioPreferences::IsBoundingBoxesOn()) {
+ switch (theType) {
+ case GraphObjectTypes::Node:
+ DrawGroupBoundingBoxes(*theTranslator);
+ break;
+ case GraphObjectTypes::Text:
+ case GraphObjectTypes::Model:
+ case GraphObjectTypes::Layer:
+ case GraphObjectTypes::Light:
+ case GraphObjectTypes::Path:
+ DrawNonGroupBoundingBoxes(*theTranslator);
+ break;
+ }
+ }
+ // Don't draw the axis if there is a widget.
+ if (CStudioPreferences::ShouldDisplayPivotPoint()
+ && shouldDisplayWidget == false) {
+ switch (theTranslator->GetGraphObject().m_Type) {
+ case GraphObjectTypes::Node:
+ case GraphObjectTypes::Text:
+ case GraphObjectTypes::Model:
+ DrawAxis(*theTranslator);
+ break;
+ }
+ }
+ if (theType == GraphObjectTypes::Path && selectedPath == false) {
+ selectedPath = true;
+ if (!m_PathWidget)
+ m_PathWidget = uic::widgets::IPathWidget::CreatePathWidget(
+ m_UICContext.GetAllocator(), m_UICContext);
+ m_PathWidget->SetNode(
+ static_cast<SNode &>(theTranslator->GetGraphObject()));
+ m_UICContext.GetRenderer().AddRenderWidget(*m_PathWidget);
+ }
+ }
+ }
+ }
+
+ if (theHandles.size() > 1)
+ theTranslator = nullptr;
+
+ uic::widgets::IStudioWidget *theNextWidget(nullptr);
+ if (theTranslator && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type)
+ && theTranslator->GetGraphObject().m_Type != GraphObjectTypes::Layer) {
+
+ uic::render::SNode &theNode(
+ static_cast<uic::render::SNode &>(theTranslator->GetGraphObject()));
+ SCamera *theRenderCamera = m_UICContext.GetRenderer().GetCameraForNode(theNode);
+ bool isActiveCamera = theRenderCamera == (static_cast<SCamera *>(&theNode));
+ if (shouldDisplayWidget && isActiveCamera == false) {
+ switch (theToolMode) {
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ case STUDIO_TOOLMODE_MOVE:
+ // Render translation widget
+ if (!m_TranslationWidget)
+ m_TranslationWidget = uic::widgets::IStudioWidget::CreateTranslationWidget(
+ m_UICContext.GetAllocator());
+ theNextWidget = m_TranslationWidget.mPtr;
+ break;
+ case STUDIO_TOOLMODE_ROTATE:
+ if (!m_RotationWidget)
+ m_RotationWidget = uic::widgets::IStudioWidget::CreateRotationWidget(
+ m_UICContext.GetAllocator());
+ theNextWidget = m_RotationWidget.mPtr;
+ break;
+
+ case STUDIO_TOOLMODE_SCALE:
+ if (!m_ScaleWidget)
+ m_ScaleWidget = uic::widgets::IStudioWidget::CreateScaleWidget(
+ m_UICContext.GetAllocator());
+ theNextWidget = m_ScaleWidget.mPtr;
+ break;
+ }
+ if (theNextWidget) {
+ theNextWidget->SetNode(static_cast<SNode &>(theTranslator->GetGraphObject()));
+ m_UICContext.GetRenderer().AddRenderWidget(*theNextWidget);
+ }
+ }
+ }
+ if (m_LastRenderedWidget.mPtr && m_LastRenderedWidget.mPtr != theNextWidget)
+ ResetWidgets();
+
+ m_LastRenderedWidget = theNextWidget;
+ if (m_LastRenderedWidget) {
+ m_LastRenderedWidget->SetSubComponentId(inWidgetId);
+ switch (g_StudioApp.GetMinpulationMode()) {
+ case StudioManipulationModes::Local:
+ m_LastRenderedWidget->SetRenderWidgetMode(uic::render::RenderWidgetModes::Local);
+ break;
+ case StudioManipulationModes::Global:
+ m_LastRenderedWidget->SetRenderWidgetMode(uic::render::RenderWidgetModes::Global);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ m_Scene->PrepareForRender(GetViewportDimensions(), m_UICContext);
+
+ m_UICContext.RunRenderTasks();
+
+ m_Scene->Render(GetViewportDimensions(), m_UICContext, SScene::DoNotClear);
+
+ if (inDrawGuides && m_EditCameraEnabled == false && g_StudioApp.IsAuthorZoom() == false) {
+ m_GuideContainer.clear();
+ // Figure out the matte area.
+ NVRenderRect theContextViewport = m_UICContext.GetContextViewport();
+ NVRenderRect thePresentationViewport = m_UICContext.GetPresentationViewport();
+ m_UICContext.GetRenderContext().SetViewport(theContextViewport);
+ QT3DSI32 innerLeft = thePresentationViewport.m_X;
+ QT3DSI32 innerRight = thePresentationViewport.m_X + thePresentationViewport.m_Width;
+ QT3DSI32 innerBottom = thePresentationViewport.m_Y;
+ QT3DSI32 innerTop = thePresentationViewport.m_Y + thePresentationViewport.m_Height;
+
+ QT3DSI32 outerLeft = innerLeft - 16;
+ QT3DSI32 outerRight = innerRight + 16;
+ QT3DSI32 outerBottom = innerBottom - 16;
+ QT3DSI32 outerTop = innerTop + 16;
+ // Retain the rects for picking purposes.
+ m_InnerRect = SRulerRect(innerLeft, innerTop, innerRight, innerBottom);
+ m_OuterRect = SRulerRect(outerLeft, outerTop, outerRight, outerBottom);
+
+ // Draw tick marks around the presentation
+ CreatePixelRect(*this, (QT3DSF32)outerLeft, (QT3DSF32)innerLeft, (QT3DSF32)innerBottom,
+ (QT3DSF32)innerTop, m_rectColor);
+ CreatePixelRect(*this, (QT3DSF32)innerRight, (QT3DSF32)outerRight,
+ (QT3DSF32)innerBottom, (QT3DSF32)innerTop, m_rectColor);
+ CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)outerBottom,
+ (QT3DSF32)innerBottom, m_rectColor);
+ CreatePixelRect(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight, (QT3DSF32)innerTop,
+ (QT3DSF32)outerTop, m_rectColor);
+ DrawTickMarksOnHorizontalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight,
+ (QT3DSF32)innerBottom, (QT3DSF32)innerTop,
+ (QT3DSF32)outerBottom, (QT3DSF32)outerTop, m_lineColor);
+ DrawTickMarksOnVerticalRects(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight,
+ (QT3DSF32)innerBottom, (QT3DSF32)innerTop,
+ (QT3DSF32)outerLeft, (QT3DSF32)outerRight, m_lineColor);
+ UICDM::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides();
+ UICDM::CUICDMGuideHandle theSelectedGuide;
+ Q3DStudio::SSelectedValue theSelection = m_Doc.GetSelectedValue();
+ if (theSelection.getType() == Q3DStudio::SelectedValueTypes::Guide)
+ theSelectedGuide = theSelection.getData<UICDM::CUICDMGuideHandle>();
+
+ // Draw guides
+ for (size_t guideIdx = 0, guideEnd = theGuides.size(); guideIdx < guideEnd;
+ ++guideIdx) {
+ UICDM::SGuideInfo theInfo =
+ m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]);
+ bool isGuideSelected = theGuides[guideIdx] == theSelectedGuide;
+ QT3DSVec4 theColor = isGuideSelected ? m_selectedGuideColor : m_guideColor;
+ QT3DSVec4 theFillColor = isGuideSelected ? m_selectedGuideFillColor
+ : m_guideFillColor;
+ switch (theInfo.m_Direction) {
+ case UICDM::GuideDirections::Horizontal: {
+ SHorizontalGuideFactory theFactory(*this, (QT3DSF32)innerLeft, (QT3DSF32)innerRight,
+ theColor, theFillColor);
+ CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Bottom + theInfo.m_Position,
+ (QT3DSF32)theInfo.m_Width);
+ } break;
+ case UICDM::GuideDirections::Vertical: {
+ SVerticalGuideFactory theFactory(*this, (QT3DSF32)innerBottom, (QT3DSF32)innerTop,
+ theColor, theFillColor);
+ CreateGuide(theFactory, (QT3DSF32)m_InnerRect.m_Left + theInfo.m_Position,
+ (QT3DSF32)theInfo.m_Width);
+ } break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ m_UICContext.GetPixelGraphicsRenderer().Render(
+ qt3ds::foundation::toDataRef(m_GuideContainer.data(), m_GuideContainer.size()));
+
+ m_GuideContainer.clear();
+ m_GuideAllocator.reset();
+ }
+
+ m_UICContext.EndFrame();
+
+ if (m_ZoomRender.hasValue()) {
+ RenderZoomRender(*m_ZoomRender);
+ m_ZoomRender = Empty();
+ }
+
+ // Render the pick buffer, useful for debugging why a widget wasn't hit.
+ /*
+ if ( m_PickBuffer )
+ {
+ qt3ds::render::NVRenderContext& theRenderContext( m_UICContext.GetRenderContext() );
+ qt3ds::render::STextureDetails thePickDetails = m_PickBuffer->GetTextureDetails();
+ theRenderContext.SetViewport( qt3ds::render::NVRenderRect( 0, 0, thePickDetails.m_Width,
+ thePickDetails.m_Height ) );
+ uic::render::SCamera theCamera;
+ theCamera.MarkDirty( uic::render::NodeTransformDirtyFlag::TransformIsDirty );
+ theCamera.m_Flags.SetOrthographic( true );
+ QT3DSVec2 theDimensions( (QT3DSF32)thePickDetails.m_Width, (QT3DSF32)thePickDetails.m_Height );
+ theCamera.CalculateGlobalVariables( render::NVRenderRectF( 0, 0, theDimensions.x,
+ theDimensions.y ), theDimensions );
+ QT3DSMat44 theVP;
+ theCamera.CalculateViewProjectionMatrix( theVP );
+ theRenderContext.SetCullingEnabled( false );
+ theRenderContext.SetBlendingEnabled( false );
+ theRenderContext.SetDepthTestEnabled( false );
+ theRenderContext.SetDepthWriteEnabled( false );
+ m_UICContext.GetRenderer().RenderQuad( theDimensions, theVP, *m_PickBuffer );
+ }*/
+ }
+}
+
+void STranslation::ResetWidgets()
+{
+ if (m_ScaleWidget)
+ m_ScaleWidget->SetAxisScale(QT3DSVec3(1, 1, 1));
+ if (m_RotationWidget)
+ m_RotationWidget->ClearRotationEdges();
+ m_CumulativeRotation = 0.0f;
+}
+
+void STranslation::DoPrepareForDrag(SNode *inSelectedNode)
+{
+ if (inSelectedNode == nullptr)
+ return;
+
+ m_MouseDownNode = *inSelectedNode;
+ m_MouseDownParentGlobalTransformInverse = Empty();
+ m_MouseDownParentRotationInverse = Empty();
+ m_MouseDownGlobalRotation = Empty();
+ // Orphan this node manually since it is a straight copy
+ m_MouseDownNode.m_Parent = nullptr;
+ m_MouseDownNode.m_FirstChild = nullptr;
+ m_MouseDownNode.m_NextSibling = nullptr;
+ SCamera *theCamera = m_UICContext.GetRenderer().GetCameraForNode(*inSelectedNode);
+ m_CumulativeRotation = 0.0f;
+ if (theCamera == nullptr)
+ return;
+ m_MouseDownCamera = *theCamera;
+ m_LastPathDragValue = Empty();
+}
+
+void STranslation::EndDrag()
+{
+ ResetWidgets();
+}
+
+bool STranslation::IsPathWidgetActive()
+{
+ UICDM::TInstanceHandleList theHandles = m_Doc.GetSelectedValue().GetSelectedInstances();
+ for (size_t selectedIdx = 0, selectedEnd = theHandles.size(); selectedIdx < selectedEnd;
+ ++selectedIdx) {
+ UICDM::CUICDMInstanceHandle theInstance(theHandles[selectedIdx]);
+ if (m_Doc.GetDocumentReader().GetObjectTypeName(theInstance) == L"PathAnchorPoint")
+ theInstance = m_AssetGraph.GetParent(m_AssetGraph.GetParent(theInstance));
+ SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theInstance);
+ if (theTranslator && theTranslator->GetGraphObject().m_Type == GraphObjectTypes::Path)
+ return true;
+ }
+ return false;
+}
+
+inline uic::render::SLayer *GetLayerForNode(const uic::render::SNode &inNode)
+{
+ SNode *theNode;
+ // empty loop intentional
+ for (theNode = const_cast<SNode *>(&inNode);
+ theNode && theNode->m_Type != GraphObjectTypes::Layer; theNode = theNode->m_Parent) {
+ }
+ if (theNode && theNode->m_Type == GraphObjectTypes::Layer)
+ return static_cast<SLayer *>(theNode);
+ return nullptr;
+}
+
+void STranslation::RenderZoomRender(SZoomRender &inRender)
+{
+ SLayer *theLayer(inRender.m_Layer);
+ CPt thePoint(inRender.m_Point);
+ if (theLayer) {
+ uic::render::IUICRenderer &theRenderer(m_UICContext.GetRenderer());
+ Option<uic::render::SLayerPickSetup> thePickSetup(
+ theRenderer.GetLayerPickSetup(*theLayer, QT3DSVec2((QT3DSF32)thePoint.x, (QT3DSF32)thePoint.y),
+ uic::render::SWindowDimensions(16, 16)));
+ if (thePickSetup.hasValue()) {
+ qt3ds::render::NVRenderContext &theRenderContext(m_UICContext.GetRenderContext());
+ theRenderContext.SetViewport(qt3ds::render::NVRenderRect(0, 0, 100, 100));
+ theRenderContext.SetScissorRect(qt3ds::render::NVRenderRect(0, 0, 100, 100));
+ theRenderContext.SetDepthWriteEnabled(true);
+ theRenderContext.SetScissorTestEnabled(true);
+ theRenderContext.SetClearColor(QT3DSVec4(.2, .2, .2, 0));
+ theRenderContext.Clear(qt3ds::render::NVRenderClearFlags(
+ qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth));
+ theRenderer.RunLayerRender(*theLayer, thePickSetup->m_ViewProjection);
+ theRenderContext.SetScissorTestEnabled(false);
+ }
+ }
+}
+
+void STranslation::DrawBoundingBox(SNode &inNode, QT3DSVec3 inColor)
+{
+ qt3ds::NVBounds3 theBounds = inNode.GetBounds(m_UICContext.GetBufferManager(),
+ m_UICContext.GetPathManager(), true, this);
+ uic::render::IRenderWidget &theBBoxWidget = uic::render::IRenderWidget::CreateBoundingBoxWidget(
+ inNode, theBounds, inColor, m_UICContext.GetRenderer().GetPerFrameAllocator());
+ m_UICContext.GetRenderer().AddRenderWidget(theBBoxWidget);
+}
+
+void STranslation::DrawLightBoundingBox(SNode &inNode, QT3DSVec3 inColor)
+{
+ SLight *theLight = reinterpret_cast<SLight *>(&inNode);
+ if (theLight->m_LightType != uic::render::RenderLightTypes::Area) {
+ return;
+ }
+
+ qt3ds::NVBounds3 theBounds(
+ qt3ds::QT3DSVec3(-theLight->m_AreaWidth * 0.5f, -theLight->m_AreaHeight * 0.5f, 0.0f),
+ qt3ds::QT3DSVec3(theLight->m_AreaWidth * 0.5f, theLight->m_AreaHeight * 0.5f, 0.0f));
+ uic::render::IRenderWidget &theBBoxWidget = uic::render::IRenderWidget::CreateBoundingBoxWidget(
+ inNode, theBounds, inColor, m_UICContext.GetRenderer().GetPerFrameAllocator());
+ m_UICContext.GetRenderer().AddRenderWidget(theBBoxWidget);
+}
+
+void STranslation::DrawAxis(SGraphObjectTranslator &inTranslator)
+{
+ if (GraphObjectTypes::IsNodeType(inTranslator.GetGraphObject().m_Type)) {
+ uic::render::IRenderWidget &theAxisWidget = uic::render::IRenderWidget::CreateAxisWidget(
+ static_cast<SNode &>(inTranslator.GetGraphObject()),
+ m_UICContext.GetRenderer().GetPerFrameAllocator());
+ m_UICContext.GetRenderer().AddRenderWidget(theAxisWidget);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+}
+
+Option<QT3DSU32> STranslation::PickWidget(CPt inMouseCoords, TranslationSelectMode::Enum,
+ uic::widgets::IStudioWidgetBase &inWidget)
+{
+ SNode &theNode = inWidget.GetNode();
+ SGraphObjectTranslator *theWidgetTranslator =
+ theNode.m_UserData.DynamicCast<SGraphObjectTranslator>();
+ SLayer *theLayer = GetLayerForNode(theNode);
+ if (theLayer && theWidgetTranslator) {
+ Option<uic::render::SLayerPickSetup> thePickSetup(
+ m_UICContext.GetRenderer().GetLayerPickSetup(
+ *theLayer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y),
+ uic::render::SWindowDimensions(4, 4)));
+ if (thePickSetup.hasValue()) {
+ qt3ds::render::NVRenderContext &theContext(m_UICContext.GetRenderContext());
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderFrameBuffer *>
+ __currentrt(theContext, &qt3ds::render::NVRenderContext::GetRenderTarget,
+ &qt3ds::render::NVRenderContext::SetRenderTarget);
+ qt3ds::render::NVRenderFrameBuffer *theFBO =
+ m_UICContext.GetResourceManager().AllocateFrameBuffer();
+ const QT3DSU32 fboDims = 8;
+ if (!m_PickBuffer) {
+ m_PickBuffer = theContext.CreateTexture2D();
+ m_PickBuffer->SetTextureData(qt3ds::foundation::NVDataRef<qt3ds::QT3DSU8>(), 0, fboDims,
+ fboDims,
+ qt3ds::render::NVRenderTextureFormats::LuminanceAlpha8);
+ }
+ qt3ds::render::NVRenderRenderBuffer *theRenderBuffer =
+ m_UICContext.GetResourceManager().AllocateRenderBuffer(
+ fboDims, fboDims, qt3ds::render::NVRenderRenderBufferFormats::Depth16);
+ theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Color0,
+ qt3ds::render::NVRenderTextureOrRenderBuffer(*m_PickBuffer));
+ theFBO->Attach(qt3ds::render::NVRenderFrameBufferAttachments::Depth,
+ qt3ds::render::NVRenderTextureOrRenderBuffer(*theRenderBuffer));
+ qt3ds::render::NVRenderRect theViewport(0, 0, fboDims, fboDims);
+ theContext.SetViewport(theViewport);
+ theContext.SetDepthWriteEnabled(true);
+ theContext.SetDepthTestEnabled(true);
+ theContext.SetScissorTestEnabled(false);
+ theContext.SetBlendingEnabled(false);
+ theContext.SetClearColor(QT3DSVec4(0, 0, 0, 0));
+ theContext.Clear(qt3ds::render::NVRenderClearFlags(
+ qt3ds::render::NVRenderClearValues::Color | qt3ds::render::NVRenderClearValues::Depth));
+ inWidget.RenderPick(thePickSetup->m_ProjectionPreMultiply, theContext,
+ uic::render::SWindowDimensions(4, 4));
+ // Now read the pixels back.
+ m_PixelBuffer.resize(fboDims * fboDims * 3);
+ theContext.ReadPixels(theViewport, qt3ds::render::NVRenderReadPixelFormats::RGB8,
+ m_PixelBuffer);
+ m_UICContext.GetResourceManager().Release(*theFBO);
+ m_UICContext.GetResourceManager().Release(*theRenderBuffer);
+ eastl::hash_map<QT3DSU32, QT3DSU32> tallies;
+ QT3DSU32 numPixels = fboDims * fboDims;
+ for (QT3DSU32 idx = 0; idx < numPixels; ++idx) {
+ qt3ds::QT3DSU16 theChannelAmount =
+ m_PixelBuffer[idx * 3] + (m_PixelBuffer[idx * 3 + 1] << 8);
+ if (theChannelAmount)
+ tallies.insert(eastl::make_pair(theChannelAmount, (QT3DSU32)0)).first->second += 1;
+ }
+ QT3DSU32 tallyMaxTally = 0;
+ QT3DSU32 tallyMaxIdx = 0;
+ for (eastl::hash_map<QT3DSU32, QT3DSU32>::iterator iter = tallies.begin(),
+ end = tallies.end();
+ iter != end; ++iter) {
+ if (iter->second > tallyMaxTally) {
+ tallyMaxTally = iter->second;
+ tallyMaxIdx = iter->first;
+ }
+ }
+ if (tallyMaxIdx > 0) {
+ return tallyMaxIdx;
+ }
+ }
+ }
+ return Empty();
+}
+
+SStudioPickValue STranslation::Pick(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode)
+{
+ bool requestRender = false;
+
+ if (m_Doc.GetDocumentReader().AreGuidesEditable()) {
+ UICDM::TGuideHandleList theGuides = m_Doc.GetDocumentReader().GetGuides();
+ CPt renderSpacePt(inMouseCoords.x - (long)m_InnerRect.m_Left,
+ (long)GetViewportDimensions().y - inMouseCoords.y
+ - (long)m_InnerRect.m_Bottom);
+ for (size_t guideIdx = 0, guideEnd = theGuides.size(); guideIdx < guideEnd; ++guideIdx) {
+ UICDM::SGuideInfo theGuideInfo =
+ m_Doc.GetDocumentReader().GetGuideInfo(theGuides[guideIdx]);
+ float width = (theGuideInfo.m_Width / 2.0f) + 2.0f;
+ switch (theGuideInfo.m_Direction) {
+ case UICDM::GuideDirections::Horizontal:
+ if (fabs((float)renderSpacePt.y - theGuideInfo.m_Position) <= width)
+ return theGuides[guideIdx];
+ break;
+ case UICDM::GuideDirections::Vertical:
+ if (fabs((float)renderSpacePt.x - theGuideInfo.m_Position) <= width)
+ return theGuides[guideIdx];
+ break;
+ }
+ }
+ }
+ if (IsPathWidgetActive()) {
+ Option<QT3DSU32> picked = PickWidget(inMouseCoords, inSelectMode, *m_PathWidget);
+ if (picked.hasValue()) {
+ RequestRender();
+ DoPrepareForDrag(&m_PathWidget->GetNode());
+ return m_PathWidget->PickIndexToPickValue(*picked);
+ }
+ }
+ // Pick against the widget first if possible.
+ if (m_LastRenderedWidget) {
+ Option<QT3DSU32> picked = PickWidget(inMouseCoords, inSelectMode, *m_LastRenderedWidget);
+ if (picked.hasValue()) {
+ RequestRender();
+ DoPrepareForDrag(&m_LastRenderedWidget->GetNode());
+ return m_LastRenderedWidget->PickIndexToPickValue(*picked);
+ }
+ }
+ if (m_Scene && m_Scene->m_FirstChild) {
+ uic::render::SUICRenderPickResult thePickResult =
+ m_UICContext.GetRenderer().Pick(*m_Scene->m_FirstChild, GetViewportDimensions(),
+ QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y));
+ if (thePickResult.m_HitObject) {
+ const SGraphObject &theObject = *thePickResult.m_HitObject;
+ if (theObject.m_Type == GraphObjectTypes::Model
+ || theObject.m_Type == GraphObjectTypes::Text
+ || theObject.m_Type == GraphObjectTypes::Path) {
+ const SNode &theTranslatorModel(static_cast<const SNode &>(theObject));
+ SGraphObjectTranslator *theTranslator =
+ theTranslatorModel.m_UserData.DynamicCast<SGraphObjectTranslator>();
+ const SNode *theModelPtr = &theTranslatorModel;
+ if (theTranslator->GetPossiblyAliasedInstanceHandle()
+ != theTranslator->GetInstanceHandle()) {
+ theTranslator =
+ GetOrCreateTranslator(theTranslator->GetPossiblyAliasedInstanceHandle());
+ theModelPtr =
+ static_cast<const SNode *>(&theTranslator->GetNonAliasedGraphObject());
+ }
+ CUICDMInstanceHandle theActiveComponent =
+ m_Reader.GetComponentForSlide(m_Doc.GetActiveSlide());
+ if (inSelectMode == TranslationSelectMode::Group) {
+ // Bounce up the hierarchy till one of two conditions are met
+ // the parent is a layer or the our component is the active component
+ // but the parent's is not.
+ while (theTranslator && GraphObjectTypes::IsNodeType(
+ theTranslator->GetGraphObject().m_Type)) {
+ SNode *myNode = static_cast<SNode *>(&theTranslator->GetGraphObject());
+ if (myNode->m_Parent == nullptr) {
+ theTranslator = nullptr;
+ break;
+ }
+ SNode *parentNode = myNode->m_Parent;
+ SGraphObjectTranslator *theParentTranslator =
+ parentNode->m_UserData.DynamicCast<SGraphObjectTranslator>();
+ CUICDMInstanceHandle myComponent =
+ m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle());
+ CUICDMInstanceHandle myParentComponent = m_Reader.GetAssociatedComponent(
+ theParentTranslator->GetInstanceHandle());
+ if (parentNode->m_Type == GraphObjectTypes::Layer) {
+ if (myParentComponent != theActiveComponent)
+ theTranslator = nullptr;
+ break;
+ }
+ if (myComponent == theActiveComponent
+ && myParentComponent != theActiveComponent)
+ break;
+ theTranslator = theParentTranslator;
+ }
+ } else {
+ // Bounce up until we get into the active component and then stop.
+ while (inSelectMode == TranslationSelectMode::Single && theTranslator
+ && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type)
+ && m_Reader.GetAssociatedComponent(theTranslator->GetInstanceHandle())
+ != theActiveComponent) {
+ SNode *theNode = static_cast<SNode *>(&theTranslator->GetGraphObject());
+ theNode = theNode->m_Parent;
+ if (theNode && theNode->m_Type != GraphObjectTypes::Layer)
+ theTranslator =
+ theNode->m_UserData.DynamicCast<SGraphObjectTranslator>();
+ else
+ theTranslator = nullptr;
+ }
+ }
+
+ if (theTranslator) {
+ QT3DS_ASSERT(GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type));
+ DoPrepareForDrag(static_cast<SNode *>(&theTranslator->GetGraphObject()));
+ return theTranslator->GetInstanceHandle();
+ }
+ }
+ }
+ if (requestRender)
+ RequestRender();
+ }
+ return SStudioPickValue();
+}
+
+qt3ds::foundation::Option<UICDM::SGuideInfo> STranslation::PickRulers(CPt inMouseCoords)
+{
+ CPt renderSpacePt(inMouseCoords.x, (long)GetViewportDimensions().y - inMouseCoords.y);
+ // If mouse is inside outer rect but outside inner rect.
+ if (m_OuterRect.Contains(renderSpacePt.x, renderSpacePt.y)
+ && !m_InnerRect.Contains(renderSpacePt.x, renderSpacePt.y)) {
+ std::shared_ptr<UICDM::IGuideSystem> theGuideSystem =
+ m_StudioSystem.GetFullSystem()->GetCoreSystem()->GetGuideSystem();
+ if (renderSpacePt.x >= m_InnerRect.m_Left && renderSpacePt.x <= m_InnerRect.m_Right) {
+ return UICDM::SGuideInfo((QT3DSF32)renderSpacePt.y - (QT3DSF32)m_InnerRect.m_Bottom,
+ UICDM::GuideDirections::Horizontal);
+ } else if (renderSpacePt.y >= m_InnerRect.m_Bottom
+ && renderSpacePt.y <= m_InnerRect.m_Top) {
+ return UICDM::SGuideInfo((QT3DSF32)renderSpacePt.x - (QT3DSF32)m_InnerRect.m_Left,
+ UICDM::GuideDirections::Vertical);
+ }
+ }
+ return qt3ds::foundation::Option<UICDM::SGuideInfo>();
+}
+
+QT3DSVec3 STranslation::GetIntendedPosition(UICDM::CUICDMInstanceHandle inInstance, CPt inPos)
+{
+ ClearDirtySet();
+ SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(inInstance);
+ if (theTranslator == nullptr)
+ return QT3DSVec3(0, 0, 0);
+ if (GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type) == false)
+ return QT3DSVec3(0, 0, 0);
+ SNode *theNode = static_cast<SNode *>(&theTranslator->GetGraphObject());
+ SCamera *theCamera = m_UICContext.GetRenderer().GetCameraForNode(*theNode);
+ {
+ // Get the node's parent
+ CUICDMInstanceHandle theParent = m_AssetGraph.GetParent(inInstance);
+ SGraphObjectTranslator *theParentTranslator = GetOrCreateTranslator(theParent);
+ if (theParentTranslator
+ && GraphObjectTypes::IsNodeType(theParentTranslator->GetGraphObject().m_Type))
+ theCamera = m_UICContext.GetRenderer().GetCameraForNode(
+ *static_cast<SNode *>(&theParentTranslator->GetGraphObject()));
+ }
+ if (theCamera == nullptr)
+ return QT3DSVec3(0, 0, 0);
+
+ QT3DSVec3 theGlobalPos(theNode->GetGlobalPos());
+ return m_UICContext.GetRenderer().UnprojectToPosition(*theCamera, theGlobalPos,
+ QT3DSVec2((QT3DSF32)inPos.x, (QT3DSF32)inPos.y));
+}
+
+static void CheckLockToAxis(QT3DSF32 &inXDistance, QT3DSF32 &inYDistance, bool inLockToAxis)
+{
+ if (inLockToAxis) {
+ if (fabs(inXDistance) > fabs(inYDistance))
+ inYDistance = 0;
+ else
+ inXDistance = 0;
+ }
+}
+
+void STranslation::ApplyPositionalChange(QT3DSVec3 inDiff, SNode &inNode,
+ CUpdateableDocumentEditor &inEditor)
+{
+ if (m_MouseDownParentGlobalTransformInverse.isEmpty()) {
+ if (inNode.m_Parent)
+ m_MouseDownParentGlobalTransformInverse =
+ inNode.m_Parent->m_GlobalTransform.getInverse();
+ else
+ m_MouseDownParentGlobalTransformInverse = QT3DSMat44::createIdentity();
+ }
+ QT3DSMat44 theGlobalTransform = m_MouseDownNode.m_GlobalTransform;
+ QT3DSMat44 theNewLocalTransform =
+ m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform;
+ QT3DSVec3 theOldPos = theNewLocalTransform.column3.getXYZ();
+ theOldPos.z *= -1;
+
+ theGlobalTransform.column3 += QT3DSVec4(inDiff, 0.0f);
+ theNewLocalTransform = m_MouseDownParentGlobalTransformInverse.getValue() * theGlobalTransform;
+ QT3DSVec3 thePos = theNewLocalTransform.column3.getXYZ();
+ thePos.z *= -1;
+
+ QT3DSVec3 theDiff = thePos - theOldPos;
+
+ SetPosition(m_MouseDownNode.m_Position + theDiff, inEditor);
+}
+
+void STranslation::TranslateSelectedInstanceAlongCameraDirection(
+ CPt inOriginalCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+ SCamera *theCamera = m_UICContext.GetRenderer().GetCameraForNode(*theNode);
+ if (theCamera == nullptr)
+ return;
+ QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y);
+ if (fabs(theYDistance) == 0)
+ return;
+
+ QT3DSF32 theMouseMultiplier = 1.0f / 2.0f;
+ QT3DSF32 theDistanceMultiplier = 1.0f + theYDistance * theMouseMultiplier;
+ QT3DSVec3 theCameraDir = m_MouseDownCamera.GetDirection();
+
+ QT3DSVec3 theDiff = theCameraDir * theDistanceMultiplier;
+ ApplyPositionalChange(theDiff, *theNode, inEditor);
+}
+
+void STranslation::TranslateSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor, bool inLockToAxis)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+ uic::render::IUICRenderer &theRenderer(m_UICContext.GetRenderer());
+
+ QT3DSF32 theXDistance = QT3DSF32(inMouseCoords.x - inOriginalCoords.x);
+ QT3DSF32 theYDistance = QT3DSF32(inMouseCoords.y - inOriginalCoords.y);
+ if (fabs(theXDistance) == 0 && fabs(theYDistance) == 0)
+ return;
+
+ CheckLockToAxis(theXDistance, theYDistance, inLockToAxis);
+
+ inMouseCoords.x = inOriginalCoords.x + (long)theXDistance;
+ inMouseCoords.y = inOriginalCoords.y + (long)theYDistance;
+ QT3DSVec3 theNodeGlobal = m_MouseDownNode.GetGlobalPos();
+ QT3DSVec3 theOriginalPos = theRenderer.UnprojectToPosition(
+ *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y));
+ QT3DSVec3 theNewPos = theRenderer.UnprojectToPosition(
+ *theNode, theNodeGlobal, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y));
+
+ QT3DSVec3 theDiff = theNewPos - theOriginalPos;
+ ApplyPositionalChange(theDiff, *theNode, inEditor);
+}
+
+void STranslation::ScaleSelectedInstanceZ(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+
+ // Scale scales uniformly and responds to mouse Y only.
+ QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y;
+ if (fabs(theYDistance) == 0)
+ return;
+
+ QT3DSF32 theMouseMultiplier = 1.0f / 40.0f;
+ QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier;
+
+ SetScale(QT3DSVec3(m_MouseDownNode.m_Scale.x, m_MouseDownNode.m_Scale.y,
+ m_MouseDownNode.m_Scale.z * theScaleMultiplier),
+ inEditor);
+}
+
+void STranslation::ScaleSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+
+ // Scale scales uniformly and responds to mouse Y only.
+ QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y;
+ if (fabs(theYDistance) == 0)
+ return;
+
+ QT3DSF32 theMouseMultiplier = 1.0f / 40.0f;
+ QT3DSF32 theScaleMultiplier = 1.0f + theYDistance * theMouseMultiplier;
+
+ SetScale(m_MouseDownNode.m_Scale * theScaleMultiplier, inEditor);
+}
+
+void STranslation::CalculateNodeGlobalRotation(SNode &inNode)
+{
+ if (inNode.m_Parent)
+ CalculateNodeGlobalRotation(*inNode.m_Parent);
+ if (m_MouseDownParentRotationInverse.isEmpty()) {
+ m_MouseDownParentRotationInverse = QT3DSMat33::createIdentity();
+ m_MouseDownGlobalRotation = QT3DSMat33::createIdentity();
+ }
+
+ QT3DSMat44 localRotation;
+ inNode.CalculateRotationMatrix(localRotation);
+ if (inNode.m_Flags.IsLeftHanded())
+ SNode::FlipCoordinateSystem(localRotation);
+ QT3DSMat33 theRotation;
+ SNode::GetMatrixUpper3x3(theRotation, localRotation);
+
+ m_MouseDownParentRotationInverse = m_MouseDownGlobalRotation;
+ m_MouseDownGlobalRotation = m_MouseDownGlobalRotation.getValue() * theRotation;
+}
+
+void STranslation::ApplyRotationToSelectedInstance(const QT3DSQuat &inFinalRotation, SNode &inNode,
+ CUpdateableDocumentEditor &inEditor,
+ bool inIsMouseRelative)
+{
+ if (m_MouseDownParentRotationInverse.isEmpty()) {
+ CalculateNodeGlobalRotation(inNode);
+ m_MouseDownParentRotationInverse = m_MouseDownParentRotationInverse->getInverse();
+ }
+ QT3DSMat33 theRotationMatrix(inFinalRotation);
+
+ QT3DSMat33 theFinalGlobal = theRotationMatrix * m_MouseDownGlobalRotation.getValue();
+ QT3DSMat33 theLocalGlobal = m_MouseDownParentRotationInverse.getValue() * theFinalGlobal;
+ QT3DSVec3 theRotations = inNode.GetRotationVectorFromRotationMatrix(theLocalGlobal);
+ theRotations = uic::render::SRotationHelper::ToNearestAngle(inNode.m_Rotation, theRotations,
+ inNode.m_RotationOrder);
+ SetRotation(theRotations, inEditor);
+ // Trackball rotation is relative to the previous mouse position.
+ // Rotation manipulator rotation is relative to only the original mouse down position
+ // so inIsMouseRelative is false for rotations that are relative to the original mouse down
+ // position.
+ if (inIsMouseRelative)
+ m_MouseDownGlobalRotation = theFinalGlobal;
+}
+
+void STranslation::RotateSelectedInstanceAboutCameraDirectionVector(
+ CPt inPreviousMouseCoords, CPt inMouseCoords, CUpdateableDocumentEditor &inEditor)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+ SCamera *theCamera = m_UICContext.GetRenderer().GetCameraForNode(*theNode);
+ if (theCamera == nullptr)
+ return;
+
+ QT3DSVec3 theDirection = m_MouseDownCamera.GetDirection();
+ QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousMouseCoords.y;
+ QT3DSQuat theYRotation(-1.0f * theYDistance * g_RotationScaleFactor, theDirection);
+
+ ApplyRotationToSelectedInstance(theYRotation, *theNode, inEditor);
+}
+
+// This method never feels right to me. It is difficult to apply it to a single axis (of course for
+// that
+// you can use the inspector palette).
+void STranslation::RotateSelectedInstance(CPt inOriginalCoords, CPt inPreviousCoords,
+ CPt inMouseCoords, CUpdateableDocumentEditor &inEditor,
+ bool inLockToAxis)
+{
+ SNode *theNode = GetSelectedNode();
+ if (theNode == nullptr)
+ return;
+ SCamera *theCamera = m_UICContext.GetRenderer().GetCameraForNode(*theNode);
+ if (theCamera == nullptr)
+ return;
+ // We want to do a similar translation to what we did below but we need to calculate the
+ // parent's
+ // global rotation without scale included.
+
+ QT3DSF32 theXDistance = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inPreviousCoords.x;
+ QT3DSF32 theYDistance = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inPreviousCoords.y;
+ bool xIsZero = fabs(theXDistance) < .001f;
+ bool yIsZero = fabs(theYDistance) < .001f;
+ if (xIsZero && yIsZero)
+ return;
+
+ if (inLockToAxis) {
+ QT3DSF32 originalDistX = (QT3DSF32)inMouseCoords.x - (QT3DSF32)inOriginalCoords.x;
+ QT3DSF32 originalDistY = (QT3DSF32)inMouseCoords.y - (QT3DSF32)inOriginalCoords.y;
+ if (inLockToAxis) {
+ if (fabs(originalDistX) > fabs(originalDistY))
+ theYDistance = 0;
+ else
+ theXDistance = 0;
+ }
+ }
+
+ QT3DSVec3 theXAxis = m_MouseDownCamera.m_GlobalTransform.column0.getXYZ();
+ QT3DSVec3 theYAxis = m_MouseDownCamera.m_GlobalTransform.column1.getXYZ();
+
+ QT3DSVec3 theFinalAxis = theXDistance * theYAxis + theYDistance * theXAxis;
+ QT3DSF32 theTotalDistance = theFinalAxis.normalize();
+ QT3DSQuat theRotation(theTotalDistance * g_RotationScaleFactor / 2.0f, theFinalAxis);
+
+ ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor);
+}
+
+inline void NiceAdd(QT3DSF32 &ioValue, QT3DSF32 inIncrement)
+{
+ QT3DSF32 temp = ioValue + inIncrement;
+ // Round to nearest .5
+ QT3DSF32 sign = temp >= 0 ? 1.0f : -1.0f;
+ QT3DSU32 largerValue = (QT3DSU32)(fabs(temp * 10.0f));
+
+ QT3DSU32 leftover = largerValue % 10;
+ // Round down to zero
+ largerValue -= leftover;
+ if (leftover < 2)
+ leftover = 0;
+ else if (leftover > 7) {
+ leftover = 0;
+ largerValue += 10;
+ } else
+ leftover = 5;
+ largerValue += leftover;
+
+ ioValue = sign * (QT3DSF32)largerValue / 10.0f;
+}
+
+static inline QT3DSVec3 GetAxis(QT3DSU32 inIndex, QT3DSMat33 &inMatrix)
+{
+ QT3DSVec3 retval(0, 0, 0);
+ switch (inIndex) {
+ case 0:
+ retval = inMatrix.column0;
+ break;
+ case 1:
+ retval = inMatrix.column1;
+ break;
+ case 2:
+ retval = inMatrix.column2;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ retval.normalize();
+ return retval;
+}
+
+inline Option<QT3DSF32> GetScaleAlongAxis(const QT3DSVec3 &inAxis, const QT3DSVec3 &inObjToOriginal,
+ const QT3DSVec3 &inObjToCurrent)
+{
+ QT3DSF32 lhs = inAxis.dot(inObjToCurrent);
+ QT3DSF32 rhs = inAxis.dot(inObjToOriginal);
+ if (fabs(rhs) > .001f)
+ return lhs / rhs;
+ return Empty();
+}
+
+// Make a nice rotation from the incoming rotation
+static inline QT3DSF32 MakeNiceRotation(QT3DSF32 inAngle)
+{
+ TODEG(inAngle);
+ inAngle *= 10.0f;
+ QT3DSF32 sign = inAngle > 0.0f ? 1.0f : -1.0f;
+ // Attempt to ensure angle is prtty clean
+ QT3DSU32 clampedAngle = (QT3DSU32)(fabs(inAngle) + .5f);
+ QT3DSU32 leftover = clampedAngle % 10;
+ clampedAngle -= leftover;
+ if (leftover <= 2)
+ leftover = 0;
+ else if (leftover <= 7)
+ leftover = 5;
+ else
+ leftover = 10;
+ clampedAngle += leftover;
+ QT3DSF32 retval = (QT3DSF32)clampedAngle;
+ retval = (retval * sign) / 10.0f;
+ TORAD(retval);
+ return retval;
+}
+
+static inline QT3DSF32 ShortestAngleDifference(QT3DSF32 inCumulative, QT3DSF32 inNewTotal)
+{
+ QT3DSF32 diff = uic::render::SRotationHelper::ToMinimalAngle(inNewTotal - inCumulative);
+ return inCumulative + diff;
+}
+
+Option<SDragPreparationResult>
+STranslation::PrepareWidgetDrag(uic::widgets::StudioWidgetComponentIds::Enum inComponentId,
+ uic::widgets::StudioWidgetTypes::Enum inWidgetId,
+ uic::render::RenderWidgetModes::Enum inWidgetMode, SNode &inNode,
+ CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords)
+{
+ SDragPreparationResult retval;
+ retval.m_ComponentId = inComponentId;
+ retval.m_WidgetType = inWidgetId;
+ retval.m_WidgetMode = inWidgetMode;
+ uic::render::IUICRenderer &theRenderer(m_UICContext.GetRenderer());
+ retval.m_Renderer = &theRenderer;
+ retval.m_Node = &inNode;
+ retval.m_Layer = GetLayerForNode(inNode);
+ retval.m_Camera = theRenderer.GetCameraForNode(inNode);
+ uic::render::SWindowDimensions theUnsignedDimensions(m_UICContext.GetWindowDimensions());
+ QT3DSVec2 theWindowDimensions((QT3DSF32)theUnsignedDimensions.m_Width,
+ (QT3DSF32)theUnsignedDimensions.m_Height);
+ if (retval.m_Camera == nullptr || retval.m_Layer == nullptr)
+ return Empty();
+
+ SCamera &theCamera(*retval.m_Camera);
+ SLayer &theLayer(*retval.m_Layer);
+ QT3DSVec2 theLayerOriginalCoords = m_UICContext.GetRenderer().GetLayerMouseCoords(
+ *retval.m_Layer, QT3DSVec2((QT3DSF32)inOriginalCoords.x, (QT3DSF32)inOriginalCoords.y),
+ theWindowDimensions, true);
+
+ QT3DSVec2 theLayerMouseCoords = m_UICContext.GetRenderer().GetLayerMouseCoords(
+ *retval.m_Layer, QT3DSVec2((QT3DSF32)inMouseCoords.x, (QT3DSF32)inMouseCoords.y),
+ theWindowDimensions, true);
+
+ QT3DSVec2 thePreviousLayerMouseCoords = m_UICContext.GetRenderer().GetLayerMouseCoords(
+ *retval.m_Layer, QT3DSVec2((QT3DSF32)inPreviousMouseCoords.x, (QT3DSF32)inPreviousMouseCoords.y),
+ theWindowDimensions, true);
+ QT3DSMat44 theGlobalTransform(QT3DSMat44::createIdentity());
+ if (inWidgetMode == uic::render::RenderWidgetModes::Local) {
+ theGlobalTransform = m_MouseDownNode.m_GlobalTransform;
+ }
+ retval.m_GlobalTransform = theGlobalTransform;
+ QT3DSMat33 theNormalMat(theGlobalTransform.column0.getXYZ(), theGlobalTransform.column1.getXYZ(),
+ theGlobalTransform.column2.getXYZ());
+ theNormalMat = theNormalMat.getInverse().getTranspose();
+ retval.m_NormalMatrix = theNormalMat;
+ qt3ds::render::NVRenderRectF theLayerRect(theRenderer.GetLayerRect(theLayer));
+ SRay theOriginalRay =
+ theCamera.Unproject(theLayerOriginalCoords, theLayerRect, theWindowDimensions);
+ SRay theCurrentRay =
+ theCamera.Unproject(theLayerMouseCoords, theLayerRect, theWindowDimensions);
+ SRay thePreviousRay =
+ theCamera.Unproject(thePreviousLayerMouseCoords, theLayerRect, theWindowDimensions);
+ retval.m_OriginalRay = theOriginalRay;
+ retval.m_CurrentRay = theCurrentRay;
+ retval.m_PreviousRay = thePreviousRay;
+ QT3DSVec3 theAxis;
+ QT3DSVec3 thePlaneNormal;
+ bool isPlane = false;
+ QT3DSVec3 globalPos = inNode.GetGlobalPivot();
+ QT3DSVec3 camGlobalPos = theCamera.GetGlobalPos();
+ QT3DSVec3 theCamDirection;
+ retval.m_GlobalPos = globalPos;
+ retval.m_CameraGlobalPos = camGlobalPos;
+ if (theCamera.m_Flags.IsOrthographic())
+ theCamDirection = theCamera.GetDirection();
+ else {
+ theCamDirection = globalPos - camGlobalPos;
+ // The normal will be normalized below.
+ }
+ theCamDirection.normalize();
+ retval.m_CameraDirection = theCamDirection;
+ retval.m_AxisIndex = 0;
+ switch (inComponentId) {
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ case uic::widgets::StudioWidgetComponentIds::XAxis:
+ theAxis = QT3DSVec3(1, 0, 0);
+ break;
+ case uic::widgets::StudioWidgetComponentIds::YAxis:
+ theAxis = QT3DSVec3(0, 1, 0);
+ retval.m_AxisIndex = 1;
+ break;
+ case uic::widgets::StudioWidgetComponentIds::ZAxis:
+ theAxis = QT3DSVec3(0, 0, -1);
+ retval.m_AxisIndex = 2;
+ break;
+ case uic::widgets::StudioWidgetComponentIds::XPlane:
+ thePlaneNormal = QT3DSVec3(1, 0, 0);
+ isPlane = true;
+ break;
+ case uic::widgets::StudioWidgetComponentIds::YPlane:
+ thePlaneNormal = QT3DSVec3(0, 1, 0);
+ isPlane = true;
+ break;
+ case uic::widgets::StudioWidgetComponentIds::ZPlane:
+ thePlaneNormal = QT3DSVec3(0, 0, -1);
+ isPlane = true;
+ break;
+ case uic::widgets::StudioWidgetComponentIds::CameraPlane: {
+ isPlane = true;
+ thePlaneNormal = theCamDirection;
+ } break;
+ }
+ retval.m_IsPlane = isPlane;
+ if (inWidgetId == uic::widgets::StudioWidgetTypes::Rotation) {
+ if (isPlane == false) {
+ theAxis = theNormalMat.transform(theAxis);
+ theAxis.normalize();
+ thePlaneNormal = theAxis;
+ retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos));
+ } else {
+ if (inComponentId != uic::widgets::StudioWidgetComponentIds::CameraPlane) {
+ thePlaneNormal = theNormalMat.transform(thePlaneNormal);
+ }
+ thePlaneNormal.normalize();
+ retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos)));
+ }
+ } else {
+ if (isPlane == false) {
+ theAxis = theNormalMat.transform(theAxis);
+ theAxis.normalize();
+ QT3DSVec3 theCameraToObj = globalPos - camGlobalPos;
+ QT3DSVec3 theTemp = theAxis.cross(theOriginalRay.m_Direction);
+ // Then the axis is parallel to the camera, we can't drag meaningfullly
+ if (theTemp.magnitudeSquared() < .05f) {
+ // Attempt to find a better axis by moving the object back towards the camera.
+ QT3DSF32 theSign = theCameraToObj.dot(theCamDirection) > 0.0 ? -1.0f : 1.0f;
+ QT3DSF32 theDistance = theCameraToObj.dot(theCamDirection);
+ QT3DSVec3 thePoint = globalPos + (theDistance * theSign) * theAxis;
+ QT3DSVec3 theNewDir = thePoint - camGlobalPos;
+ theNewDir.normalize();
+ theTemp = theAxis.cross(theNewDir);
+ // Attempt again to find a better cross
+ if (theTemp.magnitudeSquared() < .05f)
+ return Empty();
+ }
+ thePlaneNormal = theTemp.cross(theAxis);
+ thePlaneNormal.normalize();
+ retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * thePlaneNormal.dot(globalPos));
+ } else {
+ thePlaneNormal = theNormalMat.transform(thePlaneNormal);
+ thePlaneNormal.normalize();
+ retval.m_Plane = qt3ds::NVPlane(thePlaneNormal, -1.0f * (thePlaneNormal.dot(globalPos)));
+ }
+ }
+ retval.m_Axis = theAxis;
+ retval.m_OriginalPlaneCoords = theOriginalRay.Intersect(retval.m_Plane);
+ retval.m_CurrentPlaneCoords = theCurrentRay.Intersect(retval.m_Plane);
+ retval.m_PreviousPlaneCoords = thePreviousRay.Intersect(retval.m_Plane);
+ return retval;
+}
+
+void STranslation::PerformWidgetDrag(int inWidgetSubComponent, CPt inOriginalCoords,
+ CPt inPreviousMouseCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor)
+{
+ if (inWidgetSubComponent == 0 || m_LastRenderedWidget == nullptr) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ Option<SDragPreparationResult> thePrepResult(PrepareWidgetDrag(
+ static_cast<uic::widgets::StudioWidgetComponentIds::Enum>(inWidgetSubComponent),
+ m_LastRenderedWidget->GetWidgetType(), m_LastRenderedWidget->GetRenderWidgetMode(),
+ m_LastRenderedWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords));
+ if (!thePrepResult.hasValue())
+ return;
+
+ Option<QT3DSVec3> theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords);
+ Option<QT3DSVec3> theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords);
+ Option<QT3DSVec3> thePreviousPlaneCoords(thePrepResult->m_PreviousPlaneCoords);
+ QT3DSVec3 globalPos(thePrepResult->m_GlobalPos);
+ bool isPlane(thePrepResult->m_IsPlane);
+ QT3DSVec3 theAxis(thePrepResult->m_Axis);
+ QT3DSU32 axisIndex(thePrepResult->m_AxisIndex);
+ SNode *theNode(thePrepResult->m_Node);
+ SRay theCurrentRay(thePrepResult->m_CurrentRay);
+ SRay theOriginalRay(thePrepResult->m_OriginalRay);
+ QT3DSVec3 thePlaneNormal(thePrepResult->m_Plane.n);
+ QT3DSVec3 theCamDirection(thePrepResult->m_CameraDirection);
+ QT3DSVec3 camGlobalPos(thePrepResult->m_CameraGlobalPos);
+
+ switch (m_LastRenderedWidget->GetWidgetType()) {
+ default:
+ QT3DS_ASSERT(false);
+ return;
+ case uic::widgets::StudioWidgetTypes::Scale: {
+ if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) {
+ QT3DSVec3 objToOriginal = globalPos - *theOriginalPlaneCoords;
+ QT3DSVec3 objToCurrent = globalPos - *theCurrentPlaneCoords;
+ QT3DSVec3 theScaleMultiplier(1, 1, 1);
+ if (!isPlane) {
+ // Ensure that we only have a scale vector in the direction of the axis.
+ objToOriginal = theAxis * (theAxis.dot(objToOriginal));
+ objToCurrent = theAxis * (theAxis.dot(objToCurrent));
+ QT3DSF32 objToOriginalDot = theAxis.dot(objToOriginal);
+ if (fabs(objToOriginalDot) > .001f)
+ theScaleMultiplier[axisIndex] =
+ theAxis.dot(objToCurrent) / theAxis.dot(objToOriginal);
+ else
+ theScaleMultiplier[axisIndex] = 0.0f;
+ }
+
+ QT3DSMat33 theNodeAxisMatrix(theNode->m_GlobalTransform.column0.getXYZ(),
+ theNode->m_GlobalTransform.column1.getXYZ(),
+ theNode->m_GlobalTransform.column2.getXYZ());
+ theNodeAxisMatrix = theNodeAxisMatrix.getInverse().getTranspose();
+ QT3DSVec3 &theLocalXAxis(theNodeAxisMatrix.column0);
+ QT3DSVec3 &theLocalYAxis(theNodeAxisMatrix.column1);
+ QT3DSVec3 &theLocalZAxis(theNodeAxisMatrix.column2);
+ theLocalXAxis.normalize();
+ theLocalYAxis.normalize();
+ theLocalZAxis.normalize();
+
+ Option<QT3DSF32> theXScale = GetScaleAlongAxis(theLocalXAxis, objToOriginal, objToCurrent);
+ Option<QT3DSF32> theYScale = GetScaleAlongAxis(theLocalYAxis, objToOriginal, objToCurrent);
+ Option<QT3DSF32> theZScale = GetScaleAlongAxis(theLocalZAxis, objToOriginal, objToCurrent);
+ QT3DSVec3 theScale = m_MouseDownNode.m_Scale;
+ if (theXScale.isEmpty() && theYScale.isEmpty() && theZScale.isEmpty()) {
+ theScale = QT3DSVec3(0, 0, 0);
+ } else {
+ if (theXScale.hasValue())
+ theScale.x *= *theXScale;
+ if (theYScale.hasValue())
+ theScale.y *= *theYScale;
+ if (theZScale.hasValue())
+ theScale.z *= *theZScale;
+ }
+ m_LastRenderedWidget->SetAxisScale(theScaleMultiplier);
+ SetScale(theScale, inEditor);
+ }
+ } break;
+ case uic::widgets::StudioWidgetTypes::Rotation: {
+ QT3DSF32 theIntersectionCosine = theOriginalRay.m_Direction.dot(thePlaneNormal);
+ QT3DSVec3 objToPrevious;
+ QT3DSVec3 objToCurrent;
+ /*
+ long theModifiers = CHotKeys::GetCurrentKeyModifiers();
+ if ( theModifiers & CHotKeys::MODIFIER_SHIFT )
+ {
+ DebugBreak();
+ }
+ */
+ if (!theOriginalPlaneCoords.hasValue() || !theCurrentPlaneCoords.hasValue())
+ return;
+ if (fabs(theIntersectionCosine) > .08f) {
+ objToPrevious = globalPos - *theOriginalPlaneCoords;
+ objToCurrent = globalPos - *theCurrentPlaneCoords;
+ objToPrevious.normalize();
+ QT3DSF32 lineLen = objToCurrent.normalize();
+ QT3DSF32 cosAngle = objToPrevious.dot(objToCurrent);
+ QT3DSVec3 theCrossProd = objToPrevious.cross(objToCurrent);
+ QT3DSF32 theCrossPlaneDot = theCrossProd.dot(thePlaneNormal);
+ QT3DSF32 angleSign = theCrossPlaneDot >= 0.0f ? 1.0f : -1.0f;
+ QT3DSF32 angleRad = acos(cosAngle) * angleSign;
+ angleRad = MakeNiceRotation(angleRad);
+ QT3DSQuat theRotation(angleRad, thePlaneNormal);
+ m_CumulativeRotation = ShortestAngleDifference(m_CumulativeRotation, angleRad);
+ m_LastRenderedWidget->SetRotationEdges(-1.0f * objToPrevious, thePlaneNormal,
+ m_CumulativeRotation, lineLen);
+ ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false);
+ }
+ // In this case we are viewing the plane of rotation pretty much dead on, so we need to
+ // assume
+ // the camera and the object are both in the plane of rotation. In this case we *sort* of
+ // need to
+ // do trackball rotation but force it to one plane of rotation.
+ else {
+ // Setup a plane 600 units away from the camera and have the gadget run from there.
+
+ // This keeps rotation consistent.
+ QT3DSVec3 thePlaneSpot = theCamDirection * -600.0f + camGlobalPos;
+ qt3ds::NVPlane theCameraPlaneAtObject(theCamDirection,
+ -1.0f * (theCamDirection.dot(thePlaneSpot)));
+ theCurrentPlaneCoords = theCurrentRay.Intersect(theCameraPlaneAtObject);
+ theOriginalPlaneCoords = theOriginalRay.Intersect(theCameraPlaneAtObject);
+ QT3DSVec3 theChangeVector = *theOriginalPlaneCoords - *theCurrentPlaneCoords;
+ // Remove any component of the change vector that doesn't lie in the plane.
+ theChangeVector =
+ theChangeVector - theChangeVector.dot(thePlaneNormal) * thePlaneNormal;
+ QT3DSF32 theDistance = theChangeVector.normalize();
+ // We want about 90* per 200 units in imaginary 600-units-from-camera space.
+ QT3DSF32 theScaleFactor = 1.0f / 200.0f;
+ if (thePrepResult->m_Camera->m_Flags.IsOrthographic())
+ theScaleFactor = 1.0f / 100.0f;
+
+ QT3DSF32 theDeg = 90.0f * theDistance * theScaleFactor;
+ // Check the sign of the angle.
+ QT3DSVec3 theCurrentIsectDir = camGlobalPos - *theCurrentPlaneCoords;
+ QT3DSVec3 thePreviousIsectDir = camGlobalPos - *theOriginalPlaneCoords;
+ QT3DSVec3 theCrossProd = theCurrentIsectDir.cross(thePreviousIsectDir);
+ QT3DSF32 theAngleSign = theCrossProd.dot(thePlaneNormal) > 0.0f ? 1.0f : -1.0f;
+ theDeg *= theAngleSign;
+ QT3DSF32 theRad(theDeg);
+ TORAD(theRad);
+ theRad = MakeNiceRotation(theRad);
+ QT3DSQuat theRotation(theRad, thePlaneNormal);
+ ApplyRotationToSelectedInstance(theRotation, *theNode, inEditor, false);
+ }
+ } break;
+ case uic::widgets::StudioWidgetTypes::Translation: {
+ if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) {
+ QT3DSVec3 theDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords;
+ if (isPlane) {
+ ApplyPositionalChange(theDiff, *theNode, inEditor);
+ } else {
+ QT3DSVec3 theMovement = theAxis * theAxis.dot(theDiff);
+ ApplyPositionalChange(theMovement, *theNode, inEditor);
+ }
+ }
+ } break;
+ }
+}
+
+static float RoundToNearest(float inValue, float inMin, float inMax, float inRound)
+{
+ float half = (inMin + inMax) / 2.0f;
+ inValue -= half;
+ inValue = inRound * floor(inValue / inRound + .5f);
+ inValue += half;
+ inValue -= inMin;
+ return inValue;
+}
+
+void STranslation::PerformGuideDrag(CUICDMGuideHandle inGuide, CPt inPoint,
+ CUpdateableDocumentEditor &inEditor)
+{
+ UICDM::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide);
+ CPt renderSpacePt(inPoint.x, (long)GetViewportDimensions().y - inPoint.y);
+ switch (theInfo.m_Direction) {
+ case UICDM::GuideDirections::Horizontal:
+ theInfo.m_Position = RoundToNearest((float)renderSpacePt.y, (float)m_InnerRect.m_Bottom,
+ (float)m_InnerRect.m_Top, 10.0f);
+ break;
+ case UICDM::GuideDirections::Vertical:
+ theInfo.m_Position = RoundToNearest((float)renderSpacePt.x, (float)m_InnerRect.m_Left,
+ (float)m_InnerRect.m_Right, 10.0f);
+ break;
+ default:
+ QT3DS_ASSERT(FALSE);
+ break;
+ break;
+ }
+ inEditor.EnsureEditor(L"Drag Guide", __FILE__, __LINE__).UpdateGuide(inGuide, theInfo);
+ inEditor.FireImmediateRefresh(UICDM::CUICDMInstanceHandle());
+}
+
+void STranslation::CheckGuideInPresentationRect(CUICDMGuideHandle inGuide,
+ CUpdateableDocumentEditor &inEditor)
+{
+ UICDM::SGuideInfo theInfo = m_Doc.GetDocumentReader().GetGuideInfo(inGuide);
+ bool inPresentation = false;
+ QT3DSF32 presHeight = (QT3DSF32)m_InnerRect.m_Top - (QT3DSF32)m_InnerRect.m_Bottom;
+ QT3DSF32 presWidth = (QT3DSF32)m_InnerRect.m_Right - (QT3DSF32)m_InnerRect.m_Left;
+ switch (theInfo.m_Direction) {
+ case UICDM::GuideDirections::Horizontal:
+ inPresentation = 0.0f <= theInfo.m_Position && presHeight >= theInfo.m_Position;
+ break;
+ case UICDM::GuideDirections::Vertical:
+ inPresentation = 0.0f <= theInfo.m_Position && presWidth >= theInfo.m_Position;
+ break;
+ }
+ if (!inPresentation)
+ inEditor.EnsureEditor(L"Delete Guide", __FILE__, __LINE__).DeleteGuide(inGuide);
+}
+
+namespace {
+
+QT3DSF32 degToRad(const QT3DSF32 a)
+{
+ return (QT3DSF32)0.01745329251994329547 * a;
+}
+
+/**
+\brief Converts radians to degrees.
+*/
+QT3DSF32 radToDeg(const QT3DSF32 a)
+{
+ return (QT3DSF32)57.29577951308232286465 * a;
+}
+}
+
+void STranslation::PerformPathDrag(uic::studio::SPathPick &inPathPick, CPt inOriginalCoords,
+ CPt inPreviousMouseCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor)
+{
+ Option<SDragPreparationResult> thePrepResult(PrepareWidgetDrag(
+ uic::widgets::StudioWidgetComponentIds::ZPlane,
+ uic::widgets::StudioWidgetTypes::Translation, uic::render::RenderWidgetModes::Local,
+ m_PathWidget->GetNode(), inOriginalCoords, inPreviousMouseCoords, inMouseCoords));
+ if (!thePrepResult.hasValue())
+ return;
+
+ Option<QT3DSVec3> theOriginalPlaneCoords(thePrepResult->m_OriginalPlaneCoords);
+ Option<QT3DSVec3> theCurrentPlaneCoords(thePrepResult->m_CurrentPlaneCoords);
+ Option<QT3DSVec3> thePreviousPlaneCoords(thePrepResult->m_PreviousPlaneCoords);
+ if (theOriginalPlaneCoords.hasValue() && theCurrentPlaneCoords.hasValue()) {
+ QT3DSVec3 theGlobalDiff = *theCurrentPlaneCoords - *theOriginalPlaneCoords;
+ QT3DSMat44 theGlobalInverse = thePrepResult->m_GlobalTransform.getInverse();
+ QT3DSVec3 theCurrentPos = theGlobalInverse.transform(*theCurrentPlaneCoords);
+ QT3DSVec3 theOldPos = theGlobalInverse.transform(*theOriginalPlaneCoords);
+ QT3DSVec3 theDiff = theCurrentPos - theOldPos;
+ // Now find the anchor point; nontrivial.
+ SPathTranslator *theTranslator =
+ reinterpret_cast<SPathTranslator *>(thePrepResult->m_Node->m_UserData.m_UserData);
+ UICDM::CUICDMInstanceHandle thePathHandle = theTranslator->GetInstanceHandle();
+ UICDM::CUICDMInstanceHandle theAnchorHandle = GetAnchorPoint(inPathPick);
+
+ if (theAnchorHandle.Valid()) {
+ UICDM::CUICDMPropertyHandle thePosProperty =
+ m_ObjectDefinitions.m_PathAnchorPoint.m_Position.m_Property;
+ UICDM::CUICDMPropertyHandle theAngleProperty =
+ m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingAngle.m_Property;
+ UICDM::CUICDMPropertyHandle theIncomingDistanceProperty =
+ m_ObjectDefinitions.m_PathAnchorPoint.m_IncomingDistance.m_Property;
+ UICDM::CUICDMPropertyHandle theOutgoingDistanceProperty =
+ m_ObjectDefinitions.m_PathAnchorPoint.m_OutgoingDistance.m_Property;
+
+ IDocumentReader &theReader(m_Doc.GetDocumentReader());
+ SFloat2 anchorPos =
+ *theReader.GetTypedInstancePropertyValue<SFloat2>(theAnchorHandle, thePosProperty);
+ QT3DSVec2 anchorPosVec = QT3DSVec2(anchorPos[0], anchorPos[1]);
+ if (m_LastPathDragValue.hasValue() == false) {
+ SPathAnchorDragInitialValue initialValue;
+ initialValue.m_Position = anchorPosVec;
+ initialValue.m_IncomingAngle = theReader.GetTypedInstancePropertyValue<float>(
+ theAnchorHandle, theAngleProperty);
+ initialValue.m_IncomingDistance = theReader.GetTypedInstancePropertyValue<float>(
+ theAnchorHandle, theIncomingDistanceProperty);
+ initialValue.m_OutgoingDistance = theReader.GetTypedInstancePropertyValue<float>(
+ theAnchorHandle, theOutgoingDistanceProperty);
+ m_LastPathDragValue = initialValue;
+ }
+ SPathAnchorDragInitialValue &lastValue(*m_LastPathDragValue);
+ QT3DSVec2 theCurrentValue;
+ switch (inPathPick.m_Property) {
+ case SPathPick::Anchor:
+ theCurrentValue = lastValue.m_Position;
+ break;
+ case SPathPick::IncomingControl:
+ theCurrentValue = uic::render::IPathManagerCore::GetControlPointFromAngleDistance(
+ lastValue.m_Position, lastValue.m_IncomingAngle, lastValue.m_IncomingDistance);
+ break;
+ case SPathPick::OutgoingControl:
+ theCurrentValue = uic::render::IPathManagerCore::GetControlPointFromAngleDistance(
+ lastValue.m_Position, lastValue.m_IncomingAngle + 180.0f,
+ lastValue.m_OutgoingDistance);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ theCurrentValue[0] += theDiff.x;
+ theCurrentValue[1] += theDiff.y;
+ Q3DStudio::IDocumentEditor &theEditor =
+ inEditor.EnsureEditor(L"Anchor Point Drag", __FILE__, __LINE__);
+ switch (inPathPick.m_Property) {
+ case SPathPick::Anchor:
+ theEditor.SetInstancePropertyValue(theAnchorHandle, thePosProperty,
+ SFloat2(theCurrentValue.x, theCurrentValue.y));
+ break;
+ case SPathPick::IncomingControl: {
+ QT3DSVec2 angleDistance =
+ uic::render::IPathManagerCore::GetAngleDistanceFromControlPoint(
+ anchorPosVec, theCurrentValue);
+ float angleDiff = angleDistance.x - lastValue.m_IncomingAngle;
+ float minimalDiff =
+ radToDeg(uic::render::SRotationHelper::ToMinimalAngle(degToRad(angleDiff)));
+ float newAngle = lastValue.m_IncomingAngle + minimalDiff;
+ theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle);
+ theEditor.SetInstancePropertyValue(theAnchorHandle, theIncomingDistanceProperty,
+ angleDistance.y);
+ } break;
+ case SPathPick::OutgoingControl: {
+ QT3DSVec2 angleDistance =
+ uic::render::IPathManagerCore::GetAngleDistanceFromControlPoint(
+ anchorPosVec, theCurrentValue);
+ angleDistance.x += 180.0f;
+ float angleDiff = angleDistance.x - lastValue.m_IncomingAngle;
+ float minimalDiff =
+ radToDeg(uic::render::SRotationHelper::ToMinimalAngle(degToRad(angleDiff)));
+ float newAngle = lastValue.m_IncomingAngle + minimalDiff;
+ theEditor.SetInstancePropertyValue(theAnchorHandle, theAngleProperty, newAngle);
+ theEditor.SetInstancePropertyValue(theAnchorHandle, theOutgoingDistanceProperty,
+ angleDistance.y);
+ } break;
+ }
+
+ inEditor.FireImmediateRefresh(m_AssetGraph.GetParent(theAnchorHandle));
+ }
+ }
+}
+
+// Pulled directly from old studio's Node.cpp.
+void STranslation::OnNudge(ENudgeDirection inDirection, int inToolmode, int inFlags,
+ CUpdateableDocumentEditor &inEditor)
+{
+ Q_UNUSED(inFlags);
+ SNode *theSelectedNode = GetSelectedNode();
+ if (theSelectedNode == nullptr)
+ return;
+ ;
+
+ // Increment the key press count
+ m_KeyRepeat++;
+
+ // Increment the acceleration for every 5 key presses
+ float theAcceleration = static_cast<float>(qt3ds::foundation::floor(m_KeyRepeat / 5.0)) + 1.0f;
+
+ CUICDMPropertyHandle thePropertyHandle;
+ SFloat3 theValue;
+ const wchar_t *theCommandName = L"";
+ UICDM::CUICDMInstanceHandle theInstanceHandle = m_Doc.GetSelectedInstance();
+ CDispatchDataModelImmediateScope __dispatchScope(*m_Doc.GetCore()->GetDispatch(),
+ theInstanceHandle);
+ // See what tool mode we are in
+ switch (inToolmode) {
+ // We want to nudge the position vector
+ case STUDIO_TOOLMODE_MOVE:
+ thePropertyHandle = m_ObjectDefinitions.m_Node.m_Position;
+ theValue =
+ m_Reader.GetTypedInstancePropertyValue<SFloat3>(theInstanceHandle, thePropertyHandle);
+ theCommandName = L"Set Position";
+ break;
+
+ // We want to nudge the rotation vector
+ case STUDIO_TOOLMODE_ROTATE:
+ thePropertyHandle = m_ObjectDefinitions.m_Node.m_Rotation;
+ theValue = m_Reader.GetTypedInstancePropertyValue<UICDM::SFloat3>(theInstanceHandle,
+ thePropertyHandle);
+ theCommandName = L"Set Rotation";
+ break;
+
+ // We want to nudge the scale vector
+ case STUDIO_TOOLMODE_SCALE:
+ thePropertyHandle = m_ObjectDefinitions.m_Node.m_Scale;
+ theValue = m_Reader.GetTypedInstancePropertyValue<UICDM::SFloat3>(theInstanceHandle,
+ thePropertyHandle);
+ theCommandName = L"Set Scale";
+ break;
+
+ // We should always have a tool mode, but just in case, default to position
+ default:
+ thePropertyHandle = m_ObjectDefinitions.m_Node.m_Position;
+ theValue = m_Reader.GetTypedInstancePropertyValue<UICDM::SFloat3>(theInstanceHandle,
+ thePropertyHandle);
+ theCommandName = L"Set Position";
+ break;
+ }
+
+ // If we are in rotate mode, we need to switch the x and y values because we want to rotate
+ // *around* the specified axis
+ if ((inToolmode) == STUDIO_TOOLMODE_ROTATE) {
+ // Switch from positive x to negative y
+ if (inDirection == NUDGE_POS_X) {
+ inDirection = NUDGE_NEG_Y;
+ }
+ // Switch from positive y to negative x
+ else if (inDirection == NUDGE_POS_Y) {
+ inDirection = NUDGE_NEG_X;
+ }
+ // Switch from negative x to positive y
+ else if (inDirection == NUDGE_NEG_X) {
+ inDirection = NUDGE_POS_Y;
+ }
+ // Switch from negative y to positive x
+ else if (inDirection == NUDGE_NEG_Y) {
+ inDirection = NUDGE_POS_X;
+ }
+ }
+
+ float theNudgeAmount = static_cast<float>(CStudioPreferences::GetNudgeAmount());
+
+ // Now check which value to change (x, y, or z) and whether to increment or decrement
+ switch (inDirection) {
+ // Nudge along the positive x-axis
+ case NUDGE_POS_X:
+ theValue.m_Floats[0] += (theNudgeAmount * theAcceleration);
+ break;
+
+ // Nudge along the positive y-axis
+ case NUDGE_POS_Y:
+ theValue.m_Floats[1] += (theNudgeAmount * theAcceleration);
+ break;
+
+ // Nudge along the positive z-axis
+ case NUDGE_POS_Z:
+ theValue.m_Floats[2] += (theNudgeAmount * theAcceleration);
+ break;
+
+ // Nudge along the negative x-axis
+ case NUDGE_NEG_X:
+ theValue.m_Floats[0] -= (theNudgeAmount * theAcceleration);
+ break;
+
+ // Nudge along the negative y-axis
+ case NUDGE_NEG_Y:
+ theValue.m_Floats[1] -= (theNudgeAmount * theAcceleration);
+ break;
+
+ // Nudge along the negative z-axis
+ case NUDGE_NEG_Z:
+ theValue.m_Floats[2] -= (theNudgeAmount * theAcceleration);
+ break;
+ }
+
+ inEditor.EnsureEditor(theCommandName, __FILE__, __LINE__)
+ .SetInstancePropertyValue(theInstanceHandle, thePropertyHandle, theValue);
+}
+
+SNode *STranslation::GetEditCameraLayer()
+{
+ if (m_EditCameraLayerTranslator)
+ return static_cast<SNode *>(&m_EditCameraLayerTranslator->GetGraphObject());
+ return nullptr;
+}
+
+PickTargetAreas::Enum STranslation::GetPickArea(CPt inPoint)
+{
+ qt3ds::render::NVRenderRectF displayViewport = m_UICContext.GetDisplayViewport();
+ QT3DSVec2 thePickPoint((QT3DSF32)inPoint.x,
+ m_UICContext.GetWindowDimensions().m_Height - (QT3DSF32)inPoint.y);
+ QT3DSF32 left = displayViewport.m_X;
+ QT3DSF32 right = displayViewport.m_X + displayViewport.m_Width;
+ QT3DSF32 top = displayViewport.m_Y + displayViewport.m_Height;
+ QT3DSF32 bottom = displayViewport.m_Y;
+ if (thePickPoint.x < left || thePickPoint.x > right || thePickPoint.y < bottom
+ || thePickPoint.y > top)
+ return PickTargetAreas::Matte;
+ return PickTargetAreas::Presentation;
+}
diff --git a/src/Authoring/Studio/Render/StudioRendererTranslation.h b/src/Authoring/Studio/Render/StudioRendererTranslation.h
new file mode 100644
index 00000000..7698d8ec
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioRendererTranslation.h
@@ -0,0 +1,691 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_RENDERER_TRANSLATION_H
+#define UIC_STUDIO_RENDERER_TRANSLATION_H
+#pragma once
+#include "StudioRendererImpl.h"
+#include "UICRenderLayer.h"
+#include "UICRenderer.h"
+#include "StudioWidget.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "foundation/FastAllocator.h"
+#include "StudioPickValues.h"
+#include "UICDMGuides.h"
+#include "PathWidget.h"
+#include "StudioPreferences.h"
+
+namespace uic {
+namespace studio {
+ struct SGraphObjectTranslator;
+ extern QT3DSU32 g_GraphObjectTranslatorTag;
+ inline void InitializePointerTags(IStringTable &) { g_GraphObjectTranslatorTag = 0x0088BEEF; }
+}
+}
+namespace uic {
+namespace render {
+ template <>
+ struct SPointerTag<uic::studio::SGraphObjectTranslator>
+ {
+ static QT3DSU32 GetTag() { return uic::studio::g_GraphObjectTranslatorTag; }
+ };
+}
+}
+
+namespace uic {
+namespace studio {
+
+ typedef std::shared_ptr<UICDM::ISignalConnection> TSignalConnection;
+
+ struct STranslation;
+
+ struct SGraphObjectTranslator
+ {
+ protected:
+ UICDM::CUICDMInstanceHandle m_InstanceHandle;
+
+ public:
+ // This will never be null. The reason it is a pointer is because
+ // alias translators need to switch which graph object they point to
+ UICDM::CUICDMInstanceHandle m_AliasInstanceHandle;
+ SGraphObject *m_GraphObject;
+ QT3DSU32 m_DirtyIndex;
+ SGraphObjectTranslator(UICDM::CUICDMInstanceHandle inInstance, SGraphObject &inObj)
+ : m_InstanceHandle(inInstance)
+ , m_GraphObject(&inObj)
+ , m_DirtyIndex(QT3DS_MAX_U32)
+ {
+ m_GraphObject->m_UserData = uic::render::STaggedPointer(this);
+ }
+ // The destructors will not be called at this time for most of the objects
+ // but they will be released.
+ virtual ~SGraphObjectTranslator() {}
+ // Push new data into the UIC render graph.
+ virtual void PushTranslation(STranslation &inTranslatorContext);
+ virtual void AfterRenderGraphIsBuilt(STranslation &) {}
+ virtual void SetActive(bool inActive) = 0;
+ virtual void ClearChildren() = 0;
+ virtual void AppendChild(SGraphObject &inChild) = 0;
+ virtual SGraphObject &GetGraphObject() { return *m_GraphObject; }
+ virtual SGraphObject &GetNonAliasedGraphObject() { return *m_GraphObject; }
+ virtual UICDM::CUICDMInstanceHandle GetInstanceHandle() { return m_InstanceHandle; }
+ virtual UICDM::CUICDMInstanceHandle GetSceneGraphInstanceHandle()
+ {
+ return m_InstanceHandle;
+ }
+ virtual UICDM::CUICDMInstanceHandle GetPossiblyAliasedInstanceHandle()
+ {
+ if (m_AliasInstanceHandle.Valid())
+ return m_AliasInstanceHandle;
+ return GetInstanceHandle();
+ }
+ };
+
+ struct STranslatorGetDirty
+ {
+ QT3DSU32 operator()(const SGraphObjectTranslator &inTrans) const
+ {
+ return inTrans.m_DirtyIndex;
+ }
+ };
+ struct STranslatorSetDirty
+ {
+ void operator()(SGraphObjectTranslator &inTrans, QT3DSU32 idx) const
+ {
+ inTrans.m_DirtyIndex = idx;
+ }
+ };
+
+ typedef InvasiveSet<SGraphObjectTranslator, STranslatorGetDirty, STranslatorSetDirty>
+ TTranslatorDirtySet;
+
+ struct TranslationSelectMode
+ {
+ enum Enum {
+ Group = 0,
+ Single = 1,
+ NestedComponentSingle,
+ };
+ };
+
+ struct EditCameraTypes
+ {
+ enum Enum {
+ SceneCamera = 0,
+ Perspective,
+ Orthographic,
+ Directional,
+ };
+ };
+
+ const QT3DSF32 g_EditCameraFOV = 45.0f;
+ const QT3DSF32 g_RotationScaleFactor = 2 * M_PI / 180.0f;
+
+ struct SEditCameraPersistentInformation
+ {
+ QT3DSVec3 m_Position;
+ QT3DSVec3 m_Direction;
+ QT3DSF32 m_ViewRadius;
+ EditCameraTypes::Enum m_CameraType;
+ QT3DSQuat m_Rotation;
+ SEditCameraPersistentInformation()
+ : m_Position(0, 0, 0)
+ , m_Direction(0, 0, 0)
+ , m_ViewRadius(600)
+ , m_CameraType(EditCameraTypes::Perspective)
+ , m_Rotation(QT3DSQuat::createIdentity())
+ {
+ }
+
+ void ApplyToCamera(SCamera &inCamera, QT3DSVec2 inViewport)
+ {
+ // Setup shared default values.
+ inCamera.m_ClipFar = 2000000.0f;
+ inCamera.m_ClipNear = 1.0f;
+ if (m_CameraType == EditCameraTypes::Perspective) {
+ inCamera.m_FOV = g_EditCameraFOV;
+ TORAD(inCamera.m_FOV);
+ inCamera.m_Flags.SetOrthographic(false);
+ } else
+ inCamera.m_Flags.SetOrthographic(true);
+
+ QT3DSVec3 theDirection = m_Direction;
+ theDirection.normalize();
+
+ // The goal is to setup a global transform that
+ QT3DSMat44 thePivotMatrix = QT3DSMat44::createIdentity();
+ thePivotMatrix.column3.x = m_Position.x;
+ thePivotMatrix.column3.y = m_Position.y;
+ thePivotMatrix.column3.z = m_Position.z;
+ QT3DSMat44 theGlobalTransform = thePivotMatrix;
+
+ QT3DSVec3 theUpDir(QT3DSVec3(0, 1, 0));
+ QT3DSF32 theTestLen = theDirection.cross(theUpDir).magnitudeSquared();
+ if (theTestLen < .01f)
+ theUpDir = QT3DSVec3(0, 0, 1) * theDirection.dot(QT3DSVec3(0, 1, 0));
+ theUpDir.normalize();
+ QT3DSMat33 theLookAtMatrix = inCamera.GetLookAtMatrix(theUpDir, theDirection);
+ QT3DSMat33 theFinalMatrix = theLookAtMatrix * QT3DSMat33(m_Rotation);
+ QT3DSMat44 theRotationTransform = QT3DSMat44(theFinalMatrix.column0, theFinalMatrix.column1,
+ theFinalMatrix.column2, QT3DSVec3(0, 0, 0));
+
+ // The view radius dictates the zoom.
+ QT3DSF32 theZoom = 1.0f;
+ if (inCamera.m_Flags.IsOrthographic()) {
+ QT3DSF32 theViewport = NVMin(inViewport.x, inViewport.y);
+ theZoom = (m_ViewRadius * 2.0f) / theViewport;
+ } else {
+ // We know the hypotenuse is 600.
+ // So if we want to zoom the scene, we do this.
+ theZoom = m_ViewRadius / (sinf(inCamera.m_FOV / 2.0f) * 600.f);
+ }
+ QT3DSMat44 theScaleMatrix = QT3DSMat44(QT3DSVec4(theZoom, theZoom, theZoom, 1));
+ QT3DSMat44 thePositionMatrix = QT3DSMat44::createIdentity();
+ thePositionMatrix.column3.x = m_Position.x;
+ thePositionMatrix.column3.y = m_Position.y;
+ thePositionMatrix.column3.z = m_Position.z + 600;
+ theGlobalTransform = theGlobalTransform * theRotationTransform;
+ theGlobalTransform = theGlobalTransform * theScaleMatrix;
+ theGlobalTransform = theGlobalTransform * thePivotMatrix.getInverse();
+ theGlobalTransform = theGlobalTransform * thePositionMatrix;
+ // This works because the camera has no hierarchy.
+ inCamera.m_LocalTransform = theGlobalTransform;
+ inCamera.m_Flags.SetTransformDirty(false);
+ inCamera.MarkDirty(uic::render::NodeTransformDirtyFlag::TransformNotDirty);
+ }
+
+ bool IsOrthographic() const { return m_CameraType != EditCameraTypes::Perspective; }
+
+ bool SupportsRotation() const { return m_CameraType != EditCameraTypes::Directional; }
+ };
+ struct MovementTypes
+ {
+ enum Enum {
+ Unknown = 0,
+ Translate,
+ TranslateAlongCameraDirection,
+ Scale,
+ ScaleZ,
+ Rotation,
+ RotationAboutCameraDirection,
+ };
+ };
+
+ struct SEditCameraLayerTranslator;
+ struct SZoomRender
+ {
+ CPt m_Point;
+ uic::render::SLayer *m_Layer;
+ SZoomRender(CPt inPoint, uic::render::SLayer *inLayer)
+ : m_Point(inPoint)
+ , m_Layer(inLayer)
+ {
+ }
+ SZoomRender()
+ : m_Layer(nullptr)
+ {
+ }
+ };
+
+ struct PickTargetAreas
+ {
+ enum Enum {
+ Presentation,
+ Matte,
+ };
+ };
+
+ struct SRulerRect
+ {
+ QT3DSI32 m_Left;
+ QT3DSI32 m_Top;
+ QT3DSI32 m_Right;
+ QT3DSI32 m_Bottom;
+ SRulerRect()
+ : m_Left(0)
+ , m_Top(0)
+ , m_Right(0)
+ , m_Bottom(0)
+ {
+ }
+ SRulerRect(QT3DSI32 l, QT3DSI32 t, QT3DSI32 r, QT3DSI32 b)
+ : m_Left(l)
+ , m_Top(t)
+ , m_Right(r)
+ , m_Bottom(b)
+ {
+ }
+ bool Contains(QT3DSI32 x, QT3DSI32 y) const
+ {
+ return x >= m_Left && x <= m_Right && y >= m_Bottom && y <= m_Top;
+ }
+ };
+
+ struct SDragPreparationResult
+ {
+ uic::render::IUICRenderer *m_Renderer;
+ SNode *m_Node;
+ SLayer *m_Layer;
+ SCamera *m_Camera;
+ qt3ds::render::NVPlane m_Plane;
+ QT3DSVec3 m_GlobalPos;
+ QT3DSVec3 m_CameraGlobalPos;
+ QT3DSVec3 m_CameraDirection;
+ QT3DSVec3 m_Axis;
+ QT3DSMat44 m_GlobalTransform;
+ QT3DSMat33 m_NormalMatrix;
+ QT3DSU32 m_AxisIndex;
+ uic::widgets::StudioWidgetComponentIds::Enum m_ComponentId;
+ uic::widgets::StudioWidgetTypes::Enum m_WidgetType;
+ uic::render::RenderWidgetModes::Enum m_WidgetMode;
+ SRay m_OriginalRay;
+ SRay m_CurrentRay;
+ SRay m_PreviousRay;
+ Option<QT3DSVec3> m_OriginalPlaneCoords;
+ Option<QT3DSVec3> m_CurrentPlaneCoords;
+ Option<QT3DSVec3> m_PreviousPlaneCoords;
+ bool m_IsPlane;
+ SDragPreparationResult() {}
+ };
+
+ struct SPathAnchorDragInitialValue
+ {
+ QT3DSVec2 m_Position;
+ float m_IncomingAngle;
+ float m_IncomingDistance;
+ float m_OutgoingDistance;
+ SPathAnchorDragInitialValue() {}
+ };
+
+ struct STranslation : public uic::render::IUICRenderNodeFilter
+ {
+ typedef eastl::pair<UICDM::CUICDMInstanceHandle, SGraphObjectTranslator *>
+ THandleTranslatorPair;
+ typedef eastl::vector<THandleTranslatorPair> THandleTranslatorPairList;
+ // Now that we have aliases, one instance handle can map to several translators. One
+ // translator, however, only
+ // maps to one instance handle.
+ typedef nvhash_map<UICDM::CUICDMInstanceHandle, THandleTranslatorPairList, eastl::hash<int>>
+ TInstanceToTranslatorMap;
+ IStudioRenderer &m_Renderer;
+ IUICRenderContext &m_UICContext;
+ CDoc &m_Doc;
+ IDocumentReader &m_Reader;
+ SComposerObjectDefinitions &m_ObjectDefinitions;
+ UICDM::CStudioSystem &m_StudioSystem;
+ UICDM::CStudioFullSystem &m_FullSystem;
+ Q3DStudio::CGraph &m_AssetGraph;
+
+ // allocator for scene graph and translators
+ qt3ds::foundation::SSAutoDeallocatorAllocator m_Allocator;
+ // All translator related containers must come after the allocator
+ TInstanceToTranslatorMap m_TranslatorMap;
+ TTranslatorDirtySet m_DirtySet;
+ uic::render::SPresentation m_Presentation;
+ uic::render::SScene *m_Scene;
+ Q3DStudio::CGraphIterator m_GraphIterator;
+ nvvector<TSignalConnection> m_SignalConnections;
+ QT3DSI32 m_ComponentSecondsDepth;
+ SNode m_MouseDownNode;
+ SCamera m_MouseDownCamera;
+ Option<QT3DSMat44> m_MouseDownParentGlobalTransformInverse;
+ Option<QT3DSMat33> m_MouseDownParentRotationInverse;
+ Option<QT3DSMat33> m_MouseDownGlobalRotation;
+ QT3DSI32 m_KeyRepeat;
+ bool m_EditCameraEnabled;
+ bool m_EditLightEnabled;
+ SEditCameraPersistentInformation m_EditCameraInfo;
+ SCamera m_EditCamera;
+ SLight m_EditLight;
+ QT3DSVec2 m_Viewport;
+ SEditCameraLayerTranslator *m_EditCameraLayerTranslator;
+ Option<SZoomRender> m_ZoomRender;
+ NVScopedRefCounted<uic::widgets::IStudioWidget> m_TranslationWidget;
+ NVScopedRefCounted<uic::widgets::IStudioWidget> m_RotationWidget;
+ NVScopedRefCounted<uic::widgets::IStudioWidget> m_ScaleWidget;
+ NVScopedRefCounted<uic::widgets::IStudioWidget> m_LastRenderedWidget;
+ NVScopedRefCounted<uic::widgets::IPathWidget> m_PathWidget;
+ NVScopedRefCounted<qt3ds::render::NVRenderTexture2D> m_PickBuffer;
+ Option<SPathAnchorDragInitialValue> m_LastPathDragValue;
+ nvvector<qt3ds::QT3DSU8> m_PixelBuffer;
+ QT3DSF32 m_CumulativeRotation;
+ eastl::vector<uic::render::SPGGraphObject *> m_GuideContainer;
+ qt3ds::foundation::SFastAllocator<> m_GuideAllocator;
+ // The rects are maintained from last render because the render context
+ // doesn't guarantee the rects it returns are valid outside of begin/end render calls.
+ SRulerRect m_OuterRect;
+ SRulerRect m_InnerRect; // presentation rect.
+
+ QT3DSVec4 m_rectColor;
+ QT3DSVec4 m_lineColor;
+ QT3DSVec4 m_guideColor;
+ QT3DSVec4 m_selectedGuideColor;
+ QT3DSVec4 m_guideFillColor;
+ QT3DSVec4 m_selectedGuideFillColor;
+
+ STranslation(IStudioRenderer &inRenderer, IUICRenderContext &inContext);
+ void MarkBeginComponentSeconds(UICDM::CUICDMSlideHandle) { ++m_ComponentSecondsDepth; }
+
+ void MarkComponentSeconds(UICDM::CUICDMSlideHandle)
+ {
+ m_ComponentSecondsDepth = NVMax(0, m_ComponentSecondsDepth - 1);
+ if (m_ComponentSecondsDepth == 0)
+ RequestRender();
+ }
+
+ void ReleaseTranslation(Q3DStudio::TIdentifier inInstance);
+
+ void MarkGraphInstanceDirty(Q3DStudio::TIdentifier inInstance,
+ Q3DStudio::TIdentifier /*inParent*/)
+ {
+ MarkDirty(inInstance);
+ }
+
+ void MarkDirty(UICDM::CUICDMInstanceHandle inInstance);
+
+ void MarkDirty(UICDM::CUICDMInstanceHandle *inInstance, long inInstanceCount)
+ {
+ for (long idx = 0; idx < inInstanceCount; ++idx)
+ MarkDirty(inInstance[idx]);
+ }
+
+ void DrawBoundingBox(SNode &inNode, QT3DSVec3 inColor);
+ void DrawLightBoundingBox(SNode &inNode, QT3DSVec3 inColor);
+
+ void DrawChildBoundingBoxes(SNode &inNode)
+ {
+ ::CColor color = CStudioPreferences::GetGroupBoundingBoxColor();
+ QT3DSVec3 colorVec(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f);
+ for (SNode *theChild = inNode.m_FirstChild; theChild;
+ theChild = theChild->m_NextSibling) {
+ if (IncludeNode(*theChild))
+ DrawBoundingBox(*theChild, colorVec);
+ }
+ }
+
+ void DrawGroupBoundingBoxes(SGraphObjectTranslator &inTranslator)
+ {
+ SNode &theNode = static_cast<SNode &>(inTranslator.GetGraphObject());
+ if (theNode.m_FirstChild) {
+ ::CColor color = CStudioPreferences::GetGroupBoundingBoxColor();
+ QT3DSVec3 colorVec(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f);
+ DrawBoundingBox(theNode, colorVec);
+ if (inTranslator.GetGraphObject().m_Type != GraphObjectTypes::Layer)
+ DrawChildBoundingBoxes(theNode);
+ }
+ }
+
+ void DrawNonGroupBoundingBoxes(SGraphObjectTranslator &inTranslator)
+ {
+ SNode &theNode = static_cast<SNode &>(inTranslator.GetGraphObject());
+ if (inTranslator.GetGraphObject().m_Type == GraphObjectTypes::Light) {
+ ::CColor color = CStudioPreferences::GetLightBoundingBoxColor();
+ QT3DSVec3 colorVec(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f);
+ DrawLightBoundingBox(theNode, colorVec);
+ } else if (inTranslator.GetGraphObject().m_Type != GraphObjectTypes::Layer) {
+ ::CColor color = CStudioPreferences::GetSingleBoundingBoxColor();
+ QT3DSVec3 colorVec(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f);
+ DrawBoundingBox(theNode, colorVec);
+ DrawChildBoundingBoxes(theNode);
+ } else {
+ ::CColor color = CStudioPreferences::GetSingleBoundingBoxColor();
+ QT3DSVec3 colorVec(color.GetRed() / 255.f,
+ color.GetGreen() / 255.f,
+ color.GetBlue() / 255.f);
+ m_UICContext.GetRenderer().RenderLayerRect(
+ static_cast<SLayer &>(inTranslator.GetGraphObject()), colorVec);
+ }
+ }
+
+ void DrawAxis(SGraphObjectTranslator &inTranslator);
+
+ void SetViewport(QT3DSF32 inWidth, QT3DSF32 inHeight) { m_Viewport = QT3DSVec2(inWidth, inHeight); }
+
+ QT3DSVec2 GetViewportDimensions() { return m_Viewport; }
+
+ void ClearDirtySet()
+ {
+ // The dirty set may be modified while this operation is taking place in the case of
+ // alias nodes.
+ for (qt3ds::QT3DSU32 idx = 0; idx < (qt3ds::QT3DSU32)m_DirtySet.size(); ++idx) {
+ if (m_Reader.IsInstance(m_DirtySet[idx]->GetInstanceHandle()))
+ m_DirtySet[idx]->PushTranslation(*this);
+ }
+ m_DirtySet.clear();
+ }
+ // We build the render graph every time we render. This may seem wasteful
+ void BuildRenderGraph(UICDM::CUICDMInstanceHandle inParent,
+ CUICDMInstanceHandle inAliasHandle = UICDM::CUICDMInstanceHandle());
+ void
+ BuildRenderGraph(SGraphObjectTranslator &inParent,
+ UICDM::CUICDMInstanceHandle inAliasHandle = UICDM::CUICDMInstanceHandle());
+ void
+ DeactivateScan(SGraphObjectTranslator &inParent,
+ UICDM::CUICDMInstanceHandle inAliasHandle = UICDM::CUICDMInstanceHandle());
+ void PreRender();
+ void Render(int inWidgetId, bool inDrawGuides);
+ void EndRender();
+ void DoPrepareForDrag(SNode *inSelectedNode);
+ void ResetWidgets();
+ void EndDrag();
+ bool IsPathWidgetActive();
+
+ void PrepareForDrag() { DoPrepareForDrag(GetSelectedNode()); }
+
+ SStudioPickValue Pick(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode);
+ Option<QT3DSU32> PickWidget(CPt inMouseCoords, TranslationSelectMode::Enum inSelectMode,
+ uic::widgets::IStudioWidgetBase &inWidget);
+
+ qt3ds::foundation::Option<UICDM::SGuideInfo> PickRulers(CPt inMouseCoords);
+
+ SNode *GetSelectedNode()
+ {
+ UICDM::CUICDMInstanceHandle theHandle = m_Doc.GetSelectedInstance();
+ SGraphObjectTranslator *theTranslator = GetOrCreateTranslator(theHandle);
+ if (theTranslator
+ && GraphObjectTypes::IsNodeType(theTranslator->GetGraphObject().m_Type))
+ return static_cast<SNode *>(&theTranslator->GetGraphObject());
+ return nullptr;
+ }
+ static inline SFloat3 ToDataModel(const QT3DSVec3 &inValue)
+ {
+ return SFloat3(inValue.x, inValue.y, inValue.z);
+ }
+
+ static inline SFloat3 ToDataModelRotation(const QT3DSVec3 &inValue)
+ {
+ SFloat3 retval = ToDataModel(inValue);
+ TODEG(retval.m_Floats[0]);
+ TODEG(retval.m_Floats[1]);
+ TODEG(retval.m_Floats[2]);
+ return retval;
+ }
+
+ void SetPosition(const QT3DSVec3 &inPosition, CUpdateableDocumentEditor &inEditor)
+ {
+ inEditor.EnsureEditor(L"Set Position", __FILE__, __LINE__)
+ .SetInstancePropertyValue(m_Doc.GetSelectedInstance(),
+ m_ObjectDefinitions.m_Node.m_Position,
+ ToDataModel(inPosition));
+ inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance());
+ }
+ void SetRotation(const QT3DSVec3 &inRotation, CUpdateableDocumentEditor &inEditor)
+ {
+ inEditor.EnsureEditor(L"Set Rotation", __FILE__, __LINE__)
+ .SetInstancePropertyValue(m_Doc.GetSelectedInstance(),
+ m_ObjectDefinitions.m_Node.m_Rotation,
+ ToDataModelRotation(inRotation));
+ inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance());
+ }
+ void SetScale(const QT3DSVec3 &inScale, CUpdateableDocumentEditor &inEditor)
+ {
+ inEditor.EnsureEditor(L"Set Scale", __FILE__, __LINE__)
+ .SetInstancePropertyValue(m_Doc.GetSelectedInstance(),
+ m_ObjectDefinitions.m_Node.m_Scale, ToDataModel(inScale));
+ inEditor.FireImmediateRefresh(m_Doc.GetSelectedInstance());
+ }
+
+ QT3DSVec3 GetIntendedPosition(UICDM::CUICDMInstanceHandle inInstance, CPt inPos);
+
+ void ApplyPositionalChange(QT3DSVec3 inDiff, SNode &inNode,
+ CUpdateableDocumentEditor &inEditor);
+
+ void TranslateSelectedInstanceAlongCameraDirection(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ void TranslateSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor, bool inLockToAxis);
+
+ void ScaleSelectedInstanceZ(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ void ScaleSelectedInstance(CPt inOriginalCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ void CalculateNodeGlobalRotation(SNode &inNode);
+
+ void ApplyRotationToSelectedInstance(const QT3DSQuat &inFinalRotation, SNode &inNode,
+ CUpdateableDocumentEditor &inEditor,
+ bool inIsMouseRelative = true);
+
+ void RotateSelectedInstanceAboutCameraDirectionVector(CPt inPreviousMouseCoords,
+ CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ // This method never feels right to me. It is difficult to apply it to a single axis (of
+ // course for that
+ // you can use the inspector palette).
+ void RotateSelectedInstance(CPt inOriginalCoords, CPt inPreviousMouseCoords,
+ CPt inMouseCoords, CUpdateableDocumentEditor &inEditor,
+ bool inLockToAxis);
+
+ Option<SDragPreparationResult>
+ PrepareWidgetDrag(uic::widgets::StudioWidgetComponentIds::Enum inComponentId,
+ uic::widgets::StudioWidgetTypes::Enum inWidgetId,
+ uic::render::RenderWidgetModes::Enum inWidgetMode, SNode &inNode,
+ CPt inOriginalCoords, CPt inPreviousMouseCoords, CPt inMouseCoords);
+
+ void PerformWidgetDrag(int inWidgetSubComponent, CPt inOriginalCoords,
+ CPt inPreviousMouseCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ void PerformGuideDrag(CUICDMGuideHandle inGuide, CPt inPoint,
+ CUpdateableDocumentEditor &inEditor);
+ void CheckGuideInPresentationRect(CUICDMGuideHandle inGuide,
+ CUpdateableDocumentEditor &inEditor);
+
+ void PerformPathDrag(uic::studio::SPathPick &inPathPick, CPt inOriginalCoords,
+ CPt inPreviousMouseCoords, CPt inMouseCoords,
+ CUpdateableDocumentEditor &inEditor);
+
+ // Pulled directly from old studio's Node.cpp.
+ void OnNudge(ENudgeDirection inDirection, int inToolmode, int inFlags,
+ CUpdateableDocumentEditor &inEditor);
+
+ void OnNudgeFinished() { m_KeyRepeat = 0; }
+
+ void RequestRender()
+ {
+ if (m_ComponentSecondsDepth == 0)
+ m_Renderer.RequestRender();
+ }
+
+ void RenderZoomRender(SZoomRender &inRender);
+
+ // IUICRenderNodeFilter
+ bool IncludeNode(const SNode &inNode) override;
+
+ PickTargetAreas::Enum GetPickArea(CPt inPoint);
+
+ SNode *GetEditCameraLayer();
+
+ void ReleaseEffect(UICDM::CUICDMInstanceHandle inInstance);
+ // Create a new translator for this type. Do not add to any maps or anything else.
+ SGraphObjectTranslator *CreateTranslator(UICDM::CUICDMInstanceHandle inInstance);
+ // Returns the canonical translator for a given instance or creates a new translator if none
+ // exist.
+ SGraphObjectTranslator *GetOrCreateTranslator(UICDM::CUICDMInstanceHandle inInstance);
+ // Create a new aliased translator for this type.
+ SGraphObjectTranslator *GetOrCreateTranslator(UICDM::CUICDMInstanceHandle inInstance,
+ UICDM::CUICDMInstanceHandle inAliasInstance);
+ THandleTranslatorPairList &
+ GetTranslatorsForInstance(UICDM::CUICDMInstanceHandle inInstance);
+ UICDM::CUICDMInstanceHandle GetAnchorPoint(SPathPick &inPick);
+ UICDM::CUICDMInstanceHandle GetAnchorPoint(QT3DSU32 inAnchorIndex);
+ };
+
+ struct SDisableUseClearColor
+ {
+ SGraphObjectTranslator *m_SceneTranslator;
+ bool m_PreviousUseClearColor;
+ bool m_DisableUseClearColor;
+
+ SDisableUseClearColor(STranslation &inTranslation, bool disableUseClearColor)
+ : m_SceneTranslator(nullptr)
+ , m_PreviousUseClearColor(false)
+ , m_DisableUseClearColor(disableUseClearColor)
+ {
+ if (m_DisableUseClearColor) {
+ TIdentifier theRoot = inTranslation.m_AssetGraph.GetRoot(0);
+ m_SceneTranslator = inTranslation.GetOrCreateTranslator(theRoot);
+ if (m_SceneTranslator) {
+ SScene &theScene = static_cast<SScene &>(m_SceneTranslator->GetGraphObject());
+ m_PreviousUseClearColor = theScene.m_UseClearColor;
+ SetUseClearColor(false);
+ }
+ }
+ }
+
+ ~SDisableUseClearColor()
+ {
+ if (m_DisableUseClearColor) {
+ SetUseClearColor(m_PreviousUseClearColor);
+ }
+ }
+
+ void SetUseClearColor(bool inUseClearColor)
+ {
+ if (m_SceneTranslator) {
+ SScene &theScene = static_cast<SScene &>(m_SceneTranslator->GetGraphObject());
+ theScene.m_UseClearColor = inUseClearColor;
+ }
+ }
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Render/StudioRotationWidget.cpp b/src/Authoring/Studio/Render/StudioRotationWidget.cpp
new file mode 100644
index 00000000..813cf7ac
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioRotationWidget.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioWidgetImpl.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderVertexBuffer.h"
+#include "UICRenderNode.h"
+#include "foundation/Qt3DSContainers.h"
+#include "UICRenderShaderCodeGeneratorV2.h"
+#include "UICRenderCamera.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "StudioUtils.h"
+
+using namespace uic::widgets;
+
+namespace {
+
+struct SRotationWidget : public SStudioWidgetImpl<StudioWidgetTypes::Rotation>
+{
+ typedef SStudioWidgetImpl<StudioWidgetTypes::Rotation> TBase;
+ NVRenderInputAssembler *m_XAxis;
+ NVRenderInputAssembler *m_YAxis;
+ NVRenderInputAssembler *m_ZAxis;
+ NVRenderInputAssembler *m_CameraAxis;
+ // We use a rect to clear the Z buffer.
+ NVRenderInputAssembler *m_CameraRect;
+
+ NVRenderShaderProgram *m_ZClearShader;
+
+ volatile QT3DSI32 mRefCount;
+
+ SRotationWidget(NVAllocatorCallback &inAlloc)
+ : TBase(inAlloc)
+ , m_XAxis(nullptr)
+ , m_YAxis(nullptr)
+ , m_ZAxis(nullptr)
+ , m_CameraAxis(nullptr)
+ , m_CameraRect(nullptr)
+ , m_ZClearShader(nullptr)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ NVRenderInputAssembler *CreateRing(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, QT3DSVec3 inDirection,
+ QT3DSF32 inInnerRadius, QT3DSF32 inOuterRadius, QT3DSF32 inRingColor,
+ const char *inRingName)
+ {
+ QT3DS_ASSERT(inInnerRadius <= inOuterRadius);
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inRingName);
+ NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName);
+ if (retval) {
+ return retval;
+ }
+
+ TResultVecType theVertexData(m_Allocator, "SRotationWidget::theVertexData");
+
+ QT3DSI32 numSubDivisions = 50;
+ QT3DSF32 arcRad = 360.0f / (QT3DSF32)numSubDivisions;
+ TORAD(arcRad);
+ QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0));
+ if (tempCross.magnitudeSquared() < .05f)
+ tempCross = inDirection.cross(QT3DSVec3(1, 0, 0));
+
+ QT3DSVec3 upDir = inDirection.cross(tempCross);
+ QT3DSVec3 leftDir = upDir.cross(inDirection);
+ upDir.normalize();
+ leftDir.normalize();
+
+ QT3DSF32 ringWidth = inOuterRadius - inInnerRadius;
+ QT3DSF32 ringHalfWidth = ringWidth / 2.0f;
+ QT3DSF32 middleRadius = inInnerRadius + ringHalfWidth;
+
+ for (QT3DSI32 idx = 0, numLooper = numSubDivisions; idx < numLooper; ++idx) {
+ QT3DSF32 startDeg = idx * 360.0f / numSubDivisions;
+ QT3DSF32 endDeg = (idx + 1) * 360.0f / numSubDivisions;
+ QT3DSF32 startRad(startDeg);
+ QT3DSF32 endRad(endDeg);
+ TORAD(startRad);
+ TORAD(endRad);
+ QT3DSF32 startSin = NVSin(startRad);
+ QT3DSF32 endSin = NVSin(endRad);
+ QT3DSF32 startCos = NVCos(startRad);
+ QT3DSF32 endCos = NVCos(endRad);
+
+ QT3DSVec3 startDir = startSin * upDir + startCos * leftDir;
+ QT3DSVec3 endDir = endSin * upDir + endCos * leftDir;
+
+ QT3DSVec3 discStart = startDir * inInnerRadius;
+ QT3DSVec3 discEnd = endDir * inInnerRadius;
+ QT3DSVec3 ringStart = startDir * inOuterRadius;
+ QT3DSVec3 ringEnd = endDir * inOuterRadius;
+
+ QT3DSVec3 middleStart = startDir * (middleRadius);
+ QT3DSVec3 middleEnd = endDir * (middleRadius);
+ QT3DSVec3 middleTopLeft = middleStart + inDirection * ringHalfWidth;
+ QT3DSVec3 middleTopRight = middleStart - inDirection * ringHalfWidth;
+ QT3DSVec3 middleBottomLeft = middleEnd + inDirection * ringHalfWidth;
+ QT3DSVec3 middleBottomRight = middleEnd - inDirection * ringHalfWidth;
+
+ // Now two tris for the ring
+ theVertexData.push_back(QT3DSVec4(discStart, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringStart, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(discEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(discStart, inRingColor));
+ // Two tris for the ring that is perpendicular to the viewer
+ theVertexData.push_back(QT3DSVec4(middleTopLeft, inRingColor));
+ theVertexData.push_back(QT3DSVec4(middleTopRight, inRingColor));
+ theVertexData.push_back(QT3DSVec4(middleBottomRight, inRingColor));
+ theVertexData.push_back(QT3DSVec4(middleBottomRight, inRingColor));
+ theVertexData.push_back(QT3DSVec4(middleBottomLeft, inRingColor));
+ theVertexData.push_back(QT3DSVec4(middleTopLeft, inRingColor));
+ }
+
+ QT3DSU32 stride;
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout(
+ IStudioWidget::GetVertexBufferAttributesAndStride(stride));
+ NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size()));
+ retval = &inWidgetContext.GetOrCreateInputAssembler(
+ theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+
+ return retval;
+ }
+
+ NVRenderInputAssembler *CreateRect(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, QT3DSVec3 inDirection,
+ QT3DSF32 inHalfWidth, const char *inItemName)
+ {
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName);
+ NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName);
+ if (retval) {
+ return retval;
+ }
+
+ QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0));
+ if (tempCross.magnitudeSquared() < .05f)
+ tempCross = inDirection.cross(QT3DSVec3(1, 0, 0));
+
+ QT3DSVec3 upDir = inDirection.cross(tempCross);
+ QT3DSVec3 leftDir = upDir.cross(inDirection);
+
+ TResultVecType theVertexData(m_Allocator, "SRotationWidget::theVertexData");
+
+ theVertexData.push_back(QT3DSVec4(upDir * inHalfWidth, 0.0f));
+ theVertexData.push_back(QT3DSVec4(leftDir * inHalfWidth, 0.0f));
+ theVertexData.push_back(QT3DSVec4(upDir * -1.0f * inHalfWidth, 0.0f));
+
+ theVertexData.push_back(QT3DSVec4(upDir * -1.0f * inHalfWidth, 0.0f));
+ theVertexData.push_back(QT3DSVec4(leftDir * -1.0f * inHalfWidth, 0.0f));
+ theVertexData.push_back(QT3DSVec4(upDir * inHalfWidth, 0.0f));
+
+ QT3DSU32 stride;
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout(
+ IStudioWidget::GetVertexBufferAttributesAndStride(stride));
+ NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size()));
+
+ retval = &inWidgetContext.GetOrCreateInputAssembler(
+ theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+ return retval;
+ }
+
+ NVRenderShaderProgram *CreateZClearShader(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext,
+ const char *inItemName)
+ {
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName);
+ NVRenderShaderProgram *retval = inWidgetContext.GetShader(theItemName);
+ if (retval) {
+ return retval;
+ }
+
+ uic::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator());
+ theGenerator.BeginProgram();
+ uic::render::IShaderStageGenerator &theVertexGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Vertex));
+ uic::render::IShaderStageGenerator &theFragmentGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Fragment));
+ theVertexGenerator.AddIncoming("attr_pos", "vec3");
+ theVertexGenerator.AddUniform("model_view_projection", "mat4");
+ theVertexGenerator.Append("void main() {");
+ theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);");
+ theVertexGenerator.Append("}");
+ theFragmentGenerator.Append("void main() {");
+ theFragmentGenerator.Append("\tgl_FragColor.rgb = vec3(0.0, 0.0, 0.0);");
+ theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;");
+ theFragmentGenerator.Append("}");
+ return inWidgetContext.CompileAndStoreShader(theItemName);
+ }
+
+ static inline QT3DSVec3 ToFixedCameraPos(const QT3DSVec3 &inCameraPos, const SCamera &inCamera)
+ {
+ if (inCamera.m_Flags.IsOrthographic()) {
+ return QT3DSVec3(inCameraPos.x, inCameraPos.y, -600.f);
+ }
+ QT3DSF32 multiplier = -600.f / inCameraPos.z;
+ return inCameraPos * multiplier;
+ }
+
+ void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override
+ {
+ // Widgets have to clear the depth buffer; they shouldn't interact with other components
+ // but they should self-occlude.
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetBlendFunction(qt3ds::render::NVRenderBlendFunctionArgument(
+ qt3ds::render::NVRenderSrcBlendFunc::SrcAlpha,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha,
+ qt3ds::render::NVRenderSrcBlendFunc::One,
+ qt3ds::render::NVRenderDstBlendFunc::OneMinusSrcAlpha));
+ inRenderContext.SetBlendEquation(qt3ds::render::NVRenderBlendEquationArgument(
+ NVRenderBlendEquation::Add, NVRenderBlendEquation::Add));
+
+ float pixelRatio = float(devicePixelRatio());
+ QT3DSF32 theRingRadius = 100.0f * pixelRatio;
+ QT3DSF32 theRingWidth = 2.0f * pixelRatio;
+ QT3DSF32 theRingInner = theRingRadius;
+ QT3DSF32 theRingOuter = theRingRadius + theRingWidth;
+ if (m_XAxis == nullptr) {
+ TBase::SetupRender(inWidgetContext, inRenderContext);
+ m_PickShader = IStudioWidget::CreateWidgetPickShader(inWidgetContext, inRenderContext);
+ ;
+ m_XAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(-1, 0, 0), theRingInner,
+ theRingOuter, 0.0f, "RotationWidgetXAxis");
+ m_YAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, -1, 0), theRingInner,
+ theRingOuter, 0.0f, "RotationWidgetYAxis");
+ m_ZAxis = CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), theRingInner,
+ theRingOuter, 0.0f, "RotationWidgetZAxis");
+ m_CameraAxis =
+ CreateRing(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), theRingInner + 5,
+ theRingOuter + 5, 0.0f, "RotationWidgetCameraAxis");
+ m_CameraRect = CreateRect(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), 200.0f,
+ "RotationWidgetZClear");
+ m_ZClearShader =
+ CreateZClearShader(inWidgetContext, inRenderContext, "RotationWidgetZClear");
+ }
+ QT3DSVec3 theXColor(GetXAxisColor());
+ QT3DSVec3 theYColor(GetYAxisColor());
+ QT3DSVec3 theZColor(GetZAxisColor());
+ QT3DSVec3 theRingColor(QT3DSVec3(.8, .8, .8));
+
+ QT3DSMat44 theMVP = TBase::SetupMVP(inWidgetContext);
+ inRenderContext.SetCullingEnabled(false);
+ QT3DSMat44 theCameraMVP = m_WidgetInfo.m_LayerProjection * m_CameraTranslationScale;
+
+ inRenderContext.SetBlendingEnabled(false);
+ inRenderContext.SetColorWritesEnabled(false);
+ inRenderContext.SetDepthTestEnabled(false);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetActiveShader(m_ZClearShader);
+ inRenderContext.SetInputAssembler(m_CameraRect);
+ m_ZClearShader->SetPropertyValue("model_view_projection", theCameraMVP);
+ inRenderContext.Draw(NVRenderDrawMode::Triangles, m_CameraRect->GetVertexCount(), 0);
+
+ inRenderContext.SetColorWritesEnabled(true);
+ inRenderContext.SetActiveShader(m_Shader);
+ m_Shader->SetPropertyValue("model_view_projection", theCameraMVP);
+ inRenderContext.SetDepthTestEnabled(false);
+ inRenderContext.SetDepthWriteEnabled(false);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::CameraPlane, theRingColor,
+ inRenderContext, m_CameraAxis);
+
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetActiveShader(m_Shader);
+ m_Shader->SetPropertyValue("model_view_projection", theMVP);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext,
+ m_XAxis);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext,
+ m_YAxis);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext,
+ m_ZAxis);
+
+ if (m_RotationWedge.hasValue()) {
+ BeginImmediateDrawing(inWidgetContext, inRenderContext);
+
+ QT3DSMat33 theMVPRotation(m_WidgetInfo.m_CameraGlobalInverse.column0.getXYZ(),
+ m_WidgetInfo.m_CameraGlobalInverse.column1.getXYZ(),
+ m_WidgetInfo.m_CameraGlobalInverse.column2.getXYZ());
+ theMVPRotation = theMVPRotation.getInverse().getTranspose();
+
+ QT3DSVec3 theRotationAxis = theMVPRotation.transform(m_RotationWedge->m_RotationAxis);
+ QT3DSVec3 theStartDirection = theMVPRotation.transform(m_RotationWedge->m_StartDirection);
+ theRotationAxis.normalize();
+ theStartDirection.normalize();
+ QT3DSQuat theRotation(m_RotationWedge->m_Angle, theRotationAxis);
+ QT3DSVec3 theEndDirection = theRotation.rotate(theStartDirection);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetDepthWriteEnabled(false);
+ inRenderContext.SetDepthTestEnabled(false);
+ inRenderContext.SetBlendingEnabled(true);
+ QT3DSVec4 lineColor(1, 1, 1, .7);
+ QT3DSVec4 fillColor(1, 1, 1, .2);
+ switch (m_Highlight) {
+ default:
+ break;
+ case StudioWidgetComponentIds::XAxis:
+ lineColor = QT3DSVec4(theXColor, .7);
+ fillColor = QT3DSVec4(theXColor, .2);
+ break;
+ case StudioWidgetComponentIds::YAxis:
+ lineColor = QT3DSVec4(theYColor, .7);
+ fillColor = QT3DSVec4(theYColor, .2);
+ break;
+ case StudioWidgetComponentIds::ZAxis:
+ lineColor = QT3DSVec4(theZColor, .7);
+ fillColor = QT3DSVec4(theZColor, .2);
+ break;
+ }
+ QT3DSVec3 theStartPos(m_WidgetInfo.m_Position);
+ QT3DSF32 theStartLineLen = theRingOuter * m_WidgetInfo.m_Scale;
+ if (m_Highlight == StudioWidgetComponentIds::CameraPlane)
+ theStartLineLen = (theRingOuter + 5) * m_WidgetInfo.m_Scale;
+ // Get the end line length in camera space.
+ QT3DSVec3 theGlobalStart = m_Node->GetGlobalPivot();
+ QT3DSQuat theGlobalRot(m_RotationWedge->m_Angle, m_RotationWedge->m_RotationAxis);
+ QT3DSVec3 theGlobalDir = theGlobalRot.rotate(m_RotationWedge->m_StartDirection);
+ QT3DSVec3 theGlobalEnd = theGlobalStart + theGlobalDir * m_RotationWedge->m_EndLineLen;
+ // Transform both start, end into camera space and get the length of the resulting
+ // vector
+ QT3DSVec3 theCameraStart = m_WidgetInfo.m_CameraGlobalInverse.transform(theGlobalStart);
+ QT3DSVec3 theCameraEnd = m_WidgetInfo.m_CameraGlobalInverse.transform(theGlobalEnd);
+ QT3DSF32 theCameraEndLineLen = QT3DSVec3(theCameraEnd - theCameraStart).magnitude();
+ QT3DSF32 theEndLineLen = theCameraEndLineLen;
+ // Draw lines in world space
+ SCamera &theCamera(*m_WidgetInfo.m_Camera);
+ QT3DSVec3 lineStart(ToFixedCameraPos(theStartPos, theCamera));
+ QT3DSVec3 startLineEnd(
+ ToFixedCameraPos(theStartPos + theStartDirection * theStartLineLen, theCamera));
+ QT3DSVec3 endLineEnd(ToFixedCameraPos(theCameraEnd, theCamera));
+ DrawImmediateLine(lineStart, startLineEnd, 1.0f, lineColor);
+ DrawImmediateLine(lineStart, endLineEnd, 1.0f, lineColor);
+ DrawFilledArc(theStartPos, theStartDirection, theStartLineLen, theRotationAxis,
+ m_RotationWedge->m_Angle, fillColor);
+ // Now setup the model-view-projection.
+ QT3DSMat44 theProjection = m_WidgetInfo.m_LayerProjection;
+ EndImmediateDrawing(inWidgetContext, inRenderContext, theProjection);
+
+ // Now we attempt to render some text. First we format it.
+
+ char textBuffer[25] = { 0 };
+ QT3DSF32 angleDeg(m_RotationWedge->m_Angle);
+ TODEG(angleDeg);
+ sprintf(textBuffer, "%.1f", angleDeg);
+ STextRenderInfo theInfo;
+ theInfo.m_Text = inRenderContext.GetStringTable().RegisterStr(textBuffer);
+ theInfo.m_HorizontalAlignment = TextHorizontalAlignment::Center;
+ theInfo.m_VerticalAlignment = TextVerticalAlignment::Bottom;
+ theInfo.m_FontSize = 12.0f * pixelRatio;
+ theInfo.m_Font = inRenderContext.GetStringTable().RegisterStr("TitilliumWeb-Regular");
+ QT3DSMat44 theTransMatrix(QT3DSMat44::createIdentity());
+ theTransMatrix.column3.x = endLineEnd.x;
+ theTransMatrix.column3.y = endLineEnd.y;
+ theTransMatrix.column3.z = endLineEnd.z;
+ // We want to scale the text *down* so that it looks better.
+ theTransMatrix.column0[0] = m_WidgetInfo.m_Scale * .8f;
+ theTransMatrix.column1[1] = m_WidgetInfo.m_Scale * .8f;
+ theTransMatrix.column2[2] = m_WidgetInfo.m_Scale * .8f;
+ QT3DSMat44 theTextMVP = theProjection * theTransMatrix;
+ inWidgetContext.RenderText(theInfo, QT3DSVec3(1, 1, 1), QT3DSVec3(.2, .2, .2), theTextMVP);
+ }
+ m_Highlight = StudioWidgetComponentIds::NoId;
+ }
+
+ void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext,
+ uic::render::SWindowDimensions /*inWinDimensions*/) override
+ {
+ if (m_XAxis && m_PickShader) {
+ QT3DSMat44 theCameraMVP =
+ inProjPremult * m_WidgetInfo.m_PureProjection * m_CameraTranslationScale;
+ QT3DSMat44 theMVP = inProjPremult * m_WidgetInfo.m_PureProjection * m_TranslationScale;
+
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetBlendingEnabled(true);
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetActiveShader(m_ZClearShader);
+ m_ZClearShader->SetPropertyValue("model_view_projection", theCameraMVP);
+ inRenderContext.SetInputAssembler(m_CameraRect);
+ inRenderContext.Draw(NVRenderDrawMode::Triangles, m_CameraRect->GetVertexCount(), 0);
+
+ inRenderContext.SetActiveShader(m_PickShader);
+ m_PickShader->SetPropertyValue("model_view_projection", theCameraMVP);
+ RenderPickBuffer(StudioWidgetComponentIds::CameraPlane, m_CameraAxis, inRenderContext);
+
+ m_PickShader->SetPropertyValue("model_view_projection", theMVP);
+
+ RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext);
+ }
+ }
+};
+}
+
+IStudioWidget &IStudioWidget::CreateRotationWidget(NVAllocatorCallback &inAlloc)
+{
+ return *QT3DS_NEW(inAlloc, SRotationWidget)(inAlloc);
+}
diff --git a/src/Authoring/Studio/Render/StudioScaleWidget.cpp b/src/Authoring/Studio/Render/StudioScaleWidget.cpp
new file mode 100644
index 00000000..56b8bb33
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioScaleWidget.cpp
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioWidgetImpl.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderVertexBuffer.h"
+#include "UICRenderNode.h"
+#include "foundation/Qt3DSContainers.h"
+#include "UICRenderShaderCodeGenerator.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "StudioUtils.h"
+
+using namespace uic::widgets;
+
+namespace {
+
+struct SScaleWidget : public SStudioWidgetImpl<StudioWidgetTypes::Scale>
+{
+ typedef SStudioWidgetImpl<StudioWidgetTypes::Scale> TBase;
+
+ NVRenderInputAssembler *m_XAxis;
+ NVRenderInputAssembler *m_YAxis;
+ NVRenderInputAssembler *m_ZAxis;
+
+ NVRenderInputAssembler *m_XPlane;
+ NVRenderInputAssembler *m_YPlane;
+ NVRenderInputAssembler *m_ZPlane;
+
+ volatile QT3DSI32 mRefCount;
+
+ SScaleWidget(NVAllocatorCallback &inAlloc)
+ : TBase(inAlloc)
+ , m_XAxis(nullptr)
+ , m_YAxis(nullptr)
+ , m_ZAxis(nullptr)
+ , m_XPlane(nullptr)
+ , m_YPlane(nullptr)
+ , m_ZPlane(nullptr)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ NVRenderInputAssembler *CreateScaleAxis(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext,
+ const QT3DSVec3 &inDirection, QT3DSF32 inStartOffset,
+ QT3DSF32 inLength, QT3DSF32 inWidth, QT3DSF32 inBoxSideLength,
+ const char *inAxisName)
+ {
+
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inAxisName);
+ NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName);
+ if (retval) {
+ return retval;
+ }
+
+ QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0));
+ if (tempCross.magnitudeSquared() < .05f)
+ tempCross = inDirection.cross(QT3DSVec3(1, 0, 0));
+
+ QT3DSVec3 upDir = inDirection.cross(tempCross);
+ QT3DSVec3 leftDir = upDir.cross(inDirection);
+
+ TResultVecType theVertexData(m_Allocator, "SScaleWidget::theVertexData");
+
+ QT3DSVec3 rectStart = inDirection * inStartOffset;
+ // Rect end is also box start, obviously
+ QT3DSVec3 rectEnd = inDirection * (inStartOffset + inLength);
+ QT3DSVec3 boxEnd = inDirection * (inStartOffset + inLength + inBoxSideLength);
+
+ QT3DSF32 axisHalfWidth = inWidth / 2.0f;
+ // Create the axis
+ CreateRect(rectStart, rectEnd, upDir, axisHalfWidth, 0.0f, theVertexData);
+ CreateRect(rectStart, rectEnd, leftDir, axisHalfWidth, 0.0f, theVertexData);
+ // Create box at the top.
+ QT3DSF32 boxSideHalfLength = inBoxSideLength / 2;
+ // Get the four sides
+ QT3DSVec3 boxRectStart = rectEnd + (leftDir * boxSideHalfLength);
+ QT3DSVec3 boxRectEnd = boxEnd + (leftDir * boxSideHalfLength);
+ CreateRect(boxRectStart, boxRectEnd, upDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ boxRectStart = rectEnd - leftDir * boxSideHalfLength;
+ boxRectEnd = boxEnd - leftDir * boxSideHalfLength;
+ CreateRect(boxRectStart, boxRectEnd, upDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ boxRectStart = rectEnd + upDir * boxSideHalfLength;
+ boxRectEnd = boxEnd + upDir * boxSideHalfLength;
+ CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ boxRectStart = rectEnd - upDir * boxSideHalfLength;
+ boxRectEnd = boxEnd - upDir * boxSideHalfLength;
+ CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ // now create the top and bottom
+ // bottom
+ boxRectStart = rectEnd + upDir * boxSideHalfLength;
+ boxRectEnd = rectEnd - upDir * boxSideHalfLength;
+ CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ // top
+ boxRectStart = boxEnd + upDir * boxSideHalfLength;
+ boxRectEnd = boxEnd - upDir * boxSideHalfLength;
+ CreateRect(boxRectStart, boxRectEnd, leftDir, boxSideHalfLength, 0.0f, theVertexData);
+
+ QT3DSU32 stride;
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout(
+ IStudioWidget::GetVertexBufferAttributesAndStride(stride));
+ NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size()));
+ retval = &inWidgetContext.GetOrCreateInputAssembler(
+ theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+
+ return retval;
+ }
+
+ void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override
+ {
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ float pixelRatio = float(devicePixelRatio());
+ QT3DSF32 axisStart = 20.0f * pixelRatio;
+ QT3DSF32 axisLength = 60.0f * pixelRatio;
+ QT3DSF32 axisTotalLength = axisStart + axisLength;
+ if (m_XAxis == nullptr) {
+ TBase::SetupRender(inWidgetContext, inRenderContext);
+
+ QT3DSF32 axisWidth = 2.0f * pixelRatio;
+ QT3DSF32 triWidth = 7.0f * pixelRatio;
+ m_XAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0), axisStart,
+ axisLength, axisWidth, triWidth, "ScaleWidgetXAxis");
+ m_YAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0), axisStart,
+ axisLength, axisWidth, triWidth, "ScaleWidgetYAxis");
+ m_ZAxis = CreateScaleAxis(inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1), axisStart,
+ axisLength, axisWidth, triWidth, "ScaleWidgetZAxis");
+
+ QT3DSF32 axisPos = GetDiscPos() * pixelRatio;
+ QT3DSF32 axisDiscRadius = GetDiscRadius() * pixelRatio;
+ QT3DSF32 axisRingRadius = GetDiscRingRadius() * pixelRatio;
+ m_XPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0),
+ QT3DSVec3(0, axisPos, -axisPos), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "ScaleWidgetXPlane");
+ m_YPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0),
+ QT3DSVec3(axisPos, 0, -axisPos), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "ScaleWidgetYPlane");
+ m_ZPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1),
+ QT3DSVec3(axisPos, axisPos, 0), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "ScaleWidgetZPlane");
+
+ inRenderContext.SetActiveShader(m_Shader);
+ m_Shader->SetPropertyValue("attr_pos_add_start", axisStart + 1);
+ }
+
+ QT3DSMat44 theMVP = TBase::SetupMVP(inWidgetContext);
+ inRenderContext.SetBlendingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetActiveShader(m_Shader);
+ m_Shader->SetPropertyValue("model_view_projection", theMVP);
+ // temporary set color1 to white so we can hopefully see mistakes.
+ m_Shader->SetPropertyValue("color1", QT3DSVec3(1, 1, 1));
+
+ QT3DSVec3 theXColor(GetXAxisColor());
+ QT3DSVec3 theYColor(GetYAxisColor());
+ QT3DSVec3 theZColor(GetZAxisColor());
+ QT3DSVec3 theRingColor(QT3DSVec3(.8, .8, .8));
+ QT3DSVec3 theEndOffset = QT3DSVec3(axisTotalLength);
+ QT3DSVec3 theScaledEnd = QT3DSVec3(theEndOffset.x * m_AxisScale.x, theEndOffset.y * m_AxisScale.y,
+ theEndOffset.z * m_AxisScale.z);
+ QT3DSVec3 theEndAddition = theScaledEnd - theEndOffset;
+
+ m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(theEndAddition.x, 0, 0));
+ RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext,
+ m_XAxis);
+
+ m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, theEndAddition.y, 0));
+ RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext,
+ m_YAxis);
+
+ m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, 0, -1.0f * theEndAddition.z));
+ RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext,
+ m_ZAxis);
+
+ m_Shader->SetPropertyValue("attr_pos_add_amount", QT3DSVec3(0, 0, 0));
+ RenderTwoToneGeometry(StudioWidgetComponentIds::XPlane, theXColor, theRingColor,
+ inRenderContext, m_XPlane);
+ RenderTwoToneGeometry(StudioWidgetComponentIds::YPlane, theYColor, theRingColor,
+ inRenderContext, m_YPlane);
+ RenderTwoToneGeometry(StudioWidgetComponentIds::ZPlane, theZColor, theRingColor,
+ inRenderContext, m_ZPlane);
+
+ m_Highlight = StudioWidgetComponentIds::NoId;
+ }
+
+ void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext,
+ uic::render::SWindowDimensions /*inWinDimensions*/) override
+ {
+ if (m_XAxis && m_PickShader) {
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetBlendingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetActiveShader(m_PickShader);
+ // The projection premultiplication step moves the viewport around till
+ // it is centered over the mouse and scales everything *post* rendering (to keep
+ // appropriate aspect).
+ QT3DSMat44 theMVP = inProjPremult * m_PureProjection * m_TranslationScale;
+ m_PickShader->SetPropertyValue("model_view_projection", theMVP);
+
+ RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::XPlane, m_XPlane, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::YPlane, m_YPlane, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::ZPlane, m_ZPlane, inRenderContext);
+ }
+ }
+};
+}
+
+IStudioWidget &IStudioWidget::CreateScaleWidget(NVAllocatorCallback &inAlloc)
+{
+ return *QT3DS_NEW(inAlloc, SScaleWidget)(inAlloc);
+}
diff --git a/src/Authoring/Studio/Render/StudioTranslationWidget.cpp b/src/Authoring/Studio/Render/StudioTranslationWidget.cpp
new file mode 100644
index 00000000..d75e8567
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioTranslationWidget.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioWidgetImpl.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "render/Qt3DSRenderContext.h"
+#include "UICRenderShaderCodeGenerator.h"
+#include "UICRenderNode.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+#include "StudioUtils.h"
+
+using namespace uic::widgets;
+
+namespace {
+
+struct STranslationWidget : public SStudioWidgetImpl<StudioWidgetTypes::Translation>
+{
+ typedef SStudioWidgetImpl<StudioWidgetTypes::Translation> TBase;
+ NVRenderInputAssembler *m_XAxis;
+ NVRenderInputAssembler *m_YAxis;
+ NVRenderInputAssembler *m_ZAxis;
+
+ NVRenderInputAssembler *m_XPlane;
+ NVRenderInputAssembler *m_YPlane;
+ NVRenderInputAssembler *m_ZPlane;
+
+ volatile QT3DSI32 mRefCount;
+
+ STranslationWidget(NVAllocatorCallback &inAlloc)
+ : TBase(inAlloc)
+ , m_XAxis(nullptr)
+ , m_YAxis(nullptr)
+ , m_ZAxis(nullptr)
+ , m_XPlane(nullptr)
+ , m_YPlane(nullptr)
+ , m_ZPlane(nullptr)
+ , mRefCount(0)
+ {
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator);
+
+ void Render(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext) override
+ {
+ // Widgets have to clear the depth buffer; they shouldn't interact with other components
+ // but they should self-occlude.
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetDepthTestEnabled(true);
+ if (m_XAxis == nullptr) {
+ TBase::SetupRender(inWidgetContext, inRenderContext);
+ float pixelRatio = float(devicePixelRatio());
+ QT3DSF32 axisStart = 20.0f * pixelRatio;
+ QT3DSF32 axisLength = 60.0f * pixelRatio;
+ QT3DSF32 triLength = 20.0f * pixelRatio;
+ QT3DSF32 axisWidth = 2.0f * pixelRatio;
+ QT3DSF32 triWidth = 7.0f * pixelRatio;
+ m_XAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0),
+ axisStart, axisLength, triLength, axisWidth, triWidth,
+ "TranslationWidgetXAxis");
+ m_YAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0),
+ axisStart, axisLength, triLength, axisWidth, triWidth,
+ "TranslationWidgetYAxis");
+ m_ZAxis = CreateAxis(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1),
+ axisStart, axisLength, triLength, axisWidth, triWidth,
+ "TranslationWidgetZAxis");
+
+ QT3DSF32 axisPos = GetDiscPos() * pixelRatio;
+ QT3DSF32 axisDiscRadius = GetDiscRadius() * pixelRatio;
+ QT3DSF32 axisRingRadius = GetDiscRingRadius() * pixelRatio;
+ m_XPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(1, 0, 0),
+ QT3DSVec3(0, axisPos, -axisPos), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "TranslationWidgetXPlane");
+ m_YPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 1, 0),
+ QT3DSVec3(axisPos, 0, -axisPos), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "TranslationWidgetYPlane");
+ m_ZPlane =
+ CreateRingedDisc(m_Allocator, inWidgetContext, inRenderContext, QT3DSVec3(0, 0, -1),
+ QT3DSVec3(axisPos, axisPos, 0), axisDiscRadius, axisRingRadius, 0.0f,
+ 1.0f, "TranslationWidgetZPlane");
+ }
+ QT3DSMat44 theMVP = TBase::SetupMVP(inWidgetContext);
+ inRenderContext.SetBlendingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetActiveShader(m_Shader);
+ m_Shader->SetPropertyValue("model_view_projection", theMVP);
+ // temporary set color1 to white so we can hopefully see mistakes.
+ m_Shader->SetPropertyValue("color1", QT3DSVec3(1, 1, 1));
+
+ QT3DSVec3 theXColor(GetXAxisColor());
+ QT3DSVec3 theYColor(GetYAxisColor());
+ QT3DSVec3 theZColor(GetZAxisColor());
+ QT3DSVec3 theRingColor(QT3DSVec3(.8, .8, .8));
+
+ RenderSingleToneGeometry(StudioWidgetComponentIds::XAxis, theXColor, inRenderContext,
+ m_XAxis);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::YAxis, theYColor, inRenderContext,
+ m_YAxis);
+ RenderSingleToneGeometry(StudioWidgetComponentIds::ZAxis, theZColor, inRenderContext,
+ m_ZAxis);
+ RenderTwoToneGeometry(StudioWidgetComponentIds::XPlane, theXColor, theRingColor,
+ inRenderContext, m_XPlane);
+ RenderTwoToneGeometry(StudioWidgetComponentIds::YPlane, theYColor, theRingColor,
+ inRenderContext, m_YPlane);
+ RenderTwoToneGeometry(StudioWidgetComponentIds::ZPlane, theZColor, theRingColor,
+ inRenderContext, m_ZPlane);
+ m_Highlight = StudioWidgetComponentIds::NoId;
+ }
+
+ void RenderPick(const QT3DSMat44 &inProjPremult, NVRenderContext &inRenderContext,
+ uic::render::SWindowDimensions /*inWinDimensions*/) override
+ {
+ if (m_XAxis && m_PickShader) {
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.Clear(qt3ds::render::NVRenderClearValues::Depth);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetBlendingEnabled(false);
+ inRenderContext.SetDepthTestEnabled(true);
+ inRenderContext.SetDepthWriteEnabled(true);
+ inRenderContext.SetCullingEnabled(false);
+ inRenderContext.SetActiveShader(m_PickShader);
+ // The projection premultiplication step moves the viewport around till
+ // it is centered over the mouse and scales everything *post* rendering (to keep
+ // appropriate aspect).
+ QT3DSMat44 theMVP = inProjPremult * m_PureProjection * m_TranslationScale;
+ m_PickShader->SetPropertyValue("model_view_projection", theMVP);
+
+ RenderPickBuffer(StudioWidgetComponentIds::XAxis, m_XAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::YAxis, m_YAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::ZAxis, m_ZAxis, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::XPlane, m_XPlane, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::YPlane, m_YPlane, inRenderContext);
+ RenderPickBuffer(StudioWidgetComponentIds::ZPlane, m_ZPlane, inRenderContext);
+ }
+ }
+};
+}
+
+IStudioWidget &IStudioWidget::CreateTranslationWidget(NVAllocatorCallback &inAlloc)
+{
+ return *QT3DS_NEW(inAlloc, STranslationWidget)(inAlloc);
+}
diff --git a/src/Authoring/Studio/Render/StudioWidget.cpp b/src/Authoring/Studio/Render/StudioWidget.cpp
new file mode 100644
index 00000000..4e8e4717
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioWidget.cpp
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "StudioWidget.h"
+#include "UICRenderWidgets.h"
+#include "UICRenderContext.h"
+#include "render/Qt3DSRenderContext.h"
+#include "foundation/Qt3DSContainers.h"
+#include "UICRenderShaderCodeGeneratorV2.h"
+
+using namespace uic::widgets;
+
+void IStudioWidget::CreateRect(QT3DSVec3 rectStart, QT3DSVec3 rectEnd, QT3DSVec3 orth1, QT3DSF32 axisHalfWidth,
+ QT3DSF32 inColorIndex, TResultVecType &outResult)
+{
+ outResult.push_back(QT3DSVec4(rectStart + orth1 * axisHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(rectEnd + orth1 * axisHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(rectEnd - orth1 * axisHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(rectEnd - orth1 * axisHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(rectStart - orth1 * axisHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(rectStart + orth1 * axisHalfWidth, inColorIndex));
+}
+
+void IStudioWidget::CreateTriangle(QT3DSVec3 triStart, QT3DSVec3 triEnd, QT3DSVec3 orth1, QT3DSF32 triHalfWidth,
+ QT3DSF32 inColorIndex, TResultVecType &outResult)
+{
+ outResult.push_back(QT3DSVec4(triStart + orth1 * triHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(triStart - orth1 * triHalfWidth, inColorIndex));
+ outResult.push_back(QT3DSVec4(triEnd, inColorIndex));
+}
+
+NVRenderInputAssembler *IStudioWidget::CreateRingedDisc(
+ NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, QT3DSVec3 inDirection, QT3DSVec3 inCenterPt, QT3DSF32 inInnerRadius,
+ QT3DSF32 inOuterRadius, QT3DSF32 inDiscColor, QT3DSF32 inRingColor, const char *inItemName)
+{
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inItemName);
+ NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName);
+ if (retval) {
+ return retval;
+ }
+ TResultVecType theVertexData(inAllocator, "STranslationWidget::theVertexData");
+ QT3DS_ASSERT(inInnerRadius < inOuterRadius);
+ QT3DSI32 numSubDivisions = 50;
+ QT3DSF32 arcRad = 360.0f / (QT3DSF32)numSubDivisions;
+ TORAD(arcRad);
+ QT3DSVec3 tempCross = inDirection.cross(QT3DSVec3(0, 1, 0));
+ if (tempCross.magnitudeSquared() < .05f)
+ tempCross = inDirection.cross(QT3DSVec3(1, 0, 0));
+
+ QT3DSVec3 upDir = inDirection.cross(tempCross);
+ QT3DSVec3 leftDir = upDir.cross(inDirection);
+ upDir.normalize();
+ leftDir.normalize();
+
+ for (QT3DSI32 idx = 0, numLooper = numSubDivisions; idx < numLooper; ++idx) {
+ QT3DSF32 startDeg = idx * 360.0f / numSubDivisions;
+ QT3DSF32 endDeg = (idx + 1) * 360.0f / numSubDivisions;
+ QT3DSF32 startRad(startDeg);
+ QT3DSF32 endRad(endDeg);
+ TORAD(startRad);
+ TORAD(endRad);
+ QT3DSF32 startSin = NVSin(startRad);
+ QT3DSF32 endSin = NVSin(endRad);
+ QT3DSF32 startCos = NVCos(startRad);
+ QT3DSF32 endCos = NVCos(endRad);
+
+ QT3DSVec3 startDir = startSin * upDir + startCos * leftDir;
+ QT3DSVec3 endDir = endSin * upDir + endCos * leftDir;
+
+ QT3DSVec3 discStart = inCenterPt + startDir * inInnerRadius;
+ QT3DSVec3 discEnd = inCenterPt + endDir * inInnerRadius;
+ QT3DSVec3 ringStart = inCenterPt + startDir * inOuterRadius;
+ QT3DSVec3 ringEnd = inCenterPt + endDir * inOuterRadius;
+
+ // Create the Triangles
+ // disc first
+ theVertexData.push_back(QT3DSVec4(inCenterPt, inDiscColor));
+ theVertexData.push_back(QT3DSVec4(discStart, inDiscColor));
+ theVertexData.push_back(QT3DSVec4(discEnd, inDiscColor));
+
+ // Now two tris for the ring
+ theVertexData.push_back(QT3DSVec4(discStart, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringStart, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(ringEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(discEnd, inRingColor));
+ theVertexData.push_back(QT3DSVec4(discStart, inRingColor));
+ }
+
+ QT3DSU32 stride;
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout(
+ IStudioWidget::GetVertexBufferAttributesAndStride(stride));
+ NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size()));
+
+ retval = &inWidgetContext.GetOrCreateInputAssembler(
+ theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+
+ return retval;
+}
+
+// Create an axis with a triangle at the top. Really we create two axis that are orthogonal to each
+// other.
+NVRenderInputAssembler *
+IStudioWidget::CreateAxis(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, const QT3DSVec3 &inAxisDirection,
+ QT3DSF32 inAxisStartOffset, QT3DSF32 inAxisLength, QT3DSF32 inTriLength,
+ QT3DSF32 inAxisWidth, QT3DSF32 inTriWidth, const char *inAxisName)
+{
+ CRegisteredString theItemName = inRenderContext.GetStringTable().RegisterStr(inAxisName);
+ NVRenderInputAssembler *retval = inWidgetContext.GetInputAssembler(theItemName);
+ if (retval) {
+ return retval;
+ }
+
+ TResultVecType theVertexData(inAllocator, "STranslationWidget::theVertexData");
+ QT3DSVec3 orth1 = inAxisDirection.cross(QT3DSVec3(0, 0, 1));
+ if (orth1.magnitudeSquared() < .05f)
+ orth1 = inAxisDirection.cross(QT3DSVec3(0, 1, 0));
+ QT3DSVec3 orth2 = inAxisDirection.cross(orth1);
+
+ // Draw a rect that starts at inAxisStartOffset
+ QT3DSVec3 rectStart = inAxisDirection * inAxisStartOffset;
+ // Rect end is also tri start, obviously
+ QT3DSVec3 rectEnd = inAxisDirection * (inAxisStartOffset + inAxisLength);
+ QT3DSVec3 triEnd = inAxisDirection * (inAxisStartOffset + inAxisLength + inTriLength);
+
+ QT3DSF32 axisHalfWidth = inAxisWidth / 2.0f;
+ QT3DSF32 triHalfWidth = inTriWidth / 2.0f;
+ CreateRect(rectStart, rectEnd, orth1, axisHalfWidth, 0.0f, theVertexData);
+ CreateTriangle(rectEnd, triEnd, orth1, triHalfWidth, 0.0f, theVertexData);
+ CreateRect(rectStart, rectEnd, orth2, axisHalfWidth, 0.0f, theVertexData);
+ CreateTriangle(rectEnd, triEnd, orth2, triHalfWidth, 0.0f, theVertexData);
+
+ QT3DSU32 stride;
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout = &inWidgetContext.CreateAttributeLayout(
+ IStudioWidget::GetVertexBufferAttributesAndStride(stride));
+ NVRenderVertexBuffer *theVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theItemName, stride, toU8DataRef(theVertexData.begin(), theVertexData.size()));
+
+ retval = &inWidgetContext.GetOrCreateInputAssembler(
+ theItemName, theAttribLayout, toConstDataRef(&theVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+
+ return retval;
+};
+
+SWidgetRenderSetupResult::SWidgetRenderSetupResult(IRenderWidgetContext &inWidgetContext,
+ SNode &inNode,
+ RenderWidgetModes::Enum inWidgetMode)
+{
+ m_WidgetInfo =
+ inWidgetContext.GetWidgetRenderInformation(inNode, QT3DSVec3(0, 0, 0), inWidgetMode);
+ QT3DSMat44 theTranslationScale(QT3DSMat44::createIdentity());
+ QT3DSMat33 theRotationMult(QT3DSMat33::createIdentity());
+ bool includeNodeRotation = inWidgetMode == RenderWidgetModes::Local ? true : false;
+ if (includeNodeRotation) {
+ QT3DSMat44 theNodeRotation;
+ inNode.CalculateRotationMatrix(theNodeRotation);
+ if (inNode.m_Flags.IsLeftHanded()) {
+ SNode::FlipCoordinateSystem(theNodeRotation);
+ }
+ theRotationMult =
+ QT3DSMat33(theNodeRotation.column0.getXYZ(), theNodeRotation.column1.getXYZ(),
+ theNodeRotation.column2.getXYZ());
+ }
+
+ QT3DSMat33 theRotationMatrix = m_WidgetInfo.m_NormalMatrix * theRotationMult;
+ QT3DSMat33 theScaleMatrix(QT3DSMat33::createIdentity());
+ theScaleMatrix.column0[0] = m_WidgetInfo.m_Scale;
+ theScaleMatrix.column1[1] = m_WidgetInfo.m_Scale;
+ theScaleMatrix.column2[2] = m_WidgetInfo.m_Scale;
+ QT3DSMat33 theCombined = theRotationMatrix * theScaleMatrix;
+ theTranslationScale.column0 = QT3DSVec4(theCombined.column0, 0.0f);
+ theTranslationScale.column1 = QT3DSVec4(theCombined.column1, 0.0f);
+ theTranslationScale.column2 = QT3DSVec4(theCombined.column2, 0.0f);
+ theTranslationScale.column3.x = m_WidgetInfo.m_Position.x;
+ theTranslationScale.column3.y = m_WidgetInfo.m_Position.y;
+ theTranslationScale.column3.z = m_WidgetInfo.m_Position.z;
+ m_TranslationScale = theTranslationScale;
+ m_PureProjection = m_WidgetInfo.m_PureProjection;
+
+ QT3DSMat44 theCameraTransScale(QT3DSMat44::createIdentity());
+ theCameraTransScale.column0 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column0, 0.0f);
+ theCameraTransScale.column1 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column1, 0.0f);
+ theCameraTransScale.column2 = QT3DSVec4(m_WidgetInfo.m_LookAtMatrix.column2, 0.0f);
+ theCameraTransScale.column3.x = m_WidgetInfo.m_Position.x;
+ theCameraTransScale.column3.y = m_WidgetInfo.m_Position.y;
+ theCameraTransScale.column3.z = m_WidgetInfo.m_Position.z;
+ theCameraTransScale.column0[0] = m_WidgetInfo.m_Scale;
+ theCameraTransScale.column1[1] = m_WidgetInfo.m_Scale;
+ theCameraTransScale.column2[2] = m_WidgetInfo.m_Scale;
+ m_CameraTranslationScale = theCameraTransScale;
+ m_SetupResult = m_WidgetInfo.m_LayerProjection * theTranslationScale;
+}
+
+CRegisteredString IStudioWidget::GetSharedShaderName(IStringTable &inStrTable)
+{
+ return inStrTable.RegisterStr("IStudioWidget Shader");
+}
+
+CRegisteredString IStudioWidget::GetSharedPickShaderName(IStringTable &inStrTable)
+{
+ return inStrTable.RegisterStr("IStudioWidget Pick Shader");
+}
+
+NVRenderShaderProgram *IStudioWidget::CreateWidgetShader(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext)
+{
+ CRegisteredString theSharedName(GetSharedShaderName(inRenderContext.GetStringTable()));
+ NVRenderShaderProgram *retval = inWidgetContext.GetShader(theSharedName);
+ if (retval)
+ return retval;
+ uic::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator());
+ theGenerator.BeginProgram();
+ uic::render::IShaderStageGenerator &theVertexGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Vertex));
+ uic::render::IShaderStageGenerator &theFragmentGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Fragment));
+ theVertexGenerator.AddIncoming("attr_pos", "vec3");
+ theVertexGenerator.AddIncoming("attr_color_index", "float");
+ theVertexGenerator.AddOutgoing("output_color_index", "float");
+ theVertexGenerator.AddUniform("model_view_projection", "mat4");
+ // These are required in order to scale the scale widget the way we want to scale it.
+ theVertexGenerator.AddUniform("attr_pos_add_start", "float");
+ theVertexGenerator.AddUniform("attr_pos_add_amount", "vec3");
+ theVertexGenerator.Append("void main() {");
+ theVertexGenerator
+ << "\tvec3 thePos = attr_pos;" << Endl
+ << "\tif ( length(thePos) > attr_pos_add_start ) thePos = thePos + attr_pos_add_amount;"
+ << Endl;
+ theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(thePos, 1.0);");
+ theVertexGenerator.Append("\toutput_color_index = attr_color_index;");
+ theVertexGenerator.Append("}");
+ theFragmentGenerator.AddUniform("color0", "vec3");
+ theFragmentGenerator.AddUniform("color1", "vec3");
+ theFragmentGenerator.Append("void main() {");
+ theFragmentGenerator.Append("\tgl_FragColor.rgb = output_color_index > 0.0 ? color1 : color0;");
+ theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;");
+ theFragmentGenerator.Append("}");
+ return inWidgetContext.CompileAndStoreShader(theSharedName);
+}
+
+NVRenderShaderProgram *IStudioWidget::CreateWidgetPickShader(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext)
+{
+ CRegisteredString theSharedName(GetSharedPickShaderName(inRenderContext.GetStringTable()));
+ NVRenderShaderProgram *retval = inWidgetContext.GetShader(theSharedName);
+ if (retval)
+ return retval;
+ uic::render::IShaderProgramGenerator &theGenerator(inWidgetContext.GetProgramGenerator());
+ theGenerator.BeginProgram();
+ uic::render::IShaderStageGenerator &theVertexGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Vertex));
+ uic::render::IShaderStageGenerator &theFragmentGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Fragment));
+ theVertexGenerator.AddIncoming("attr_pos", "vec3");
+ theVertexGenerator.AddUniform("model_view_projection", "mat4");
+ theVertexGenerator.Append("void main() {");
+ theVertexGenerator.Append("\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);");
+ theVertexGenerator.Append("}");
+ theFragmentGenerator.AddUniform("object_id", "int");
+ theFragmentGenerator.Append("void main() {");
+ theFragmentGenerator.Append("\tgl_FragColor.r = float(object_id % 256)/255.0;");
+ theFragmentGenerator.Append("\tgl_FragColor.g = float(object_id / 256)/255.0;");
+ theFragmentGenerator.Append("\tgl_FragColor.b = 0;");
+ theFragmentGenerator.Append("\tgl_FragColor.a = 1.0;");
+ theFragmentGenerator.Append("}");
+ return inWidgetContext.CompileAndStoreShader(theSharedName);
+}
+
+NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry>
+IStudioWidget::GetVertexBufferAttributesAndStride(QT3DSU32 &stride)
+{
+ static qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry("attr_pos", qt3ds::render::NVRenderComponentTypes::QT3DSF32,
+ 3),
+ qt3ds::render::NVRenderVertexBufferEntry("attr_color_index",
+ qt3ds::render::NVRenderComponentTypes::QT3DSF32, 1, 12),
+ };
+
+ stride = 3 * sizeof(QT3DSF32) + 1 * sizeof(QT3DSF32);
+
+ return toConstDataRef(theEntries, 2);
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/Render/StudioWidget.h b/src/Authoring/Studio/Render/StudioWidget.h
new file mode 100644
index 00000000..030e6836
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioWidget.h
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_RENDERER_WIDGET_H
+#define UIC_STUDIO_RENDERER_WIDGET_H
+#pragma once
+#include "UICRenderWindowDimensions.h"
+#include "UICRenderWidgets.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "StudioPickValues.h"
+
+namespace uic {
+namespace widgets {
+ using namespace uic::render;
+
+ struct StudioWidgetTypes
+ {
+ enum Enum {
+ Unknown,
+ Translation,
+ Scale,
+ Rotation,
+ };
+ };
+
+ // These are also the ids used as colors in the pick image.
+ struct StudioWidgetComponentIds
+ {
+ enum Enum {
+ NoId = 0,
+ XAxis,
+ YAxis,
+ ZAxis,
+ XPlane,
+ YPlane,
+ ZPlane,
+ CameraPlane,
+ LastId,
+ };
+ };
+
+ // Functionality shared between the path widget and the various manipulation gadgets
+ class IStudioWidgetBase : public IRenderWidget, public NVRefCounted
+ {
+ public:
+ virtual void SetNode(SNode &inNode) = 0;
+ virtual void RenderPick(const QT3DSMat44 &inProjPreMult, NVRenderContext &inRenderContext,
+ uic::render::SWindowDimensions inWinDimensions) = 0;
+ virtual uic::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) = 0;
+ };
+
+ typedef nvvector<QT3DSVec4> TResultVecType;
+
+ struct SWidgetRenderSetupResult
+ {
+ QT3DSMat44 m_TranslationScale;
+ QT3DSMat44 m_CameraTranslationScale;
+ QT3DSMat44 m_PureProjection;
+ SWidgetRenderInformation m_WidgetInfo;
+ QT3DSMat44 m_SetupResult;
+
+ SWidgetRenderSetupResult() {}
+ SWidgetRenderSetupResult(IRenderWidgetContext &inWidgetContext, SNode &inNode,
+ RenderWidgetModes::Enum inWidgetMode);
+ };
+
+ class IStudioWidget : public IStudioWidgetBase
+ {
+ public:
+ static CRegisteredString GetSharedShaderName(IStringTable &inStrTable);
+ static CRegisteredString GetSharedPickShaderName(IStringTable &inStrTable);
+ static NVRenderShaderProgram *CreateWidgetShader(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext);
+ static NVRenderShaderProgram *CreateWidgetPickShader(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext);
+ static NVConstDataRef<qt3ds::render::NVRenderVertexBufferEntry>
+ GetVertexBufferAttributesAndStride(QT3DSU32 &stride);
+ static NVRenderInputAssembler *
+ CreateRingedDisc(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, QT3DSVec3 inDirection, QT3DSVec3 inCenterPt,
+ QT3DSF32 inInnerRadius, QT3DSF32 inOuterRadius, QT3DSF32 inDiscColor,
+ QT3DSF32 inRingColor, const char *inItemName);
+ static NVRenderInputAssembler *
+ CreateAxis(NVAllocatorCallback &inAllocator, IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, const QT3DSVec3 &inAxisDirection,
+ QT3DSF32 inAxisStartOffset, QT3DSF32 inAxisLength, QT3DSF32 inTriLength,
+ QT3DSF32 inAxisWidth, QT3DSF32 inTriWidth, const char *inAxisName);
+ static void CreateRect(QT3DSVec3 rectStart, QT3DSVec3 rectEnd, QT3DSVec3 orth1, QT3DSF32 axisHalfWidth,
+ QT3DSF32 inColorIndex, TResultVecType &outResult);
+ static void CreateTriangle(QT3DSVec3 triStart, QT3DSVec3 triEnd, QT3DSVec3 orth1, QT3DSF32 triHalfWidth,
+ QT3DSF32 inColorIndex, TResultVecType &outResult);
+
+ void SetNode(SNode &inNode) override = 0;
+ virtual StudioWidgetTypes::Enum GetWidgetType() const = 0;
+ virtual void SetSubComponentId(int inSubComponentId) = 0;
+ virtual void SetRenderWidgetMode(RenderWidgetModes::Enum inSpace) = 0;
+ virtual RenderWidgetModes::Enum GetRenderWidgetMode() const = 0;
+ // When we render the axis, we can scale the axis item itself
+ virtual void SetAxisScale(const QT3DSVec3 &inNewScale) = 0;
+ // Set the start/end positions of the rotation arc so the rotation gadget can show
+ // the angle and optionally display an angle readout. The start direction should
+ // be a normalized direction in world space, and the angle should be in radians
+ // inRotationAxis is expected to be a normalized direction in world space.
+ virtual void SetRotationEdges(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis,
+ QT3DSF32 inAngleRad, QT3DSF32 inEndLineLen) = 0;
+ virtual void ClearRotationEdges() = 0;
+
+ static IStudioWidget &CreateTranslationWidget(NVAllocatorCallback &inAlloc);
+ static IStudioWidget &CreateRotationWidget(NVAllocatorCallback &inAlloc);
+ static IStudioWidget &CreateScaleWidget(NVAllocatorCallback &inAlloc);
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Render/StudioWidgetImpl.h b/src/Authoring/Studio/Render/StudioWidgetImpl.h
new file mode 100644
index 00000000..a00f4fcd
--- /dev/null
+++ b/src/Authoring/Studio/Render/StudioWidgetImpl.h
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIC_STUDIO_RENDERER_WIDGET_IMPL_H
+#define UIC_STUDIO_RENDERER_WIDGET_IMPL_H
+#pragma once
+
+#include "StudioWidget.h"
+#include "foundation/Qt3DSContainers.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+
+#include "UICRenderNode.h"
+#include "UICRenderShaderCodeGeneratorV2.h"
+
+namespace uic {
+namespace widgets {
+
+ typedef nvvector<QT3DSVec4> TResultVecType;
+
+ struct SRotationWedge
+ {
+ QT3DSVec3 m_StartDirection; // world space position
+ QT3DSVec3 m_RotationAxis;
+ QT3DSF32 m_Angle; // angle in radians.
+ QT3DSF32 m_EndLineLen;
+ SRotationWedge() {}
+ SRotationWedge(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis, QT3DSF32 inAngle,
+ QT3DSF32 inEndLineLen)
+ : m_StartDirection(inStartDirection)
+ , m_RotationAxis(inRotationAxis)
+ , m_Angle(inAngle)
+ , m_EndLineLen(inEndLineLen)
+ {
+ }
+ };
+
+ struct SImmediateVertex
+ {
+ QT3DSVec3 m_Position;
+ QT3DSVec4 m_Color;
+ SImmediateVertex(const QT3DSVec3 &inPosition, const QT3DSVec4 &inColor)
+ : m_Position(inPosition)
+ , m_Color(inColor)
+ {
+ }
+ SImmediateVertex() {}
+ };
+
+ template <StudioWidgetTypes::Enum TWidgetType>
+ struct SStudioWidgetImpl : public IStudioWidget
+ {
+ NVAllocatorCallback &m_Allocator;
+ NVRenderShaderProgram *m_Shader;
+ NVRenderShaderProgram *m_PickShader;
+ QT3DSMat44 m_TranslationScale;
+ QT3DSMat44 m_CameraTranslationScale;
+ QT3DSMat44 m_PureProjection;
+ SWidgetRenderInformation m_WidgetInfo;
+ StudioWidgetComponentIds::Enum m_Highlight;
+ RenderWidgetModes::Enum m_WidgetMode;
+
+ QT3DSVec3 m_AxisScale;
+ Option<SRotationWedge> m_RotationWedge;
+ nvvector<SImmediateVertex> m_ImmediateBuffer;
+ NVRenderVertexBuffer *m_ImmediateVertexBuffer;
+ NVRenderInputAssembler *m_ImmediateInputAssembler;
+ NVRenderShaderProgram *m_ImmediateShader;
+
+ SStudioWidgetImpl(NVAllocatorCallback &inAlloc)
+ : m_Allocator(inAlloc)
+ , m_Shader(NULL)
+ , m_PickShader(NULL)
+ , m_Highlight(StudioWidgetComponentIds::NoId)
+ , m_WidgetMode(RenderWidgetModes::Local)
+ , m_AxisScale(QT3DSVec3(1, 1, 1))
+ , m_ImmediateBuffer(m_Allocator, "STranslationWidget::theVertexData")
+ , m_ImmediateVertexBuffer(NULL)
+ , m_ImmediateInputAssembler(NULL)
+ , m_ImmediateShader(NULL)
+ {
+ }
+
+ void SetNode(SNode &inNode) override { m_Node = &inNode; }
+
+ void SetSubComponentId(int inId) override
+ {
+ if (inId > 0 && inId < (int)StudioWidgetComponentIds::LastId)
+ m_Highlight = static_cast<StudioWidgetComponentIds::Enum>(inId);
+ else
+ m_Highlight = StudioWidgetComponentIds::NoId;
+ }
+
+ StudioWidgetTypes::Enum GetWidgetType() const override { return TWidgetType; }
+ uic::studio::SStudioPickValue PickIndexToPickValue(QT3DSU32 inPickIndex) override
+ {
+ return uic::studio::SWidgetPick((QT3DSI32)inPickIndex);
+ }
+
+ void SetupRender(IRenderWidgetContext &inWidgetContext, NVRenderContext &inRenderContext)
+ {
+ m_Shader = IStudioWidget::CreateWidgetShader(inWidgetContext, inRenderContext);
+ m_PickShader = IStudioWidget::CreateWidgetPickShader(inWidgetContext, inRenderContext);
+ }
+
+ QT3DSMat44 SetupMVP(IRenderWidgetContext &inWidgetContext)
+ {
+ SWidgetRenderSetupResult theSetup(inWidgetContext, *m_Node, m_WidgetMode);
+ m_TranslationScale = theSetup.m_TranslationScale;
+ m_CameraTranslationScale = theSetup.m_CameraTranslationScale;
+ m_PureProjection = theSetup.m_PureProjection;
+ m_WidgetInfo = theSetup.m_WidgetInfo;
+ return theSetup.m_SetupResult;
+ }
+
+ void RenderSingleToneGeometry(StudioWidgetComponentIds::Enum inId,
+ const QT3DSVec3 &inOriginalColor,
+ NVRenderContext &inRenderContext,
+ NVRenderInputAssembler *inGeometryAssembly)
+ {
+ bool isHighlighted = inId == m_Highlight;
+ QT3DSVec3 theColor = isHighlighted ? QT3DSVec3(1, 1, 0) : inOriginalColor;
+
+ m_Shader->SetPropertyValue("color0", theColor);
+ inRenderContext.SetInputAssembler(inGeometryAssembly);
+ inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles,
+ inGeometryAssembly->GetVertexCount(), 0);
+ };
+
+ void RenderTwoToneGeometry(StudioWidgetComponentIds::Enum inId, QT3DSVec3 inColor0,
+ QT3DSVec3 inColor1, NVRenderContext &inRenderContext,
+ NVRenderInputAssembler *inGeometryAssembly)
+ {
+ bool isHighlighted = inId == m_Highlight;
+ if (isHighlighted) {
+ inColor0 = inColor1 = QT3DSVec3(1, 1, 0);
+ }
+
+ m_Shader->SetPropertyValue("color0", inColor0);
+ m_Shader->SetPropertyValue("color1", inColor1);
+ inRenderContext.SetInputAssembler(inGeometryAssembly);
+ inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles,
+ inGeometryAssembly->GetVertexCount(), 0);
+ };
+
+ void SetRenderWidgetMode(RenderWidgetModes::Enum inSpace) override { m_WidgetMode = inSpace; }
+
+ RenderWidgetModes::Enum GetRenderWidgetMode() const override { return m_WidgetMode; }
+
+ void RenderPickBuffer(StudioWidgetComponentIds::Enum inId,
+ NVRenderInputAssembler *inGeometryAssembly,
+ NVRenderContext &inRenderContext)
+ {
+ QT3DSI32 theObjectId = inId;
+ m_PickShader->SetPropertyValue("object_id", theObjectId);
+ inRenderContext.SetInputAssembler(inGeometryAssembly);
+ inRenderContext.Draw(qt3ds::render::NVRenderDrawMode::Triangles,
+ inGeometryAssembly->GetVertexCount(), 0);
+ }
+
+ void BeginImmediateDrawing(IRenderWidgetContext &, NVRenderContext &)
+ {
+ m_ImmediateBuffer.clear();
+ }
+
+ void DrawImmediateRect(const QT3DSVec3 &rectStart, const QT3DSVec3 &rectEnd, const QT3DSVec3 &orth1,
+ QT3DSF32 axisHalfWidth, const QT3DSVec4 &inColor)
+ {
+ StaticAssert<sizeof(SImmediateVertex) == 7 * sizeof(QT3DSF32)>::valid_expression();
+ m_ImmediateBuffer.push_back(
+ SImmediateVertex(rectStart + orth1 * axisHalfWidth, inColor));
+ m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd + orth1 * axisHalfWidth, inColor));
+ m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd - orth1 * axisHalfWidth, inColor));
+ m_ImmediateBuffer.push_back(SImmediateVertex(rectEnd - orth1 * axisHalfWidth, inColor));
+ m_ImmediateBuffer.push_back(
+ SImmediateVertex(rectStart - orth1 * axisHalfWidth, inColor));
+ m_ImmediateBuffer.push_back(
+ SImmediateVertex(rectStart + orth1 * axisHalfWidth, inColor));
+ }
+
+ void DrawImmediateLine(const QT3DSVec3 &inStart, const QT3DSVec3 &inEnd, QT3DSF32 inWidth,
+ const QT3DSVec4 &inColor)
+ {
+ QT3DSVec3 theDir = inEnd - inStart;
+ theDir.normalize();
+ QT3DSVec3 theTemp = theDir.cross(QT3DSVec3(0, 0, 1));
+ QT3DSF32 theTempLen = theTemp.normalize();
+ if (theTempLen < .01f) {
+ theTemp = theDir.cross(QT3DSVec3(0, 1, 0));
+ theTemp.normalize();
+ }
+ QT3DSVec3 rectStart(inStart);
+ QT3DSVec3 rectEnd(inEnd);
+ QT3DSVec3 orth1 = theDir.cross(theTemp);
+ QT3DSVec3 orth2 = orth1.cross(theDir);
+ orth1.normalize();
+ orth2.normalize();
+ QT3DSF32 axisHalfWidth = inWidth / 2.0f;
+ DrawImmediateRect(rectStart, rectEnd, orth1, axisHalfWidth, inColor);
+ DrawImmediateRect(rectStart, rectEnd, orth2, axisHalfWidth, inColor);
+ }
+
+ void DrawFilledArc(const QT3DSVec3 &inStartPos, const QT3DSVec3 &inStartDirection, QT3DSF32 inArcLen,
+ const QT3DSVec3 &inRotationAxis, QT3DSF32 inAngle, const QT3DSVec4 &inFillColor)
+ {
+ // 25 small triangles per 180 degrees
+ QT3DSF32 arcLen = (QT3DSF32)(M_PI / 25.0f);
+ QT3DSU32 increments = NVMax((QT3DSU32)1, (QT3DSU32)((fabs(inArcLen) / arcLen) + .5f));
+ QT3DSF32 angleMultiplier = inAngle / (QT3DSF32)increments;
+ for (QT3DSU32 idx = 0; idx < increments; ++idx) {
+ QT3DSF32 localAngle = angleMultiplier * idx;
+ QT3DSF32 nextAngle = angleMultiplier * (idx + 1);
+ QT3DSQuat theQuat(localAngle, inRotationAxis);
+ QT3DSQuat nextQuat(nextAngle, inRotationAxis);
+ QT3DSVec3 startDir = theQuat.rotate(inStartDirection);
+ QT3DSVec3 endDir = nextQuat.rotate(inStartDirection);
+ QT3DSVec3 arcStart = inStartPos + (startDir * inArcLen);
+ QT3DSVec3 arcEnd = inStartPos + (endDir * inArcLen);
+ m_ImmediateBuffer.push_back(SImmediateVertex(inStartPos, inFillColor));
+ m_ImmediateBuffer.push_back(SImmediateVertex(arcStart, inFillColor));
+ m_ImmediateBuffer.push_back(SImmediateVertex(arcEnd, inFillColor));
+ }
+ }
+
+ void EndImmediateDrawing(IRenderWidgetContext &inWidgetContext,
+ NVRenderContext &inRenderContext, const QT3DSMat44 &inProjection)
+ {
+ static qt3ds::render::NVRenderVertexBufferEntry theEntries[] = {
+ qt3ds::render::NVRenderVertexBufferEntry("attr_pos",
+ qt3ds::render::NVRenderComponentTypes::QT3DSF32, 3),
+ qt3ds::render::NVRenderVertexBufferEntry(
+ "attr_color", qt3ds::render::NVRenderComponentTypes::QT3DSF32, 4, 12),
+ };
+
+ if (m_ImmediateBuffer.empty())
+ return;
+
+ CRegisteredString theShaderName(
+ inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmedateShader"));
+ m_ImmediateShader = inWidgetContext.GetShader(theShaderName);
+
+ if (m_ImmediateShader == nullptr) {
+ uic::render::IShaderProgramGenerator &theGenerator(
+ inWidgetContext.GetProgramGenerator());
+ theGenerator.BeginProgram();
+ uic::render::IShaderStageGenerator &theVertexGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Vertex));
+ uic::render::IShaderStageGenerator &theFragmentGenerator(
+ *theGenerator.GetStage(uic::render::ShaderGeneratorStages::Fragment));
+ theVertexGenerator.AddIncoming("attr_pos", "vec3");
+ theVertexGenerator.AddIncoming("attr_color", "vec4");
+ theVertexGenerator.AddUniform("model_view_projection", "mat4");
+ theVertexGenerator.AddOutgoing("vertex_color", "vec4");
+ theVertexGenerator.Append("void main() {");
+ theVertexGenerator.Append(
+ "\tgl_Position = model_view_projection * vec4(attr_pos, 1.0);");
+ theVertexGenerator.Append("\tvertex_color = attr_color;");
+ theVertexGenerator.Append("}");
+ theFragmentGenerator.Append("void main() {");
+ theFragmentGenerator.Append("\tgl_FragColor = vertex_color;");
+ theFragmentGenerator.Append("}");
+
+ m_ImmediateShader = inWidgetContext.CompileAndStoreShader(theShaderName);
+ }
+
+ CRegisteredString theBufferName =
+ inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmediateBuffer");
+ m_ImmediateVertexBuffer = &inWidgetContext.GetOrCreateVertexBuffer(
+ theBufferName, 3 * sizeof(QT3DSF32) + 4 * sizeof(QT3DSF32),
+ toU8DataRef(m_ImmediateBuffer.begin(), m_ImmediateBuffer.size()));
+
+ if (!m_ImmediateInputAssembler) {
+ QT3DSU32 stride = m_ImmediateVertexBuffer->GetStride();
+ QT3DSU32 offset = 0;
+ NVRenderAttribLayout *theAttribLayout =
+ &inWidgetContext.CreateAttributeLayout(toConstDataRef(theEntries, 2));
+
+ CRegisteredString theString =
+ inRenderContext.GetStringTable().RegisterStr("StudioWidgetImmediateBuffer");
+ m_ImmediateInputAssembler = &inWidgetContext.GetOrCreateInputAssembler(
+ theString, theAttribLayout, toConstDataRef(&m_ImmediateVertexBuffer, 1), nullptr,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+ }
+
+ if (m_ImmediateShader && m_ImmediateInputAssembler) {
+ inRenderContext.SetActiveShader(m_ImmediateShader);
+ m_ImmediateShader->SetPropertyValue("model_view_projection", inProjection);
+ inRenderContext.SetInputAssembler(m_ImmediateInputAssembler);
+ inRenderContext.Draw(NVRenderDrawMode::Triangles,
+ m_ImmediateInputAssembler->GetVertexCount(), 0);
+ }
+ }
+
+ void SetAxisScale(const QT3DSVec3 &inAxisScale) override { m_AxisScale = inAxisScale; }
+
+ void SetRotationEdges(const QT3DSVec3 &inStartDirection, const QT3DSVec3 &inRotationAxis,
+ QT3DSF32 inAngleRad, QT3DSF32 inEndLineLen) override
+ {
+ m_RotationWedge =
+ SRotationWedge(inStartDirection, inRotationAxis, inAngleRad, inEndLineLen);
+ }
+
+ void ClearRotationEdges() override { m_RotationWedge = Empty(); }
+
+ static inline QT3DSF32 ToGLSLColor(QT3DSU32 inItem) { return (QT3DSF32)inItem * 1.0f / 255.0f; }
+ static inline QT3DSVec3 ToGLSLColor(QT3DSU32 R, QT3DSU32 G, QT3DSU32 B)
+ {
+ return QT3DSVec3(ToGLSLColor(R), ToGLSLColor(G), ToGLSLColor(B));
+ }
+
+ static QT3DSVec3 GetXAxisColor() { return ToGLSLColor(202, 47, 46); }
+ static QT3DSVec3 GetYAxisColor() { return ToGLSLColor(100, 205, 53); }
+ static QT3DSVec3 GetZAxisColor() { return ToGLSLColor(30, 159, 205); }
+
+ static inline QT3DSF32 GetDiscPos() { return 65.0f; }
+ static inline QT3DSF32 GetDiscRadius() { return 7.0f; }
+ static inline QT3DSF32 GetDiscRingRadius() { return GetDiscRadius() + 2.0f; }
+ };
+}
+}
+
+#endif
diff --git a/src/Authoring/Studio/Render/WGLRenderContext.cpp b/src/Authoring/Studio/Render/WGLRenderContext.cpp
new file mode 100644
index 00000000..36130d0d
--- /dev/null
+++ b/src/Authoring/Studio/Render/WGLRenderContext.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2001 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "stdafx.h"
+
+#include "WGLRenderContext.h"
+#include "foundation/TrackingAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "render/Qt3DSRenderContext.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSLogging.h"
+
+#include <QtGui/QOpenGLContext>
+#include <QtGui/QSurfaceFormat>
+#include <QOpenGLWidget>
+
+//UIC_DEFINE_THISFILE;
+
+//=============================================================================
+/**
+ * Constructor: Creates the object
+ */
+CWGLRenderContext::CWGLRenderContext(UICWindow inWindow)
+ : m_qtContext(0)
+ , m_Foundation(Q3DStudio::Foundation::SStudioFoundation::Create())
+{
+ Open(inWindow);
+}
+
+//=============================================================================
+/**
+ * Destructor: Destroys the object.
+ */
+CWGLRenderContext::~CWGLRenderContext()
+{
+ Close();
+}
+
+//=============================================================================
+/**
+ * Open the render context.
+ * @param inRenderWindow window handle
+ * @param inWindowSize window size
+ */
+void CWGLRenderContext::Open(UICWindow inRenderWindow)
+{
+ // needed because NVidia cards will fail all the system functions below if there is no window.
+ // note: the only time inRenderWindow is nullptr is when CThumbnailGenerator is used. Bug3075.
+ if (!inRenderWindow)
+ return;
+
+ QObject* qObject = static_cast<QObject*>(inRenderWindow);
+ QOpenGLWidget* qRenderWidget = qobject_cast<QOpenGLWidget*>(qObject);
+ Q_ASSERT(qRenderWidget);
+
+ OpenNormalContext(qRenderWidget);
+}
+
+QSurfaceFormat CWGLRenderContext::selectSurfaceFormat(QOpenGLWidget* window)
+{
+ struct ContextVersion {
+ int major;
+ int minor;
+ qt3ds::render::NVRenderContextType contextType;
+ };
+
+ ContextVersion versions[] = {
+ {4, 1, qt3ds::render::NVRenderContextValues::GL4},
+ {3, 3, qt3ds::render::NVRenderContextValues::GL3},
+ {2, 1, qt3ds::render::NVRenderContextValues::GL2},
+ {0, 0, qt3ds::render::NVRenderContextValues::NullContext}
+ };
+
+ QSurfaceFormat result = window->format();
+ bool valid = false;
+
+ for (const auto& ver : versions) {
+ if (ver.contextType == qt3ds::render::NVRenderContextValues::NullContext)
+ break;
+
+ // make an offscreen surface + context to query version
+ QScopedPointer<QOffscreenSurface> offscreenSurface(new QOffscreenSurface);
+
+ QSurfaceFormat format = window->format();
+ format.setRenderableType(QSurfaceFormat::OpenGL);
+ if (ver.major >= 2)
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setMajorVersion(ver.major);
+ format.setMinorVersion(ver.minor);
+ format.setDepthBufferSize(24);
+ format.setStencilBufferSize(8);
+
+ offscreenSurface->setFormat(format);
+ offscreenSurface->create();
+ Q_ASSERT(offscreenSurface->isValid());
+
+ QScopedPointer<QOpenGLContext> queryContext(new QOpenGLContext);
+ queryContext->setFormat(format);
+ if (queryContext->create()) {
+ valid = true;
+ result = format;
+ break;
+ }
+ } // of version test iteration
+
+ if (!valid) {
+ qFatal("Unable to select suitable OpenGL context");
+ }
+
+ qDebug() << Q_FUNC_INFO << "selected surface format:" << result;
+ return result;
+}
+
+//=============================================================================
+/**
+ * Open a non-multisample render context.
+ * @param inRenderWindow window handle
+ * @param inWindowSize window size
+ * @param inPixelDesc the pixel descriptor struct
+ */
+void CWGLRenderContext::OpenNormalContext(QOpenGLWidget* inRenderWindow)
+{
+ // Close before trying to open
+ Close();
+
+ // Save off the window
+ m_Window = inRenderWindow;
+ m_qtContext = m_Window->context();
+ Q_ASSERT(m_qtContext);
+
+ qt3ds::foundation::NVScopedRefCounted<qt3ds::foundation::IStringTable> theStringTable =
+ qt3ds::foundation::IStringTable::CreateStringTable(*m_Foundation.m_AllocatorCallback);
+ QSurfaceFormat contextFormat = m_qtContext->format();
+ m_RenderContext = NVScopedRefCounted<NVRenderContext>(
+ NVRenderContext::CreateGL(*m_Foundation.m_Foundation, *theStringTable,
+ contextFormat));
+ if (m_RenderContext) {
+ m_RenderContext->SetDefaultDepthBufferBitCount(contextFormat.depthBufferSize());
+ m_RenderContext->SetDefaultRenderTarget(inRenderWindow->defaultFramebufferObject());
+ }
+}
+
+//=============================================================================
+/**
+ * Close the render context.
+ */
+void CWGLRenderContext::Close()
+{
+ m_qtContext = 0;
+}
+
+//=============================================================================
+/**
+ * Prepare the render context to begin rendering.
+ */
+void CWGLRenderContext::BeginRender()
+{
+ m_Window->makeCurrent();
+ if (m_lastWidgetFBO != m_Window->defaultFramebufferObject()) {
+ m_lastWidgetFBO = m_Window->defaultFramebufferObject();
+ m_RenderContext->SetDefaultRenderTarget(m_lastWidgetFBO);
+ }
+}
+
+//=============================================================================
+/**
+ * Finalize the rendering of this frame, and present the result.
+ */
+void CWGLRenderContext::EndRender()
+{
+ m_Window->doneCurrent();
+}
+
+void CWGLRenderContext::resized()
+{
+ if (m_RenderContext) {
+ m_RenderContext->SetDefaultRenderTarget(m_Window->defaultFramebufferObject());
+ }
+}
+
+void CWGLRenderContext::requestRender()
+{
+ m_Window->update();
+}
diff --git a/src/Authoring/Studio/Render/WGLRenderContext.h b/src/Authoring/Studio/Render/WGLRenderContext.h
new file mode 100644
index 00000000..a6e5bf79
--- /dev/null
+++ b/src/Authoring/Studio/Render/WGLRenderContext.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef __WGLRENDERCONTEXT_H_
+#define __WGLRENDERCONTEXT_H_
+#include "PlatformTypes.h"
+#include "Q3DStudioNVFoundation.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+
+#include <QSurfaceFormat>
+
+QT_BEGIN_NAMESPACE
+class QOpenGLContext;
+class QOpenGLWidget;
+QT_END_NAMESPACE
+
+namespace qt3ds {
+class NVFoundation;
+}
+
+namespace qt3ds {
+namespace render {
+ class NVRenderContext;
+ class CAllocator;
+}
+}
+
+using qt3ds::NVFoundation;
+using qt3ds::render::NVRenderContext;
+using qt3ds::render::CAllocator;
+using qt3ds::foundation::NVScopedRefCounted;
+
+class GLogErrorString;
+
+//==============================================================================
+/**
+ * @class CWGLRenderContext: The OpenGL subclass of the CRenderContext class.
+ */
+class CWGLRenderContext
+{
+ // Field Members
+protected:
+ QOpenGLContext *m_qtContext;
+
+ Q3DStudio::Foundation::SStudioFoundation m_Foundation;
+ NVScopedRefCounted<NVRenderContext> m_RenderContext;
+ QOpenGLWidget* m_Window;
+ qt3ds::render::NVRenderContextType m_ContextType;
+
+ quint32 m_lastWidgetFBO = 0;
+
+ // Construction
+public:
+ CWGLRenderContext(UICWindow inRenderWindow);
+ ~CWGLRenderContext();
+
+ // Access
+public:
+ void BeginRender();
+ void EndRender();
+
+ // Only available after open.
+ NVRenderContext &GetRenderContext() { return *m_RenderContext; }
+
+ static QSurfaceFormat selectSurfaceFormat(QOpenGLWidget* window);
+
+ void resized();
+
+ void requestRender();
+
+ // Implementation
+protected:
+ void Open(UICWindow inRenderWindow);
+
+ void OpenNormalContext(QOpenGLWidget* inRenderWindow);
+ void Close();
+};
+
+#endif // __WGLRENDERCONTEXT_H_
diff --git a/src/Authoring/Studio/UI/ContextMenu.h b/src/Authoring/Studio/UI/ContextMenu.h
new file mode 100644
index 00000000..d6b42bbb
--- /dev/null
+++ b/src/Authoring/Studio/UI/ContextMenu.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2001 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_CONTEXT_MENU_H
+#define INCLUDED_CONTEXT_MENU_H 1
+
+#pragma once
+
+#ifdef WIN32
+#define MENUHANDLE HMENU
+#endif
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "GenericFunctor.h"
+#include <vector>
+
+class CContextMenu;
+
+//=============================================================================
+/**
+ * Class declaration for Context Menu performers.
+ */
+class CContextMenuPerformer
+{
+public:
+ CContextMenuPerformer()
+ : m_SubMenu(nullptr)
+ {
+ }
+
+ virtual void OnOptionSelected() = 0;
+
+ void SetSubMenu(CContextMenu *inSubMenu) { m_SubMenu = inSubMenu; }
+ CContextMenu *GetSubMenu() const { return m_SubMenu; }
+
+protected:
+ CContextMenu *m_SubMenu;
+};
+
+//=============================================================================
+/**
+ * Template declaration for Context Menu performers.
+ */
+template <class TClass>
+class CSpecificCContextMenuPerformer : public CContextMenuPerformer
+{
+public:
+ typedef void (TClass::*TFunction)();
+ CSpecificCContextMenuPerformer(TClass *inObject, TFunction inFunction)
+ {
+ m_Object = inObject;
+ m_Function = inFunction;
+ }
+
+ virtual void OnOptionSelected() { (m_Object->*m_Function)(); }
+
+ TClass *m_Object;
+ void (TClass::*m_Function)();
+};
+
+//=============================================================================
+/**
+ * Cross-platform class for creating context menus.
+ */
+class CContextMenu
+{
+ typedef std::vector<CContextMenuPerformer *> TPerformerList;
+ typedef std::vector<Q3DStudio::CString> TOptionList;
+
+public:
+ enum ESelection { NO_SELECTION = -1 };
+
+ CContextMenu();
+ virtual ~CContextMenu();
+
+ long DoPopup(CPt inLocation, HWND inParentWindow);
+ void AddSeparator();
+ void AddOption(int inOptionNameStringID, CContextMenuPerformer *inPerformer,
+ bool inIsEnabled = true);
+ void AddOption(const Q3DStudio::CString &inOptionName, CContextMenuPerformer *inPerformer,
+ bool inIsEnabled = true);
+
+ void Clear();
+ void SetCheck(long inIndex, bool inChecked);
+ void ClearChecked();
+ Q3DStudio::CString GetStringAt(long inIndex);
+ long GetItemCount();
+ long GetSelectedOption() { return m_SelectedOption; }
+ /*
+ template< class TBase >
+ void AddOption( const Q3DStudio::CString& inOptionName, TBase* inClass,
+ CSpecificCContextMenuPerformer<TBase>::TFunction inFunction, bool inIsEnabled )
+ {
+ AddOption( inOptionName, new CSpecificCContextMenuPerformer<TBase>( inClass,
+ inFunction, inIsEnabled ) );
+ }
+ */
+
+ virtual void Update();
+
+ MENUHANDLE GetMenuHandle() const { return m_Menu; }
+ CContextMenuPerformer *GetSelectedMenuPerformer(long inIndex);
+
+ void EnableOptionByIndex(long inIndex, bool inEnabledState = true);
+ void EnableOption(const Q3DStudio::CString &inOptionName, bool inEnabledState = true);
+ void EnableOption(unsigned int inOptionNameStringID, bool inEnabledState = true);
+
+protected:
+ void ProcessSelection(long inIndex);
+ void DeletePerformers();
+
+ TPerformerList m_Performers;
+ TOptionList m_Options;
+ long m_SelectedOption;
+ MENUHANDLE m_Menu;
+ long m_IndexBase; ///< Base index for command ids associated with this context menu
+};
+
+#endif // INCLUDED_CONTEXT_MENU_H \ No newline at end of file
diff --git a/src/Authoring/Studio/UI/CustomReBar.cpp b/src/Authoring/Studio/UI/CustomReBar.cpp
new file mode 100644
index 00000000..31813c3b
--- /dev/null
+++ b/src/Authoring/Studio/UI/CustomReBar.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "CustomReBar.h"
+#include "StudioPreferences.h"
+
+BEGIN_MESSAGE_MAP(CCustomReBar, CReBar)
+//}}AFX_MSG_MAP
+ON_WM_ERASEBKGND()
+END_MESSAGE_MAP()
+
+BOOL CCustomReBar::OnEraseBkgnd(CDC *pDC)
+{
+ CRect theRect;
+ GetClientRect(&theRect);
+ CColor theBaseColor(CStudioPreferences::GetToolBarBaseColor());
+ CBrush theBaseColorBrush(
+ RGB(theBaseColor.GetRed(), theBaseColor.GetGreen(), theBaseColor.GetBlue()));
+ CBrush *pOld = pDC->SelectObject(&theBaseColorBrush);
+ BOOL bRes = pDC->PatBlt(0, 0, theRect.Width(), theRect.Height(), PATCOPY);
+ pDC->SelectObject(pOld); // restore old brush
+ return bRes;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/UI/CustomReBar.h b/src/Authoring/Studio/UI/CustomReBar.h
new file mode 100644
index 00000000..66cbf5a4
--- /dev/null
+++ b/src/Authoring/Studio/UI/CustomReBar.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_CUSTOMTOOLBAR_H
+#define INCLUDED_CUSTOMTOOLBAR_H 1
+
+#pragma once
+
+//==============================================================================
+/**
+ * @class CCustomReBar overrides the Win32 CReBar simply to change the background color
+ */
+class CCustomReBar : public CReBar
+{
+public:
+ CCustomReBar() {}
+ ~CCustomReBar() {}
+
+ // Implementation
+protected:
+ // Generated message map functions
+ //{{AFX_MSG(CCustomToolBar)
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+public:
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+};
+
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/UI/PaletteState.cpp b/src/Authoring/Studio/UI/PaletteState.cpp
new file mode 100644
index 00000000..52750593
--- /dev/null
+++ b/src/Authoring/Studio/UI/PaletteState.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "PaletteState.h"
+#include "Preferences.h"
+
+CPaletteState::CPaletteState(Q3DStudio::CString inWindowName)
+ : m_WindowName(inWindowName)
+ , m_Maximized(false)
+ , m_IsVisible(false)
+{
+}
+
+CPaletteState::~CPaletteState()
+{
+}
+
+CPt CPaletteState::GetPosition() const
+{
+ return m_Position;
+}
+
+void CPaletteState::SetPosition(CPt inPosition)
+{
+ m_Position = inPosition;
+}
+
+CPt CPaletteState::GetSize() const
+{
+ return m_Size;
+}
+
+void CPaletteState::SetSize(CPt inSize)
+{
+ m_Size = inSize;
+}
+
+bool CPaletteState::IsVisible() const
+{
+ return m_IsVisible;
+}
+
+void CPaletteState::SetVisible(bool inIsVisible)
+{
+ m_IsVisible = inIsVisible;
+}
+
+void CPaletteState::LoadState()
+{
+ RestoreDefaults();
+
+ CPreferences thePrefs = CPreferences::GetUserPreferences(m_WindowName);
+
+ m_Maximized = static_cast<bool>(thePrefs.GetValue("maximized", m_Maximized));
+ m_Position.x = thePrefs.GetLongValue("positionX", m_Position.x);
+ m_Position.y = thePrefs.GetLongValue("positionY", m_Position.y);
+ m_Size.x = thePrefs.GetLongValue("sizeX", m_Size.x);
+ m_Size.y = thePrefs.GetLongValue("sizeY", m_Size.y);
+ m_IsVisible = static_cast<bool>(thePrefs.GetValue("visible", m_IsVisible));
+}
+
+void CPaletteState::SaveState() const
+{
+ CPreferences thePrefs = CPreferences::GetUserPreferences(m_WindowName);
+
+ thePrefs.SetValue("maximized", m_Maximized);
+ if (!m_Maximized) {
+ thePrefs.SetLongValue("positionX", m_Position.x);
+ thePrefs.SetLongValue("positionY", m_Position.y);
+ thePrefs.SetLongValue("sizeX", m_Size.x);
+ thePrefs.SetLongValue("sizeY", m_Size.y);
+ }
+ thePrefs.SetValue("visible", m_IsVisible);
+}
+
+void CPaletteState::RestoreDefaults()
+{
+ CPt theDisplaySize = ::GetAvailableDisplaySize();
+ CPt theInitialPos = GetDisplaySize();
+
+ if (m_WindowName == "Library") {
+ m_Position.x = theInitialPos.x;
+ m_Position.y = theInitialPos.y;
+ m_Size.x = ::dtol(theDisplaySize.x * .25);
+ m_Size.y = ::dtol(theDisplaySize.y * .60);
+ m_IsVisible = true;
+ } else if (m_WindowName == "Timeline") {
+ m_Position.x = ::dtol(theDisplaySize.x * .25) + theInitialPos.x;
+ m_Position.y = ::dtol(theDisplaySize.y * .60) + theInitialPos.y;
+ m_Size.x = ::dtol(theDisplaySize.x * .50);
+ m_Size.y = ::dtol(theDisplaySize.y * .40);
+ m_IsVisible = true;
+ } else if (m_WindowName == "Storage") {
+ m_Position.x = ::dtol(theDisplaySize.x * .75) + theInitialPos.x;
+ m_Position.y = ::dtol(theDisplaySize.y * .60) + theInitialPos.y;
+ m_Size.x = ::dtol(theDisplaySize.x * .25);
+ m_Size.y = ::dtol(theDisplaySize.y * .40);
+ m_IsVisible = true;
+ } else if (m_WindowName == "MainWindow") {
+ // m_Position.x = ::dtol( theDisplaySize.x * .25 ) + theInitialPos.x;
+ m_Position.x = theInitialPos.x;
+ m_Position.y = theInitialPos.y;
+ // m_Size.x = ::dtol( theDisplaySize.x * .50 );
+ // m_Size.y = ::dtol( theDisplaySize.y * .60 );
+ m_Size.x = ::dtol(theDisplaySize.x * 1.0);
+ m_Size.y = ::dtol(theDisplaySize.y * 1.0);
+ m_IsVisible = true;
+ } else if (m_WindowName == "Inspector") {
+ m_Position.x = theInitialPos.x;
+ m_Position.y = ::dtol(theDisplaySize.y * .60) + theInitialPos.y;
+ m_Size.x = ::dtol(theDisplaySize.x * .25);
+ m_Size.y = ::dtol(theDisplaySize.y * .40);
+ m_IsVisible = true;
+ } else {
+ m_Position.x = theInitialPos.x;
+ m_Position.y = ::dtol(theDisplaySize.y * .50);
+ m_Size.x = ::dtol(theDisplaySize.x * .25);
+ m_Size.y = ::dtol(theDisplaySize.y * .50);
+ m_IsVisible = true;
+ }
+}
+
+//=============================================================================
+/**
+* @return Returns the resolution in pixels of the current primary display.
+ */
+CPt CPaletteState::GetDisplaySize() const
+{
+ // return CPt( 0, 0 );
+ CRect theWorkArea;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &theWorkArea, FALSE);
+ return CPt(theWorkArea.left, theWorkArea.top);
+}
diff --git a/src/Authoring/Studio/UI/PaletteState.h b/src/Authoring/Studio/UI/PaletteState.h
new file mode 100644
index 00000000..c56b48e6
--- /dev/null
+++ b/src/Authoring/Studio/UI/PaletteState.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_PALETTE_STATE_H
+#define INCLUDED_PALETTE_STATE_H 1
+
+#pragma once
+
+#include "Pt.h"
+
+class CPaletteState
+{
+public:
+ CPaletteState(Q3DStudio::CString inWindowName);
+ virtual ~CPaletteState();
+
+ bool IsMaximized() const { return m_Maximized; }
+ void SetMaximized(bool inMaximized) { m_Maximized = inMaximized; }
+
+ CPt GetPosition() const;
+ void SetPosition(CPt inPosition);
+
+ CPt GetSize() const;
+ void SetSize(CPt inSize);
+
+ bool IsVisible() const;
+ void SetVisible(bool inIsVisible);
+
+ void SaveState() const;
+ void LoadState();
+
+ void RestoreDefaults();
+
+protected:
+ CPt GetDisplaySize() const;
+
+ Q3DStudio::CString m_WindowName;
+ bool m_Maximized;
+ CPt m_Position;
+ CPt m_Size;
+ bool m_IsVisible;
+};
+
+#endif // INCLUDED_PALETTE_STATE_H
diff --git a/src/Authoring/Studio/Utils/CmdLineParser.cpp b/src/Authoring/Studio/Utils/CmdLineParser.cpp
new file mode 100644
index 00000000..2c63c16f
--- /dev/null
+++ b/src/Authoring/Studio/Utils/CmdLineParser.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//=============================================================================
+// Includes
+//==============================================================================
+
+#include "CmdLineParser.h"
+#include "StudioObjectTypes.h"
+#include "UICFile.h"
+#include "UICString.h"
+
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+//=============================================================================
+/**
+ * Create a new Command Line Parser object.
+ */
+CCmdLineParser::CCmdLineParser()
+ : m_Silent(false)
+ , m_RunUnitTests(false)
+{
+}
+
+CCmdLineParser::~CCmdLineParser()
+{
+}
+
+//=============================================================================
+/**
+ * If the execution mode is to open a file or execute XML tests strings then
+ * this will return the filename used for them.
+ * @return filename for file open or XML tests
+ */
+Q3DStudio::CString CCmdLineParser::GetFilename() const
+{
+ return m_Filename;
+}
+
+//=============================================================================
+/**
+ * Call to parse the specified parameter from the command line format into
+ * a format useful by the app.
+ * @param inParameter the name of the parameter to be parsed.
+ * @param inIsFlag true if the param is a flag, false if it is an argument.
+ */
+void CCmdLineParser::ParseParam(const Q3DStudio::CString &inParameter, bool inIsFlag)
+{
+ if (inIsFlag) {
+ if (inParameter == "t" || inParameter == "test") {
+ m_ExecutionQueue.push_back(TEST_CMD_LINE);
+ m_RunUnitTests = true;
+ } else if (inParameter == "silent") {
+ m_Silent = true;
+ }
+ } else {
+ m_Params.push_back(inParameter);
+ }
+}
+
+//=============================================================================
+/**
+ * Call this to parse all the command line arguments into useful info.
+ * This is most often called with the args from main( ).
+ * @param inArgc the number of argments being given.
+ * @param inArgv the arguments.
+ */
+void CCmdLineParser::ParseArguments(int inArgc, wchar_t **inArgv)
+{
+ m_Filename.Clear();
+ m_Params.clear();
+
+ for (int i = 0; i < inArgc; ++i) {
+ bool isFlag = false;
+ Q3DStudio::CString theArg(inArgv[i]);
+ if (theArg[(long)0] == '-') {
+ isFlag = true;
+ // take off the dash
+ theArg = theArg.Extract(1);
+ }
+ ParseParam(theArg, isFlag);
+ }
+
+ // m_Params[1]is m_Filename
+ // m_Params[0] is the path to the executable
+ if (m_Params.size() > 1) {
+ ConvertToLongFilename(m_Params[1], m_Filename);
+ }
+
+ // Post-processing
+ // If there were no switches modifying m_ExecutionMode, default execution mode
+ // according to the file extension found on m_Filename.
+ if (0 == m_ExecutionQueue.size()) {
+ CUICFile theFile(m_Filename);
+ Q3DStudio::CString theExtension = "." + theFile.GetExtension();
+ if (theExtension.CompareNoCase(::LoadResourceString(IDS_FILE_EXT_UIP)))
+ m_ExecutionQueue.push_back(OPEN_FILE);
+ }
+}
+
+//=============================================================================
+/**
+ * Get the next main mode of execution that the application should be in.
+ * @return EExecutionMode execution mode; END_OF_CMDS if end of queue.
+ */
+CCmdLineParser::EExecutionMode CCmdLineParser::PopExecutionMode()
+{
+ EExecutionMode theMode = END_OF_CMDS;
+ if (m_ExecutionQueue.size() > 0) {
+ theMode = m_ExecutionQueue.front();
+ m_ExecutionQueue.pop_front();
+ }
+ return theMode;
+}
+
+//=============================================================================
+/**
+ * Return true if the silent flag is specified.
+ * @return true if running in muted mode. ie. no dialogs.
+ */
+bool CCmdLineParser::IsSilent() const
+{
+ return m_Silent;
+}
+
+//=============================================================================
+/**
+ * Return true if running unit tests.
+ * @return true if running unit tests.
+ */
+bool CCmdLineParser::IsRunUnitTests() const
+{
+ return m_RunUnitTests;
+}
+
+//=============================================================================
+/**
+ * Converts the specified path to its long form. If no long path is found,
+ * this function simply returns the specified name.
+ * @param inSource Source filename
+ * @param outLongFilename Output filename
+ */
+void CCmdLineParser::ConvertToLongFilename(const Q3DStudio::CString &inSource,
+ Q3DStudio::CString &outLongFilename) const
+{
+#ifdef _WIN32
+ TCHAR thePathBuffer[_MAX_PATH * 2 + 1] = {
+ 0
+ }; // make sure this char buffer is big enough for 2-byte chars
+ long theRet = GetLongPathName(inSource, thePathBuffer, _MAX_PATH * 2);
+ if (theRet != 0 && theRet <= _MAX_PATH * 2)
+ outLongFilename = Q3DStudio::CString(thePathBuffer);
+ else
+ outLongFilename = inSource; // No conversion
+#else
+ outLongFilename = inSource;
+#endif
+}
diff --git a/src/Authoring/Studio/Utils/CmdLineParser.h b/src/Authoring/Studio/Utils/CmdLineParser.h
new file mode 100644
index 00000000..427930a1
--- /dev/null
+++ b/src/Authoring/Studio/Utils/CmdLineParser.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_CMD_LINE_PARSER_H
+#define INCLUDED_CMD_LINE_PARSER_H 1
+
+#include <deque>
+
+#include <UICString.h>
+
+class CCmdLineParser
+{
+public:
+ enum EExecutionMode { NORMAL, OPEN_FILE, TEST_CMD_LINE, END_OF_CMDS };
+
+ CCmdLineParser();
+ virtual ~CCmdLineParser();
+
+ void ParseArguments(int inArgc, wchar_t **);
+
+ EExecutionMode PopExecutionMode();
+ bool IsSilent() const;
+ bool IsRunUnitTests() const;
+
+ Q3DStudio::CString GetFilename() const;
+
+protected:
+ virtual void ParseParam(const Q3DStudio::CString &inParameter, bool inIsFlag);
+ void ConvertToLongFilename(const Q3DStudio::CString &inSource,
+ Q3DStudio::CString &outLongFilename) const;
+
+ Q3DStudio::CString m_Filename; ///< input filename for file open
+ bool m_Silent; ///< true if non-dialog alternative output
+ std::vector<Q3DStudio::CString> m_Params; ///< filenames or optional parameters
+ std::deque<EExecutionMode> m_ExecutionQueue; ///< queue holding execution mode commands
+ bool m_RunUnitTests; ///< true if running unit tests
+};
+#endif // INCLUDED_CMD_LINE_PARSER_H
diff --git a/src/Authoring/Studio/Utils/ITickTock.h b/src/Authoring/Studio/Utils/ITickTock.h
new file mode 100644
index 00000000..2a3155bc
--- /dev/null
+++ b/src/Authoring/Studio/Utils/ITickTock.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef ITICKTOCKH
+#define ITICKTOCKH
+
+namespace Q3DStudio {
+class CString;
+}
+
+namespace UICDM {
+class ISignalConnection;
+}
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+QT_END_NAMESPACE
+
+typedef std::function<void()> TTickTockProc;
+
+namespace Q3DStudio {
+/**
+ * ITickTock is meant for a relatively small number of scheduled
+ * events that need to happen on the UI thread. Clients can cancel
+ * any event simply by releasing the returned shared pointer.
+ * Interface is completely threadsafe and the returned signal connection
+ * may safely outlive the interface.
+ */
+class ITickTock
+{
+protected:
+ virtual ~ITickTock() {}
+ static ITickTock *m_Instance;
+
+public:
+ // The timer is canceled if the shared ptr deletes the signal connection. Save to call from any
+ // thread.
+ // The callback, however, (inTickTockProc) will be activated solely from the UI thread.
+ virtual std::shared_ptr<UICDM::ISignalConnection>
+ AddTimer(unsigned long inTime, bool inIsPeriodic, TTickTockProc inTickTockProc,
+ const Q3DStudio::CString &inName) = 0;
+
+ // Called from UI thread to process all of the messages
+ // that have happened in the timer thread.
+ // Clients should not generally call this, it will be take care of for them.
+ // In the current implementation, MainFrm.h processes the tick tock message
+ // in order to call this function.
+ virtual void ProcessMessages() = 0;
+
+ friend class std::shared_ptr<ITickTock>;
+
+ // m_Instance is set to the first tick tock created, and unset when that tick tock
+ // goes away.
+ static std::shared_ptr<ITickTock> CreateTickTock(long inMessageID, QWidget* inTarget);
+
+ static ITickTock &GetInstance();
+};
+}
+
+#endif
diff --git a/src/Authoring/Studio/Utils/ImportUtils.cpp b/src/Authoring/Studio/Utils/ImportUtils.cpp
new file mode 100644
index 00000000..0b3ba6a6
--- /dev/null
+++ b/src/Authoring/Studio/Utils/ImportUtils.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "ImportUtils.h"
+#include "Dialogs.h"
+#include "UICFileTools.h"
+
+namespace Q3DStudio {
+
+SObjectFileType ImportUtils::GetObjectFileTypeForFile(const CFilePath &inFile,
+ bool inCheckFileExists /*= true*/)
+{
+ if (inCheckFileExists && inFile.IsFile() == false)
+ return SObjectFileType(OBJTYPE_UNKNOWN, DocumentEditorFileType::Unknown);
+
+ Q3DStudio::CString theExtension(inFile.GetExtension());
+ theExtension.ToLower();
+
+ if (theExtension.Compare(CDialogs::GetImportFileExtension(), Q3DStudio::CString::ENDOFSTRING,
+ false))
+ return SObjectFileType(OBJTYPE_GROUP, DocumentEditorFileType::Import);
+ else if (theExtension.Compare(CDialogs::GetMeshFileExtension(), Q3DStudio::CString::ENDOFSTRING,
+ false))
+ return SObjectFileType(OBJTYPE_MODEL, DocumentEditorFileType::Mesh);
+ else if (CDialogs::IsImageFileExtension(theExtension))
+ return SObjectFileType(
+ OBJTYPE_MODEL, OBJTYPE_IMAGE,
+ DocumentEditorFileType::Image); // Drag-drop image to scene will auto-map to Rectangle.
+ else if (theExtension.Compare(CDialogs::GetLUAFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false)
+ || theExtension.Compare(CDialogs::GetQmlFileExtension(),
+ Q3DStudio::CString::ENDOFSTRING, false))
+ return SObjectFileType(OBJTYPE_BEHAVIOR, DocumentEditorFileType::Behavior);
+ else if (CDialogs::IsFontFileExtension(theExtension))
+ return SObjectFileType(OBJTYPE_TEXT, DocumentEditorFileType::Font);
+ else if (CDialogs::IsEffectFileExtension(theExtension))
+ return SObjectFileType(OBJTYPE_EFFECT, DocumentEditorFileType::Effect);
+ else if (CDialogs::IsMaterialFileExtension(theExtension))
+ return SObjectFileType(OBJTYPE_CUSTOMMATERIAL, DocumentEditorFileType::Material);
+ else if (CDialogs::IsPathFileExtension(theExtension))
+ return SObjectFileType(OBJTYPE_PATH, DocumentEditorFileType::Path);
+ else if (CDialogs::IsPathBufferExtension(theExtension))
+ return SObjectFileType(OBJTYPE_PATH, DocumentEditorFileType::Path);
+ else if (CDialogs::IsSoundFileExtension(theExtension))
+ return SObjectFileType(OBJTYPE_SOUND, DocumentEditorFileType::Sound);
+
+ return SObjectFileType(OBJTYPE_UNKNOWN, DocumentEditorFileType::Unknown);
+}
+
+DocumentEditorInsertType::Enum ImportUtils::GetInsertTypeForDropType(EDROPDESTINATION inDestination)
+{
+ switch (inDestination) {
+ case EDROPDESTINATION_ON:
+ return DocumentEditorInsertType::LastChild;
+ case EDROPDESTINATION_ABOVE:
+ return DocumentEditorInsertType::PreviousSibling;
+ case EDROPDESTINATION_BELOW:
+ return DocumentEditorInsertType::NextSibling;
+ }
+ assert(0);
+ return DocumentEditorInsertType::LastChild;
+}
+}
diff --git a/src/Authoring/Studio/Utils/ImportUtils.h b/src/Authoring/Studio/Utils/ImportUtils.h
new file mode 100644
index 00000000..0a41dd9c
--- /dev/null
+++ b/src/Authoring/Studio/Utils/ImportUtils.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#pragma once
+#ifndef IMPORTUTILSH
+#define IMPORTUTILSH
+#include "DocumentEditorEnumerations.h"
+#include "StudioObjectTypes.h"
+#include "DropSource.h"
+
+namespace Q3DStudio {
+class CFilePath;
+
+struct SObjectFileType
+{
+ EStudioObjectType m_ObjectType; // The Object Type of the File. Used to specify the result
+ // ObjectType when drag-drop file to Scene.
+ EStudioObjectType m_IconType; // The Icon Type of the File. Used for User Interface (what Icon
+ // to display) such as Project Palette.
+ DocumentEditorFileType::Enum m_FileType; // The File Type of the File.
+
+ SObjectFileType(EStudioObjectType inObjectType, EStudioObjectType inIconType,
+ DocumentEditorFileType::Enum inFileType)
+ : m_ObjectType(inObjectType)
+ , m_IconType(inIconType)
+ , m_FileType(inFileType)
+ {
+ }
+
+ SObjectFileType(EStudioObjectType inObjectType, DocumentEditorFileType::Enum inFileType)
+ : m_ObjectType(inObjectType)
+ , m_IconType(inObjectType) // Icon type is same as object type
+ , m_FileType(inFileType)
+ {
+ }
+};
+
+class ImportUtils
+{
+public:
+ static SObjectFileType GetObjectFileTypeForFile(const CFilePath &inPath,
+ bool inCheckFileExists = true);
+
+ static DocumentEditorInsertType::Enum GetInsertTypeForDropType(EDROPDESTINATION inDestination);
+};
+}
+#endif \ No newline at end of file
diff --git a/src/Authoring/Studio/Utils/MouseCursor.h b/src/Authoring/Studio/Utils/MouseCursor.h
new file mode 100644
index 00000000..a346d597
--- /dev/null
+++ b/src/Authoring/Studio/Utils/MouseCursor.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_MOUSE_CURSOR_H
+#define INCLUDED_MOUSE_CURSOR_H 1
+
+#pragma once
+
+#include <QCursor>
+
+
+//=============================================================================
+// Includes
+//=============================================================================
+
+//=============================================================================
+/**
+ * Cross-platform cursor class.
+ */
+class CMouseCursor
+{
+public:
+ typedef long TUICMouseCursor;
+
+ static const TUICMouseCursor CURSOR_ARROW;
+ static const TUICMouseCursor CURSOR_WAIT;
+ static const TUICMouseCursor CURSOR_RESIZE_LEFTRIGHT;
+ static const TUICMouseCursor CURSOR_RESIZE_UPDOWN;
+ static const TUICMouseCursor CURSOR_GROUP_MOVE;
+ static const TUICMouseCursor CURSOR_GROUP_ROTATE;
+ static const TUICMouseCursor CURSOR_GROUP_SCALE;
+ static const TUICMouseCursor CURSOR_ITEM_MOVE;
+ static const TUICMouseCursor CURSOR_ITEM_ROTATE;
+ static const TUICMouseCursor CURSOR_ITEM_SCALE;
+ static const TUICMouseCursor CURSOR_EDIT_CAMERA_PAN;
+ static const TUICMouseCursor CURSOR_EDIT_CAMERA_ROTATE;
+ static const TUICMouseCursor CURSOR_EDIT_CAMERA_ZOOM;
+ static const TUICMouseCursor CURSOR_BLANK;
+ static const TUICMouseCursor CURSOR_DROP_INVALID;
+ static const TUICMouseCursor CURSOR_DROP_MOVE;
+ static const TUICMouseCursor CURSOR_DROP_COPY;
+ static const TUICMouseCursor CURSOR_IBEAM;
+
+ CMouseCursor();
+ virtual ~CMouseCursor();
+ QCursor GetHandle();
+ void Show();
+ void Hide();
+ void SetCursorPos(long inXPos, long inYPos);
+ bool Load(TUICMouseCursor inCursor);
+
+protected:
+ void Destroy();
+
+ QCursor m_Handle;
+
+private:
+ bool m_IsThemeCursor;
+ short m_ThemeCursor;
+};
+
+#endif // INCLUDED_MOUSE_CURSOR_H
diff --git a/src/Authoring/Studio/Utils/ResourceCache.cpp b/src/Authoring/Studio/Utils/ResourceCache.cpp
new file mode 100644
index 00000000..5a32ec18
--- /dev/null
+++ b/src/Authoring/Studio/Utils/ResourceCache.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "ResourceCache.h"
+#include "MouseCursor.h"
+#include "StudioUtils.h"
+
+#include <QUrl>
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CResourceCache::CResourceCache()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor: releases all loaded resources.
+ */
+CResourceCache::~CResourceCache()
+{
+ Clear();
+}
+
+//=============================================================================
+/**
+ * Returns a default instance of the resource cache so that the whole application
+ * can use the same cache if desired.
+ * @return Handle to the default instance of the cache
+ */
+CResourceCache *CResourceCache::GetInstance()
+{
+ static CResourceCache theCache;
+ return &theCache;
+}
+
+//=============================================================================
+/**
+ * Retrieves a bitmap image of the specified name. Currently only accepts .png
+ * files.
+ * @param inName Name of the bitmap file to be fetched
+ * @return Pointer to a bitmap object or NULL if the image could not be loaded
+ */
+QPixmap CResourceCache::GetBitmap(const QString &inName)
+{
+ QPixmap theImage;
+
+ // If our image name is not empty, then lets get it...
+ if (!inName.isEmpty()) {
+ TImageMap::iterator thePos = m_Images.find(inName);
+ if (thePos != m_Images.end()) {
+ theImage = thePos->second;
+ } else {
+ const QString resPath = QString("%1%2").arg(resourceImagePath(), inName);
+ if (theImage.load(resPath)) {
+ m_Images[inName] = theImage;
+ } else {
+ qWarning() << Q_FUNC_INFO << "missing image at path:" << resPath;
+ }
+ }
+ }
+ return theImage;
+}
+
+//=============================================================================
+/**
+ * Retrieves the specified cursor resource. The cursor is loaded if necessary
+ * otherwise a previously loaded cursor of the same ID is returned.
+ * @param inResourceID ID of the cursor to be loaded (see SCursor.h)
+ * @return Pointer to the cursor, or NULL if the cursor could not be loaded
+ */
+QCursor CResourceCache::GetCursor(CMouseCursor::TUICMouseCursor inResourceID)
+{
+ CMouseCursor *theCursor = NULL;
+ CMouseCursor::TUICMouseCursor theKey = inResourceID;
+
+ TCursorMap::iterator thePos = m_Cursors.find(theKey);
+ if (thePos != m_Cursors.end()) {
+ theCursor = thePos->second;
+ } else {
+ theCursor = new CMouseCursor();
+ if (theCursor->Load(inResourceID))
+ m_Cursors[theKey] = theCursor;
+ else {
+ delete theCursor;
+ theCursor = nullptr;
+ }
+ }
+
+ return theCursor ? theCursor->GetHandle() : QCursor();
+}
+
+//=============================================================================
+/**
+ * Clears all the maps of resources and deletes any associated resources.
+ * Called by the destructor.
+ */
+void CResourceCache::Clear()
+{
+ m_Images.clear();
+
+ TCursorMap::iterator theCursorPos = m_Cursors.begin();
+ for (; theCursorPos != m_Cursors.end(); ++theCursorPos) {
+ CMouseCursor *theCursor = theCursorPos->second;
+ delete theCursor;
+ }
+ m_Cursors.clear();
+}
diff --git a/src/Authoring/Studio/Utils/ResourceCache.h b/src/Authoring/Studio/Utils/ResourceCache.h
new file mode 100644
index 00000000..4b2f0c39
--- /dev/null
+++ b/src/Authoring/Studio/Utils/ResourceCache.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_RESOURCE_CACHE_H
+#define INCLUDED_RESOURCE_CACHE_H 1
+
+#pragma once
+
+#include "MouseCursor.h"
+#include "UICString.h"
+#include <map>
+
+#include <QCursor>
+#include <QPixmap>
+
+class CResImage;
+
+class CResourceCache
+{
+ typedef std::map<QString, QPixmap> TImageMap;
+ typedef std::map<CMouseCursor::TUICMouseCursor, CMouseCursor *> TCursorMap;
+
+public:
+ CResourceCache();
+ virtual ~CResourceCache();
+
+ static CResourceCache *GetInstance();
+
+ QPixmap GetBitmap(const QString &inName);
+ QCursor GetCursor(CMouseCursor::TUICMouseCursor inResourceID);
+
+ void Clear();
+
+protected:
+ TImageMap m_Images;
+ TCursorMap m_Cursors;
+};
+#endif // INCLUDED_RESOURCE_CACHE_H
diff --git a/src/Authoring/Studio/Utils/StringLoader.cpp b/src/Authoring/Studio/Utils/StringLoader.cpp
new file mode 100644
index 00000000..73a5324f
--- /dev/null
+++ b/src/Authoring/Studio/Utils/StringLoader.cpp
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qxmlstream.h>
+
+CStringLoader CStringLoader::s_GlobalInstance;
+
+CStringLoader::CStringLoader()
+ : m_Strings(nullptr)
+ , m_StringCount(0)
+{
+}
+
+CStringLoader::~CStringLoader()
+{
+ UnloadResourceStrings();
+}
+
+//=============================================================================
+/**
+ * Static function to load the string resource specified by inStringID.
+ * inStringID should have been specified in the Resource.h header file.
+ * This will load the string from the global string resource object.
+ */
+Q3DStudio::CString CStringLoader::LoadString(long inStringID)
+{
+ return s_GlobalInstance.LoadResourceString(inStringID);
+}
+
+//=============================================================================
+/**
+ * Load the string resource specified by inStringID.
+ * inStringID should have been specified in the Resource.h header file.
+ * @param inID the ID of the string to be loaded.
+ * @return theString specified by inStringID.
+ */
+Q3DStudio::CString CStringLoader::LoadResourceString(long inStringID)
+{
+ Q3DStudio::CString theName;
+ // Make sure we aren't going off into la-la land.
+ if (inStringID > 0 && inStringID < m_StringCount) {
+ theName = m_Strings[inStringID];
+ }
+ return theName;
+}
+
+//=============================================================================
+/**
+ * Load the string resources from the specified directory into the global table.
+ * All .stro files in the directory will be processed for strings.
+ * @param inDirectory the directory that should be read for strings.
+ */
+void CStringLoader::LoadStrings(const CUICFile &inDirectory)
+{
+ s_GlobalInstance.LoadResourceStrings(inDirectory);
+}
+
+//=============================================================================
+/**
+ * Load the string resources from the specified directory into this string table.
+ * All .stro files in the directory will be processed for strings.
+ * @param inDirectory the directory that should be read for strings.
+ */
+void CStringLoader::LoadResourceStrings(const CUICFile &inDirectory)
+{
+ UnloadResourceStrings();
+
+ // Sure hope we don't get an absolute ton of strings here...
+ m_Strings = new Q3DStudio::CString[STRING_RESOURCE_COUNT + 1];
+ m_StringCount = STRING_RESOURCE_COUNT;
+
+ Q3DStudio::CString theExtension = ".stro";
+
+ // Go through all the files in the directory and process them.
+ CFileIterator theFiles = inDirectory.GetSubItems();
+ for (; !theFiles.IsDone(); ++theFiles) {
+ CUICFile theFile = theFiles.GetCurrent();
+ Q3DStudio::CString theFilename = theFile.GetAbsolutePath();
+ // Only process .stro files.
+ if (theFilename.Find(theExtension) == theFilename.Length() - theExtension.Length()) {
+ QFile file(theFilename.toQString());
+ file.open(QFile::ReadOnly);
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ reader.readNextStartElement();
+ if (reader.name() == "string") {
+ Q3DStudio::CString theValue;
+ long theIndex = -1;
+ for (auto attrib : reader.attributes()) {
+ if (attrib.name() == "value")
+ theValue = attrib.value().toUtf8().constData();
+ else if (attrib.name() == "ID")
+ theIndex = attrib.value().toInt();
+ }
+ if (theIndex > 0 && theIndex < STRING_RESOURCE_COUNT) {
+ theValue.Replace("\\n", "\n");
+ theValue.Replace("\\t", "\t");
+ m_Strings[theIndex] = theValue;
+ }
+ }
+ }
+ }
+ }
+}
+
+void CStringLoader::UnloadStrings()
+{
+ s_GlobalInstance.UnloadResourceStrings();
+}
+
+void CStringLoader::UnloadResourceStrings()
+{
+ delete[] m_Strings;
+ m_Strings = nullptr;
+ m_StringCount = 0;
+}
+
+Q3DStudio::CString LoadResourceString(long inID)
+{
+ return CStringLoader::LoadString(inID);
+}
diff --git a/src/Authoring/Studio/Utils/StringLoader.h b/src/Authoring/Studio/Utils/StringLoader.h
new file mode 100644
index 00000000..69595566
--- /dev/null
+++ b/src/Authoring/Studio/Utils/StringLoader.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STRING_LOADER_H
+#define INCLUDED_STRING_LOADER_H 1
+#pragma once
+
+#include "UICFile.h"
+
+class CStringLoader
+{
+public:
+ CStringLoader();
+ virtual ~CStringLoader();
+
+ static Q3DStudio::CString LoadString(long inStringID);
+ static void LoadStrings(const CUICFile &inDirectory);
+ static void UnloadStrings();
+
+ Q3DStudio::CString LoadResourceString(long inStringID);
+ void LoadResourceStrings(const CUICFile &inDirectory);
+ void UnloadResourceStrings();
+
+protected:
+ Q3DStudio::CString *m_Strings;
+ long m_StringCount;
+
+ static CStringLoader s_GlobalInstance;
+};
+
+Q3DStudio::CString LoadResourceString(long inID);
+
+#endif // INCLUDED_STRING_LOADER_H
diff --git a/src/Authoring/Studio/Utils/StudioUtils.cpp b/src/Authoring/Studio/Utils/StudioUtils.cpp
new file mode 100644
index 00000000..34522198
--- /dev/null
+++ b/src/Authoring/Studio/Utils/StudioUtils.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "CoreUtils.h"
+#include "StudioPreferences.h"
+#include "StudioClipboard.h"
+#include "Pt.h"
+
+#include <QtWidgets/qapplication.h>
+#include <QtWidgets/qdesktopwidget.h>
+#include <QtGui/qdesktopservices.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qwindow.h>
+#include <QtCore/qurl.h>
+
+//==============================================================================
+/**
+ * FormatTimeString: Format a time string.
+ * @param inTimeMS Time in milliseconds.
+ * @return The formatted time string in MM:SS:MS format.
+ */
+Q3DStudio::CString FormatTimeString(long inTimeMS)
+{
+ BOOL theNegativeFlag = (inTimeMS < 0);
+ long theTimeMS = abs(inTimeMS);
+ Q3DStudio::CString theTimeString;
+ long theMM, theSS;
+
+ // Format the time in MM:SS:MS format
+
+ // Get the MM value
+ theMM = theTimeMS / 60000;
+ theTimeMS -= (theMM * 60000);
+
+ // Get the SS value
+ theSS = theTimeMS / 1000;
+ theTimeMS -= (theSS * 1000);
+
+ // Remainder is MS value
+
+ // Format the string
+ theTimeString.Format(_UIC("%d:%0.2d.%0.2d"), theMM, theSS, theTimeMS / 10);
+
+ // If the original time was negative, append the "-" to the front of the time string.
+ if (theNegativeFlag) {
+ theTimeString.Insert(0, "-");
+ }
+
+ return theTimeString;
+}
+
+//==============================================================================
+/**
+ * Checks a string to determine if it is numeric.
+ * @param inString String to check for all numeric characters.
+ * @return TRUE if the string is numeric.
+ */
+bool IsNumericString(const Q3DStudio::CString &inString)
+{
+ Q3DStudio::CString theNumbers = "0123456789";
+ long theLoop;
+ bool theNumericFlag = true;
+
+ // Iterate through the entire string
+ for (theLoop = 0; theLoop < inString.Length() && theNumericFlag; theLoop++) {
+ // Check each character for being numeric
+ if (theNumbers.Find(inString.Extract(theLoop, 1)) == Q3DStudio::CString::ENDOFSTRING)
+ theNumericFlag = false;
+ }
+
+ return theNumericFlag;
+}
+
+//=============================================================================
+/**
+ * @return The available resolution in pixels of the display index "screen"
+ * (minus the Dock/Taskbar, etc.). Default is current primary display.
+ */
+QSize GetAvailableDisplaySize(int screen)
+{
+ return QApplication::desktop()->availableGeometry(screen).size();
+}
+
+//=============================================================================
+/**
+* @return The total resolution in pixels of the display index "screen".
+* Default is the current primary display.
+ */
+QSize GetDisplaySize(int screen)
+{
+ return QApplication::desktop()->screenGeometry(screen).size();
+
+}
+
+//=============================================================================
+/**
+* @return The index of the screen containing "widget".
+ */
+int getWidgetScreen(QWidget *widget)
+{
+ return QApplication::desktop()->screenNumber(widget);
+}
+
+//=============================================================================
+/**
+ * Helper function to adjust the point for the color popup dialog so that it
+ * ends up in the right place. Adjust the given point if it is near the left
+ * or bottom part of the screen.
+ * @param ioPoint upper-left corner of the color popup dialog; will be adjust if necessary on return
+ */
+void TranslatePoint(QPoint &ioPoint, const QPoint &inSize)
+{
+ long theBuffer = 10; // Just because the taskbar seems to overlap the dialog by a little bit
+ QPoint theDlgSize(150, 260); // Size of color popup dialog - note that it's hard-coded
+ QSize theScreenSize = GetAvailableDisplaySize(-1);
+ long theVertOffset = theDlgSize.y() - inSize.y();
+ long theHorizOffset = theDlgSize.x();
+
+ // If the point is too close to both the left side and the bottom of the screen, adjust both the
+ // x and y values
+ if ((ioPoint.y() > theScreenSize.height() - theVertOffset - theBuffer)
+ && (ioPoint.x() >= theScreenSize.width() - theHorizOffset)) {
+
+ ioPoint.setX(ioPoint.x() - (theHorizOffset + inSize.x()));
+ ioPoint.setY(ioPoint.y() - theVertOffset);
+ }
+ // If the point is just too close to the bottom of the screen, adjust the y value
+ else if (ioPoint.y() > theScreenSize.height() - theVertOffset - theBuffer)
+ ioPoint.setY(ioPoint.y() - theVertOffset);
+ // If the point is just too close to the left side of the screen, adjust the x value
+ else if (ioPoint.x() >= theScreenSize.width() - theHorizOffset)
+ ioPoint.setX(ioPoint.x() - (theHorizOffset + inSize.x()));
+}
+
+long TimeToPos(long inTime, double inTimeRatio)
+{
+ return ::dtol(inTime * inTimeRatio) + CStudioPreferences::GUTTER_SIZE;
+}
+
+long TimeToPos(double inTime, double inTimeRatio)
+{
+ return ::dtol(inTime * inTimeRatio) + CStudioPreferences::GUTTER_SIZE;
+}
+
+long PosToTime(long inPos, double inTimeRatio)
+{
+ return ::dtol((inPos - CStudioPreferences::GUTTER_SIZE) / inTimeRatio);
+}
+
+//=============================================================================
+/**
+ * opens the url in the web browser
+ *
+ * @param inURL
+ * the URL to open
+ *
+ * @return void
+ */
+void ShowURLInBrowser(const Q3DStudio::CString &inURL)
+{
+ QDesktopServices::openUrl(QUrl(inURL.toQString()));
+}
+
+QString resourcePath()
+{
+ return QStringLiteral(":/res");
+}
+
+QString resourceImagePath()
+{
+ return QStringLiteral(":/images/");
+}
+
+QString resourceImageUrl()
+{
+ return QStringLiteral("qrc:/images/");
+}
+
+// Returns the qml import path required for binary installations
+QString qmlImportPath()
+{
+ QString extraImportPath(QStringLiteral("%1/qml"));
+ return extraImportPath.arg(QApplication::applicationDirPath());
+}
+
+qreal devicePixelRatio()
+{
+ static qreal pixelRatio = 0.0;
+ if (Q_UNLIKELY(pixelRatio == 0.0)) {
+ const QWindowList list = QGuiApplication::topLevelWindows();
+ if (list.size() > 0)
+ pixelRatio = list[0]->devicePixelRatio();
+ }
+ return pixelRatio;
+}
diff --git a/src/Authoring/Studio/Utils/StudioUtils.h b/src/Authoring/Studio/Utils/StudioUtils.h
new file mode 100644
index 00000000..4e248bb3
--- /dev/null
+++ b/src/Authoring/Studio/Utils/StudioUtils.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STUDIO_UTILS_H
+#define INCLUDED_STUDIO_UTILS_H 1
+
+#pragma once
+
+#include "UICString.h"
+#include <QPoint>
+#include <QSize>
+
+//==============================================================================
+// Functions
+//==============================================================================
+Q3DStudio::CString FormatTimeString(long inTimeMS);
+bool IsNumericString(Q3DStudio::CString inString);
+
+QSize GetAvailableDisplaySize(int screen = -1);
+QSize GetDisplaySize(int screen = -1);
+void TranslatePoint(QPoint &ioPoint, const QPoint &inSize);
+
+long TimeToPos(long inTime, double inTimeRatio);
+long TimeToPos(double inTime, double inTimeRatio);
+long PosToTime(long inPos, double inTimeRatio);
+
+void ShowURLInBrowser(Q3DStudio::CString inURL);
+
+QString resourceImagePath();
+QString resourceImageUrl();
+
+QString resourcePath();
+
+QString qmlImportPath();
+
+qreal devicePixelRatio();
+
+int getWidgetScreen(QWidget *widget);
+
+#endif // INCLUDED_STUDIO_UTILS_H
diff --git a/src/Authoring/Studio/Utils/SystemPreferences.cpp b/src/Authoring/Studio/Utils/SystemPreferences.cpp
new file mode 100644
index 00000000..85e5a58b
--- /dev/null
+++ b/src/Authoring/Studio/Utils/SystemPreferences.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "CColor.h"
+#include "SystemPreferences.h"
+
+#include <QApplication>
+#include <QPalette>
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSystemPreferences::~CSystemPreferences()
+{
+}
+
+//=============================================================================
+/**
+ * Retrieves the system preferred background color. Example use would be for
+ * the fill color of a button.
+ */
+QColor CSystemPreferences::GetSystemBackgroundColor()
+{
+ const auto palette = QApplication::palette();
+ return palette.color(QPalette::Active, QPalette::Button);
+}
+
+//=============================================================================
+/**
+ * Returns the color used by the system to indicate that an item is selected.
+ */
+QColor CSystemPreferences::GetSelectedItemColor()
+{
+ const auto palette = QApplication::palette();
+ return palette.color(QPalette::Active, QPalette::Highlight);
+}
+
+//=============================================================================
+/**
+ * Returns the color of selected text to be displayed on top of GetSelectedItemColor().
+ */
+QColor CSystemPreferences::GetSelectedTextColor()
+{
+ const auto palette = QApplication::palette();
+ return palette.color(QPalette::Active, QPalette::HighlightedText);
+}
+
+//=============================================================================
+/**
+ * Supposed to get whether or not anti aliasing should be enabled for a specified
+ * font size, but the Theme does not seem to include any data on it. If the
+ * theme starts working in a later release of the OS then this should work.
+ */
+bool CSystemPreferences::IsFontAntiAliasing(float inFontSize)
+{
+ Q_UNUSED(inFontSize);
+ return false;
+}
+
+//=============================================================================
+/**
+ * Scroll bars contain two arrows (up/down or left/right). These can be located
+ * on either end of the scroll bar, or located next to each other at one end. On
+ * Mac OS X, this is a system preference.
+ * @return true if the scroll bar arrows should be next to each other, at one end of the scroll bar
+ */
+bool CSystemPreferences::AreScrollArrowsAdjacent()
+{
+ return false;
+}
diff --git a/src/Authoring/Studio/Utils/SystemPreferences.h b/src/Authoring/Studio/Utils/SystemPreferences.h
new file mode 100644
index 00000000..2d72780e
--- /dev/null
+++ b/src/Authoring/Studio/Utils/SystemPreferences.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_SYSTEM_PREFERENCES_H
+#define INCLUDED_SYSTEM_PREFERENCES_H 1
+
+#pragma once
+
+class CColor;
+
+class CSystemPreferences
+{
+public:
+ CSystemPreferences();
+ virtual ~CSystemPreferences();
+ static QColor GetSystemBackgroundColor();
+ static QColor GetSelectedItemColor();
+ static QColor GetSelectedTextColor();
+ static bool IsFontAntiAliasing(float inFontSize);
+ static bool AreScrollArrowsAdjacent();
+};
+
+#endif // INCLUDED_SYSTEM_PREFERENCES_H
diff --git a/src/Authoring/Studio/Utils/TickTock.cpp b/src/Authoring/Studio/Utils/TickTock.cpp
new file mode 100644
index 00000000..dcf6eb8a
--- /dev/null
+++ b/src/Authoring/Studio/Utils/TickTock.cpp
@@ -0,0 +1,312 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "ITickTock.h"
+#include "UICDMSignals.h"
+#include "Thread.h"
+#include "Mutex.h"
+#include "Conditional.h"
+#include "StandardExtensions.h"
+#include <vector>
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QWidget>
+
+using namespace Q3DStudio;
+using Q3DStudio::CString;
+using namespace UICDM;
+using namespace std;
+
+namespace {
+
+struct TickTockImpl;
+
+struct TickTockItem : public ISignalConnection
+{
+ TTickTockProc m_Callback;
+ bool m_IsPeriodic;
+ unsigned long m_Time;
+ quint64 m_NextTime;
+ CString m_Name;
+ TickTockImpl *m_Impl;
+
+ TickTockItem(TTickTockProc inCallback, bool inPeriodic, unsigned long inTime,
+ quint64 inNextTime, const CString &inName, TickTockImpl &inImpl)
+ : m_Callback(inCallback)
+ , m_IsPeriodic(inPeriodic)
+ , m_Time(inTime)
+ , m_NextTime(inNextTime)
+ , m_Name(inName)
+ , m_Impl(&inImpl)
+ {
+ }
+ // Implemented below to access TickTockImpl API
+ virtual ~TickTockItem();
+
+ // Called when our tick tock impl is through with us.
+ // We cannot access the tick tock impl after this as
+ // it could cause a crash
+ void Release() { m_Impl = nullptr; }
+
+ void Signal()
+ {
+ if (m_Impl)
+ m_Callback();
+ }
+ bool operator<(const TickTockItem &inOther) const { return m_NextTime < inOther.m_NextTime; }
+};
+
+struct TickTockDerefCompare
+{
+ bool operator()(const TickTockItem *lhs, const TickTockItem *rhs) { return *lhs < *rhs; }
+};
+
+typedef vector<TickTockItem *> TTockList;
+
+/**
+ * Struct that implements the ITickTock interface via a thread that runs
+ * and schedules information and a message that is sent back in order to
+ * process that schedule information and send out signals on the UI thread.
+ */
+struct TickTockImpl : public ITickTock, public CRunnable
+{
+ // Mutex to protect our internal datastructures
+ // Mainly m_Tockers and m_SignalledTockers
+ CMutex m_Mutex;
+ // Our thread
+ CThread m_Thread;
+ // Event to wake up schedule thread; used to keep thread from
+ // spinning till next event.
+ CConditional m_Event;
+
+ // The sorted list (by m_NextTime) of tick tock signals
+ TTockList m_Tockers;
+ // The list of signals that are past their time and need to be sent.
+ TTockList m_SignalledTockers;
+ // True if the thread is running and we want it to continue.
+ volatile bool m_IsRunning;
+ // Message to fire into the window in order to get our notifications
+ // processed on the UI thread
+ long m_MessageID;
+ // Target window that we use in order to fire events and get processing
+ // done back on the UI thread from the schedule thread.
+ QWidget *m_Target;
+
+ TickTockImpl(long inMessageID, QWidget *inTarget)
+ : m_Thread(this, "ITickTock", NULL, false)
+ , m_IsRunning(false)
+ , m_MessageID(inMessageID)
+ , m_Target(inTarget)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ m_IsRunning = true;
+ m_Thread.Start();
+ }
+
+ virtual ~TickTockImpl()
+ {
+ if (m_Instance == this)
+ m_Instance = nullptr;
+ {
+ CMutex::Scope __mutexScope(&m_Mutex);
+ UnsafeReleaseTockers(m_Tockers);
+ UnsafeReleaseTockers(m_SignalledTockers);
+ }
+
+ if (m_IsRunning) {
+ m_IsRunning = false;
+ // Wake up the thread to notify it to exit
+ m_Event.Notify();
+ // Wait for the thread to exit.
+ m_Thread.Join();
+ }
+ }
+
+ TSignalConnectionPtr AddTimer(unsigned long inTime, bool inIsPeriodic,
+ TTickTockProc inTickTockProc,
+ const Q3DStudio::CString &inName) override
+ {
+ // Lock down so we don't conflict with the timer thread.
+ CMutex::Scope __mutexScope(&m_Mutex);
+
+ std::shared_ptr<TickTockItem> retval =
+ std::make_shared<TickTockItem>(inTickTockProc, inIsPeriodic, inTime,
+ QDateTime::currentMSecsSinceEpoch() + inTime, inName, std::ref(*this));
+ if (inTime > 0) {
+ UnsafeInsertTimer(*retval);
+ } else {
+ assert(inIsPeriodic == false);
+ m_SignalledTockers.push_back(retval.get());
+ }
+ m_Event.Notify();
+
+ return retval;
+ }
+
+ void RemoveTimer(TickTockItem &inItem)
+ {
+ CMutex::Scope __mutexScope(&m_Mutex);
+ UnsafeRemoveTimer(inItem, m_Tockers);
+ UnsafeRemoveTimer(inItem, m_SignalledTockers);
+ }
+
+ //=============================================================================
+ /**
+ * Call from the main processing thread to process any existing messages.
+ * This should be called when a message of the type specified in the constructor
+ * is recieved. This will call all the functors on the timers that have
+ * triggered.
+ */
+ void ProcessMessages() override
+ {
+ quint64 theCurrentTime = QDateTime::currentMSecsSinceEpoch();
+
+ CMutex::Scope __mutexScope(&m_Mutex);
+ // Go through all the timers looking for expired ones
+ for (TTockList::iterator theTocks = m_SignalledTockers.begin(),
+ end = m_SignalledTockers.end();
+ theTocks != end; ++theTocks) {
+ TickTockItem *theTock = (*theTocks);
+ // If this item hasn't been released in another thread.
+ if (theTock->m_Impl) {
+ theTock->Signal();
+
+ // If it is periodic, re-add it.
+ if (theTock->m_IsPeriodic == true) {
+ theTock->m_NextTime = theCurrentTime + theTock->m_Time;
+ UnsafeInsertTimer(*theTock);
+ }
+ // If it isn't, then we forget about it. The client's have a shared-ptr
+ // to the object so it will get deleted eventually, it just isn't any of
+ // our business any more
+ else
+ theTock->Release();
+ }
+ }
+
+ m_SignalledTockers.clear();
+ m_Event.Notify();
+ }
+
+ void Run(void *) override
+ {
+ unsigned long theNextTime = (unsigned long)-1;
+ // Stay in this loop until the running flag is turned off on the constructor.
+ while (m_IsRunning) {
+ // Wait for either the next time or the change notification
+ m_Event.Wait(theNextTime);
+
+ CMutex::Scope __mutexScope(&m_Mutex);
+
+ // Get the amount of time this should sleep for.
+ theNextTime = (unsigned long)-1;
+ quint64 theCurrentTime = QDateTime::currentMSecsSinceEpoch();
+ // We know that m_Tocks is sorted by m_NextTime.
+ // So we run through it linearly, transferring singalled items
+ // into the signalled vector.
+ size_t idx = 0, end = m_Tockers.size();
+ for (; idx < end; ++idx) {
+ TickTockItem *item(m_Tockers[idx]);
+ if (item->m_NextTime <= theCurrentTime && item->m_Impl != nullptr) {
+ m_SignalledTockers.push_back(item);
+ } else
+ break;
+ }
+ // We then remove all signalled items.
+ if (m_Tockers.empty() == false)
+ m_Tockers.erase(m_Tockers.begin(), m_Tockers.begin() + idx);
+
+ // And we reset next time to the difference between the current time
+ // and the first tock's next time.
+ if (m_Tockers.empty() == false) {
+ assert(m_Tockers.front()->m_NextTime > theCurrentTime);
+ theNextTime = m_Tockers.front()->m_NextTime - theCurrentTime;
+ }
+
+ if (m_SignalledTockers.empty() == false) {
+ // Send the async notification that timers have expired.
+ qApp->postEvent(m_Target, new QTimerEvent(m_MessageID));
+ }
+ }
+ }
+
+private:
+ ////////////////////////////////////////////////////////////////
+ // Unsafe functions are functions that require the mutex locked
+ // in order to function correctly
+ ////////////////////////////////////////////////////////////////
+ void UnsafeInsertTimer(TickTockItem &inItem)
+ {
+ binary_sort_insert_unique(m_Tockers, &inItem, TickTockDerefCompare());
+ }
+
+ void UnsafeReleaseTockers(TTockList &inTockers)
+ {
+ for (size_t idx = 0, end = inTockers.size(); idx < end; ++idx)
+ inTockers[idx]->Release();
+ inTockers.clear();
+ }
+
+ // Unsafe means we don't have the mutex
+ void UnsafeRemoveTimer(TickTockItem &inItem, TTockList &ioList)
+ {
+ TTockList::iterator theTimer = std::find(ioList.begin(), ioList.end(), &inItem);
+ if (theTimer != ioList.end())
+ ioList.erase(theTimer);
+ }
+};
+
+TickTockItem::~TickTockItem()
+{
+ if (m_Impl != NULL)
+ m_Impl->RemoveTimer(*this);
+ m_Impl = NULL;
+}
+}
+
+ITickTock *ITickTock::m_Instance(NULL);
+
+std::shared_ptr<ITickTock> ITickTock::CreateTickTock(long inMessageID, QWidget *inWnd)
+{
+ std::shared_ptr<TickTockImpl> theTickTock(
+ std::make_shared<TickTockImpl>(inMessageID, std::ref(inWnd)));
+ theTickTock->Initialize();
+ if (m_Instance == NULL)
+ m_Instance = theTickTock.get();
+ return theTickTock;
+}
+
+ITickTock &ITickTock::GetInstance()
+{
+ return *m_Instance;
+}
diff --git a/src/Authoring/Studio/Workspace/Dialogs.h b/src/Authoring/Studio/Workspace/Dialogs.h
new file mode 100644
index 00000000..012ce98f
--- /dev/null
+++ b/src/Authoring/Studio/Workspace/Dialogs.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2001 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#ifndef INCLUDED_DIALOGS_H
+#define INCLUDED_DIALOGS_H 1
+
+#pragma once
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "UICFile.h"
+#include "StudioObjectTypes.h"
+#include "UICMessageBox.h"
+//#include "MultilineEditDlg.h"
+#include "UICFileTools.h"
+#include "CColor.h"
+#include <QMessageBox>
+
+//=============================================================================
+// Forwards
+//=============================================================================
+class IDoc;
+class CStudioApp;
+class CControl;
+class CDialogControl;
+class IProgressCallback;
+
+class CProgressPalette;
+
+class CDialogs
+{
+public:
+ enum ESavePromptResult {
+ CANCEL_OPERATION,
+ CONTINUE_NO_SAVE,
+ SAVE_FIRST,
+ };
+
+ CDialogs(bool inShowGUI = true);
+ virtual ~CDialogs();
+
+ void DisplayAssetDeleteFailed();
+ void DisplayRefreshResourceFailed(const Q3DStudio::CString &inResourceName,
+ const Q3DStudio::CString &inDescription);
+ QString ConfirmRefreshModelFile(const QString &inOriginalPath);
+
+ // This is not an appropriate place for these, but better
+ // in an inappropriate place than duplicated
+ static const char *GetDAEFileExtension();
+ static const char *GetFbxFileExtension();
+ // Null terminated list
+ static const char **GetImgFileExtensions();
+ static const char *GetImportFileExtension();
+ static const char *GetMeshFileExtension();
+ static const char *GetLUAFileExtension();
+ static const char *GetQmlFileExtension();
+ static const char **GetFontFileExtensions();
+ static const char **GetEffectFileExtensions();
+ static const char **GetMaterialFileExtensions();
+ static const char **GetSoundFileExtensions();
+ static bool IsImageFileExtension(const char *inExt);
+ static bool IsFontFileExtension(const char *inExt);
+ static bool IsEffectFileExtension(const char *inExt);
+ static bool IsMaterialFileExtension(const char *inExt);
+ static bool IsSoundFileExtension(const char *inExt);
+
+ static const wchar_t *GetWideDAEFileExtension();
+ static const wchar_t *GetWideFbxFileExtension();
+ static const wchar_t *GetWideImportFileExtension();
+ static const wchar_t *GetWideMeshFileExtension();
+ static const wchar_t *GetWideLUAFileExtension();
+ static const wchar_t **GetWideFontFileExtensions();
+ static const wchar_t **GetWideImgFileExtensions();
+ static const wchar_t **GetWideEffectFileExtensions();
+ static const wchar_t **GetWideMaterialFileExtensions();
+ static const wchar_t **GetWideSoundFileExtensions();
+ static bool IsImageFileExtension(const wchar_t *inExt);
+ static bool IsFontFileExtension(const wchar_t *inExt);
+ static bool IsEffectFileExtension(const wchar_t *inExt);
+ static bool IsMaterialFileExtension(const wchar_t *inExt);
+ static bool IsPathFileExtension(const wchar_t *inExt);
+ static bool IsPathBufferExtension(const wchar_t *inExt);
+ static bool IsSoundFileExtension(const wchar_t *inExt);
+
+ CUICFile GetExportChoice(const Q3DStudio::CString &inExtension,
+ const Q3DStudio::CString &inDefaultName);
+
+ std::pair<CUICFile, bool> GetSaveAsChoice(const Q3DStudio::CString &inDialogTitle = "",
+ bool inFilenameUntitled = false);
+ // Returns pair of file along with a boolean indicating the state of the create
+ // new directory checkbox.
+ std::pair<CUICFile, bool>
+ GetNewDocumentChoice(const Q3DStudio::CString &inInitialDirectory = Q3DStudio::CString());
+ CUICFile GetFileOpenChoice(const Q3DStudio::CString &inInitialDirectory = Q3DStudio::CString());
+
+ void DisplayImportFailed(const QUrl &inURL, const QString &inDescription,
+ bool inWarningsOnly);
+ void DisplayLoadingPresentationFailed(const CUICFile &inPresentation, long inErrorIDS = -1);
+ void DisplaySavingPresentationFailed();
+ void DisplaySaveReadOnlyFailed(const CUICFile &inSavedLocation);
+ CUICMessageBox::EMessageBoxReturn DisplayMessageBox(const Q3DStudio::CString &inTitle,
+ const Q3DStudio::CString &inText,
+ CUICMessageBox::EMessageBoxIcon inIcon,
+ bool inShowCancel);
+ int DisplayChoiceBox(const Q3DStudio::CString &inTitle, const Q3DStudio::CString &inText,
+ int inIcon);
+ void DisplayKnownErrorDialog(const Q3DStudio::CString &inErrorText);
+
+ bool LocateFileReference(CUICFile &outFile, long inFileType);
+ bool LocateFolderReference(CUICFile &outFile);
+ bool BrowseForFolderDialog(CUICFile &outFile, Q3DStudio::CString inDefaultDir,
+ Q3DStudio::CString inDialogTitle);
+
+ ESavePromptResult PromptForSave();
+ bool PromptForKeyframeInterpolation(float &ioEaseIn, float &ioEaseOut);
+
+ bool ConfirmRevert();
+
+ void DisplayProgressScreen(const Q3DStudio::CString &inActionText,
+ const Q3DStudio::CString &inFileName,
+ const Q3DStudio::CString &inWindowTitle);
+ void DestroyProgressScreen();
+
+ bool PromptObjectTimebarColor(CColor &ioColor);
+ void DisplayProfilingStatistics();
+ /*void DisplayMultilineTextEdit(Q3DStudio::CString &ioText,
+ CMultilineEditDlg::INotification *inNotifiction = NULL);*/
+ IProgressCallback *GetProgressScreen() const;
+
+ void DisplayEnvironmentVariablesError(const Q3DStudio::CString &inErrorMessage);
+
+ Q3DStudio::CString GetFilePathChoice(const Q3DStudio::CString &inFileExtensionFilter,
+ const Q3DStudio::CString &inDefault,
+ const Q3DStudio::CString *inDialogTitle = NULL);
+ void ResetSettings(const Q3DStudio::CString &inCurrentDocPath = "");
+
+ bool DisplayResetKeyframeValuesDlg();
+ void DisplayPasteFailed();
+
+ static void DisplayGLVersionError(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inMinVersion);
+ static void DisplayGLVersionWarning(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inRecommendedVersion);
+
+protected:
+ QString CreateAllowedTypesString(long inFileTypeFilter, bool inForImport);
+ static void DisplayGLVersionDialog(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inRecommendedVersion,
+ bool inError);
+
+#ifdef WIN32
+ CProgressPalette *m_ProgressPalette;
+#endif
+ bool m_ShowGUI;
+
+ Q3DStudio::CString m_LastSaveFile; ///< Path to the file was previously saved
+};
+#endif // INCLUDED_DIALOGS_H
diff --git a/src/Authoring/Studio/Workspace/Views/Views.h b/src/Authoring/Studio/Workspace/Views/Views.h
new file mode 100644
index 00000000..52b075e0
--- /dev/null
+++ b/src/Authoring/Studio/Workspace/Views/Views.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_VIEWS_H
+#define INCLUDED_VIEWS_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CMainFrame;
+class CStudioApp;
+class CHotKeys;
+class CStudioPaletteBar;
+class CStudioDialog;
+#ifdef WIN32
+class CRuntimeClass;
+class CFrameWnd;
+#endif
+
+#include <UICString.h>
+
+//==============================================================================
+// Class
+//==============================================================================
+
+class CViews
+{
+public:
+ CViews(CStudioApp *inStudioApp);
+ virtual ~CViews();
+
+ // Implementation
+ void CreateViews();
+ void DestroyViews();
+
+ // Keyboard
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+
+ // Main Frame
+ CMainFrame *GetMainFrame(); // ONLY THE APP SHOULD USE THIS FUNCTION- ABSOLUTELY NO ONE ELSE!!!!
+
+ void RecheckMainframeSizingMode();
+
+#ifdef WIN32
+ static void CreatePaletteAndView(CRuntimeClass *inClass, CStudioPaletteBar *inPalette,
+ CFrameWnd *inParent, Q3DStudio::CString inWindowTitle);
+ static void CreateDialogAndView(CRuntimeClass *inClass, CStudioDialog *inDialog,
+ CFrameWnd *inParent, Q3DStudio::CString inWindowTitle);
+#endif
+
+protected:
+ CMainFrame *m_MainFrame; ///<
+ Q3DStudio::CString m_HelpURL; ///<
+};
+
+#endif // INCLUDED_VIEWS_H
diff --git a/src/Authoring/Studio/_Win/Application/AboutDlg.cpp b/src/Authoring/Studio/_Win/Application/AboutDlg.cpp
new file mode 100644
index 00000000..533a5601
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/AboutDlg.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "qtAuthoring-config.h"
+#include "StudioDefs.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "AboutDlg.h"
+#include "ui_AboutDlg.h"
+#include "ProductInfo.h"
+#include "HotKeys.h"
+#include "Preferences.h"
+#include "StudioPreferences.h"
+
+#include <QMouseEvent>
+#include <QPainter>
+#include <QTimer>
+
+//==============================================================================
+// Constants
+//==============================================================================
+const int TIMER_RESIZEABOUT = 6;
+const int TIMER_FADEOUT = 7;
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+CAboutDlg::CAboutDlg(QWidget* parent)
+ : QDialog(parent, Qt::MSWindowsFixedSizeDialogHint)
+ , m_ScrollingCreditsFlag(false)
+ , m_ui(new Ui::AboutDlg)
+{
+ m_ui->setupUi(this);
+ m_IconRect = m_ui->icon->pixmap()->rect();
+ setFixedSize(size());
+
+ m_Font = QFont(CStudioPreferences::GetFontFaceName());
+ m_Font.setPointSizeF(7.8);
+ m_Font = QFont(CStudioPreferences::GetFontFaceName());
+ m_Font.setPointSizeF(8.8);
+
+ m_Color_Background = CStudioPreferences::GetDarkBaseColor();
+ m_Color_Text = CStudioPreferences::GetNormalColor();
+
+ OnInitDialog();
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+CAboutDlg::~CAboutDlg()
+{
+}
+
+//==============================================================================
+/**
+ * Handles the painting of the icon in the About Box.
+ * All additional painting (text, etc) occurs by default by MFC.
+ */
+//==============================================================================
+void CAboutDlg::paintEvent(QPaintEvent* event)
+{
+ Q_UNUSED(event);
+ if (!m_ScrollingCreditsFlag) {
+ DrawFadeRect();
+ }
+}
+
+//==============================================================================
+/**
+ * Completes the construction of the About Box.
+ * Stores the product version in the member variable associated with the static
+ * text box in the dialog template. This version information comes from the
+ * VersionNumber.h file in $/Studio/Build.
+ * @return Returns TRUE always.
+ */
+void CAboutDlg::OnInitDialog()
+{
+ // Set the Studio version
+ m_ProductVersionStr.Format(
+ ::LoadResourceString(IDS_UIC_STUDIO_VERSION),
+ static_cast<const wchar_t *>(CStudioPreferences::GetVersionString()));
+
+ // Set the copyright string
+ m_CopyrightStr.Format(::LoadResourceString(IDS_ABOUT_COPYRIGHT),
+ static_cast<const wchar_t *>(Q3DStudio::CString(STUDIO_COPYRIGHT_YEAR)));
+
+ // Set the credit strings
+ m_Credit1Str.Format(::LoadResourceString(IDS_ABOUT_PAINTLIB_CREDIT));
+#ifdef QT_3DSTUDIO_FBX
+ m_Credit2Str.Format(::LoadResourceString(IDS_ABOUT_FBX_CREDIT));
+#endif
+
+#ifdef STUDIOSTORYNUM
+ m_ProductVersionStr += " (Story #";
+ m_ProductVersionStr += STUDIOSTORYNUM;
+ m_ProductVersionStr += ")";
+#endif
+
+#ifdef BETA
+ // Add "beta" to the Studio version if necessary
+ m_ProductVersionStr += " BETA";
+#endif
+
+ // Add link to Web site
+ Q3DStudio::CString theURL(::LoadResourceString(IDS_HELP_VISIT_QT));
+
+ m_ui->m_WebSite->setText(QString("<a href=\"%1\">%2</a>").arg(theURL.toQString(), theURL.toQString()));
+ m_ui->m_WebSite->setToolTip(::LoadResourceString(IDS_WEBSITELINK).toQString());
+ m_ui->m_WebSite->setOpenExternalLinks(true);
+
+ // Add link to support email address
+ Q3DStudio::CString theEmail;
+ Q3DStudio::CString theEmailSansProtocol;
+
+ theEmail = ::LoadResourceString(IDS_SUPPORTEMAIL);
+ theEmailSansProtocol = "support@qt.io";
+
+ m_ui->m_Email->setText(QString("<a href=\"%1\">%2</a>").arg(theEmail.toQString(), theEmailSansProtocol.toQString()));
+ m_ui->m_Email->setToolTip(::LoadResourceString(IDS_SUPPORTEMAIL_TEXT).toQString());
+ m_ui->m_Email->setOpenExternalLinks(true);
+
+ // Set the fonts
+ m_ui->m_Copyright->setFont(m_Font);
+ m_ui->m_Credit1->setFont(m_Font);
+ m_ui->m_Credit2->setFont(m_Font);
+ m_ui->m_WebSiteLabel->setFont(m_Font);
+ m_ui->m_WebSite->setFont(m_Font);
+ m_ui->m_EmailLabel->setFont(m_Font);
+ m_ui->m_Email->setFont(m_Font);
+
+ // Make the font bold for version numbers
+ m_ui->m_ProductVersion->setFont(m_BoldFont);
+
+ // Create brush for background color
+ m_Brush = QBrush(m_Color_Background);
+
+ m_ui->m_ProductVersion->setText(m_ProductVersionStr.toQString());
+ m_ui->m_Copyright->setText(m_CopyrightStr.toQString());
+ m_ui->m_Credit1->setText(m_Credit1Str.toQString());
+ m_ui->m_Credit2->setText(m_Credit2Str.toQString());
+
+ // Hide the email link until we have a valid one.
+ m_ui->m_EmailLabel->setVisible(false);
+ m_ui->m_Email->setVisible(false);
+}
+
+//==============================================================================
+/**
+ * Handles the WM_LBUTTONDBLCLK message
+ * Check to see if the mouse is within the Qt logo and the control key is pressed.
+ * If so, begin the transition to show the detailed credits.
+ */
+void CAboutDlg::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ // If the mouse's left button is double-clicked in the Qt logo while the control key is
+ // pressed...
+ if (m_IconRect.contains(event->pos())) {
+ // if ( CHotKeys::IsKeyDown( VK_CONTROL ) )
+ //{
+ // Start the transition to display the scrolling credits
+ // Crashing... this->BeginCreditsTransition();
+ //}
+ }
+
+ QDialog::mouseDoubleClickEvent(event);
+}
+
+//==============================================================================
+/**
+ * BeginCreditsTransition: Begin the transition from the About box to the scrolling credits.
+ */
+void CAboutDlg::BeginCreditsTransition()
+{
+ m_ScrollingCreditsFlag = TRUE;
+
+ // Hide all controls
+ for (QWidget* w : findChildren<QWidget*>())
+ w->setVisible(false);
+
+ // Invalidate the window
+ update();
+
+ // Set the resizing timer
+ QTimer::singleShot(50, this, &CAboutDlg::ResizeAbout);
+}
+
+//==============================================================================
+/**
+ * Resize the about box height until the Client area is square.
+ */
+void CAboutDlg::ResizeAbout()
+{
+ bool retriggerResizeAbout = true;
+
+ QRect theClientRect = rect();
+ bool theRightSize = false;
+
+ // this is just a sizing stuff to show some transition on size
+ if (theClientRect.height() < theClientRect.width()) {
+ theClientRect.setBottom(theClientRect.bottom() + 5);
+ if (theClientRect.bottom() >= theClientRect.width()) {
+ theRightSize = true;
+ theClientRect.setBottom(theClientRect.width());
+ retriggerResizeAbout = false;
+ }
+ } else {
+ theClientRect.setRight(theClientRect.right() + 5);
+ if (theClientRect.right() >= theClientRect.height()) {
+ theRightSize = true;
+ theClientRect.setRight(theClientRect.height());
+ retriggerResizeAbout = false;
+ }
+ }
+
+ if (parentWidget())
+ theClientRect.moveCenter(parentWidget()->geometry().center());
+ else
+ theClientRect.moveTopLeft(geometry().topLeft());
+
+ setGeometry(theClientRect);
+
+ if (theRightSize) {
+ QColor the3DColor;
+ short theColorValue;
+
+ m_CreditsRect = rect().adjusted(3, 3, -3, -3);
+
+ the3DColor = palette().color(QPalette::Base);
+ theColorValue =
+ (short)((the3DColor.red() + the3DColor.green() + the3DColor.blue()) / 3);
+
+ m_ColorFade = RGB(theColorValue, theColorValue, theColorValue);
+
+ QTimer::singleShot(50, this, &CAboutDlg::FadeOut);
+ }
+
+ if (retriggerResizeAbout) {
+ QTimer::singleShot(50, this, &CAboutDlg::ResizeAbout);
+ }
+}
+
+//==============================================================================
+/**
+ * Fade out a rectangle in the client area, from the background 3D color to black.
+ */
+void CAboutDlg::FadeOut()
+{
+ update();
+
+ // Continue fading to black
+ if (m_ColorFade != Qt::black) {
+ short theColorValue;
+
+ theColorValue = (short)(m_ColorFade.red() - 10);
+
+ if (theColorValue < 0)
+ theColorValue = 0;
+
+ m_ColorFade = qRgb(theColorValue, theColorValue, theColorValue);
+ QTimer::singleShot(50, this, &CAboutDlg::FadeOut);
+ }
+}
+
+//==============================================================================
+/**
+ * DrawFadeRect: Draw the faded rectangle as we transition to the scrolling credits.
+ */
+void CAboutDlg::DrawFadeRect()
+{
+ QPainter painter(this);
+ painter.fillRect(m_CreditsRect, m_ColorFade);
+}
+
+void CAboutDlg::OnStnClickedAboutboxProdver()
+{
+ // TODO: Add your control notification handler code here
+}
diff --git a/src/Authoring/Studio/_Win/Application/AboutDlg.h b/src/Authoring/Studio/_Win/Application/AboutDlg.h
new file mode 100644
index 00000000..244e7192
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/AboutDlg.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_ABOUT_DLG_H
+#define INCLUDED_ABOUT_DLG_H 1
+
+#pragma once
+
+#include <QDialog>
+
+#include "UICString.h"
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class AboutDlg;
+}
+QT_END_NAMESPACE
+
+class CAboutDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ CAboutDlg(QWidget* parent = nullptr);
+ ~CAboutDlg();
+
+ // Implementation
+protected:
+ bool m_ScrollingCreditsFlag = false; // TRUE if credits are being scrolled
+ QRect m_IconRect; // bounding rectangle to display the about image
+ QRect m_CreditsRect; // bounding rectangle for the scrolling credits
+ QColor m_ColorFade; // fade-out color to fade to the credits window
+ QFont m_Font; // Font for text
+ QFont m_BoldFont; // Font for Studio/Client version number
+
+ QColor m_Color_Background;
+ QColor m_Color_Text;
+ QBrush m_Brush;
+
+ void BeginCreditsTransition();
+
+ void DrawFadeRect();
+
+ void FadeOut();
+
+ void ResizeAbout();
+
+ //{{AFX_MSG(CAboutDlg)
+ void paintEvent(QPaintEvent* event) override;
+ void OnInitDialog();
+
+ void mouseDoubleClickEvent(QMouseEvent* event) override;
+
+ Q3DStudio::CString m_ProductVersionStr;
+ Q3DStudio::CString m_CopyrightStr;
+ Q3DStudio::CString m_Credit1Str;
+ Q3DStudio::CString m_Credit2Str;
+
+public:
+ void OnStnClickedAboutboxProdver();
+
+private:
+ QScopedPointer<Ui::AboutDlg> m_ui;
+};
+
+#endif // INCLUDED_ABOUT_DLG_H
diff --git a/src/Authoring/Studio/_Win/Application/AboutDlg.ui b/src/Authoring/Studio/_Win/Application/AboutDlg.ui
new file mode 100644
index 00000000..a5fe3433
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/AboutDlg.ui
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AboutDlg</class>
+ <widget class="QDialog" name="AboutDlg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>502</width>
+ <height>483</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>About Qt 3D Studio</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="icon">
+ <property name="pixmap">
+ <pixmap resource="../../MainFrm.qrc">:/res/About Icon.bmp</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="Line" name="m_Line1">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_ProductVersion">
+ <property name="text">
+ <string>&lt;studio version goes here&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_Copyright">
+ <property name="text">
+ <string>&lt;Copyright goes here&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_Credit1">
+ <property name="text">
+ <string>&lt;Credit 1 goes here&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_Credit2">
+ <property name="text">
+ <string>&lt;Credit 2 goes here&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="m_Line2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_WebSiteLabel">
+ <property name="text">
+ <string>Web site:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_WebSite">
+ <property name="text">
+ <string>&lt;Link to support site here&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_EmailLabel">
+ <property name="text">
+ <string>Support email:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_Email">
+ <property name="text">
+ <string>&lt;Link to support email here&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../MainFrm.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AboutDlg</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AboutDlg</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/Authoring/Studio/_Win/Application/BaseLink.cpp b/src/Authoring/Studio/_Win/Application/BaseLink.cpp
new file mode 100644
index 00000000..5e7b4775
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/BaseLink.cpp
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+#ifdef _DEBUG
+//#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "BaseLink.h"
+#include "Resource.h"
+#include "StudioColors.h"
+
+// The IDC_HAND should be defined in WinUser.h if the WINVER is 0x5000 or greater
+#ifndef IDC_HAND
+#define IDC_HAND MAKEINTRESOURCE(32649)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CBaseLink class
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CBaseLink::CBaseLink()
+{
+ m_bCapture = FALSE;
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CBaseLink::~CBaseLink()
+{
+}
+
+//==============================================================================
+// Message Map
+//==============================================================================
+
+BEGIN_MESSAGE_MAP(CBaseLink, CWnd)
+//{{AFX_MSG_MAP(CBaseLink)
+ON_WM_PAINT()
+ON_WM_SETCURSOR()
+ON_WM_MOUSEMOVE()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * ShowTextLink: Shows the text link.
+ *
+ * @param None
+ */
+//==============================================================================
+void CBaseLink::ShowTextLink(COLORREF inTextColor, COLORREF inBackColor, UINT inTextFormat,
+ bool inUnderlineText)
+{
+ if (IsWindow(m_hWnd)) {
+ CRect theRect;
+ CFont *theFont;
+ CFont *theOldFont = nullptr;
+ CDC *theDC;
+ CDC theMemDC;
+ CString theTextBuffer;
+
+ // Set up the link font.
+ theFont = GetFont();
+ if (theFont != nullptr) {
+ LOGFONT theLogFont;
+
+ theFont->GetLogFont(&theLogFont);
+
+ // Turn on the underline style...
+ if (inUnderlineText)
+ theLogFont.lfUnderline = TRUE;
+ else
+ theLogFont.lfUnderline = FALSE;
+
+ theFont = new CFont();
+
+ if (theFont != nullptr) {
+ theFont->CreateFontIndirect(&theLogFont);
+ }
+ }
+
+ GetClientRect(&theRect);
+ GetWindowText(theTextBuffer);
+
+ theDC = GetDC();
+
+ theDC->SetTextColor(inTextColor);
+ theDC->SetBkColor(inBackColor);
+
+ // Fill the background.
+ theDC->FillSolidRect(&theRect, inBackColor);
+
+ // Set the font and draw the text.
+ if (theFont != nullptr) {
+ theOldFont = (CFont *)theDC->SelectObject(theFont);
+ }
+ theDC->DrawText(theTextBuffer, &theRect, inTextFormat);
+
+ if (theOldFont != nullptr) {
+ theDC->SelectObject(theOldFont);
+ }
+ ReleaseDC(theDC);
+
+ if (theFont != nullptr) {
+ theFont->DeleteObject();
+ delete theFont;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CBaseLink message handlers
+
+//==============================================================================
+/**
+ * OnPaint - Handler for the WM_PAINT message
+ *
+ * Draw the control.
+ *
+ * @param None
+ */
+//==============================================================================
+void CBaseLink::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ if (IsWindowEnabled())
+ this->ShowLink();
+}
+
+//==============================================================================
+/**
+ * OnSetCursor
+ *
+ * Handle the WM_SETCURSOR message.
+ *
+ * @param inWnd Pointer to the parent CWnd object.
+ * @param inHitTest Specifies the hit-test area code. The hit test determines the
+ *cursor�s location.
+ * @param inMessage Specifies the mouse message number.
+ */
+//==============================================================================
+BOOL CBaseLink::OnSetCursor(CWnd *inWnd, UINT inHitTest, UINT inMessage)
+{
+ UNREFERENCED_PARAMETER(inWnd);
+ UNREFERENCED_PARAMETER(inHitTest);
+ UNREFERENCED_PARAMETER(inMessage);
+
+ SetCursor(LoadCursor(nullptr, IDC_HAND));
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * OnMouseMove
+ *
+ * Windows callback handler for the WM_MOUSEMOVE message
+ *
+ * @param inFlags Flags for the left button up message
+ * @param inPoint Point where the left button was last moved (client coordinates)
+ */
+//==============================================================================
+void CBaseLink::OnMouseMove(UINT inFlags, CPoint inPoint)
+{
+ UNREFERENCED_PARAMETER(inFlags);
+ UNREFERENCED_PARAMETER(inPoint);
+
+ if (IsWindowEnabled()) {
+
+ if (!m_bCapture) {
+ SetCursor(LoadCursor(nullptr, IDC_HAND));
+ SetCapture();
+
+ m_bCapture = TRUE;
+ this->ShowLink();
+ } else {
+ // Already captured...
+ if (!this->IsMouseOverLink()) {
+ m_bCapture = FALSE;
+ ReleaseCapture();
+
+ this->ShowLink();
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * IsMouseOverLink: Determine if the mouse is over the control.
+ *
+ * @param None
+ *
+ * @return true if the mouse is over the control.
+ */
+//==============================================================================
+bool CBaseLink::IsMouseOverLink()
+{
+ POINT theMousePos;
+ RECT theWndRect;
+
+ ::GetCursorPos(&theMousePos);
+ GetWindowRect(&theWndRect);
+
+ return PtInRect(&theWndRect, theMousePos) ? true : false;
+}
diff --git a/src/Authoring/Studio/_Win/Application/BaseLink.h b/src/Authoring/Studio/_Win/Application/BaseLink.h
new file mode 100644
index 00000000..4d89a155
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/BaseLink.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef _BASELINK_H_
+#define _BASELINK_H_
+
+/////////////////////////////////////////////////////////////////////////////
+// CBaseLink window
+
+class CBaseLink : public CWnd
+{
+ // Construction
+public:
+ CBaseLink();
+
+ // Attributes
+public:
+ // Operations
+public:
+protected:
+ bool m_bCapture;
+
+ virtual bool IsMouseOverLink();
+ virtual void ShowLink() = 0;
+ virtual void ShowTextLink(COLORREF inTextColor, COLORREF inBackColor, UINT inTextFormat,
+ bool inUnderlineText);
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CBaseLink)
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ virtual ~CBaseLink();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CBaseLink)
+ virtual afx_msg void OnPaint();
+ virtual afx_msg BOOL OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message);
+ virtual afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ //}}AFX_MSG
+
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Application/MsgRouter.cpp b/src/Authoring/Studio/_Win/Application/MsgRouter.cpp
new file mode 100644
index 00000000..4ce57cc8
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/MsgRouter.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MsgRouter.h"
+#include "StudioConst.h"
+#include "Core.h"
+#include "Doc.h"
+#include "Views.h"
+
+#include <QCoreApplication>
+
+static std::unique_ptr<CMsgRouter> static_theInstance;
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CMsgRouter::CMsgRouter()
+{
+ qApp->installEventFilter(this);
+ m_eventType = QEvent::registerEventType();
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CMsgRouter::~CMsgRouter()
+{
+}
+
+CMsgRouter *CMsgRouter::GetInstance()
+{
+ if (!static_theInstance) {
+ static_theInstance.reset(new CMsgRouter);
+ }
+
+ return static_theInstance.get();
+}
+
+//==============================================================================
+/**
+ * Send a command to be executed asynchronously.
+ */
+void CMsgRouter::SendCommand(CCmd *inCommand, CCore *inCore)
+{
+ SMessageData *theMsgData = new SMessageData(m_eventType);
+ theMsgData->Method = &CMsgRouter::OnCommand;
+ theMsgData->Data = inCommand;
+ theMsgData->Data2 = inCore;
+
+ qApp->postEvent(qApp, theMsgData);
+}
+
+bool CMsgRouter::eventFilter(QObject *watched, QEvent *event)
+{
+ if (event->type() == m_eventType) {
+ SMessageData *theMsgData = reinterpret_cast<SMessageData *>(event);
+ (this->*(theMsgData->Method))(theMsgData);
+ return true;
+ }
+
+ return false;
+}
+
+//==============================================================================
+/**
+ * Windows entry point for processing generic messages.
+ */
+void CMsgRouter::OnAsyncNotification(SMessageData *inMessageData)
+{
+ try {
+ CRoutedMessageBase *theRoutedMsg = static_cast<CRoutedMessageBase *>(inMessageData->Data);
+ if (theRoutedMsg)
+ theRoutedMsg->Notify();
+ } catch (...) {
+ // Catch crashes in case the object is gone
+ }
+}
+
+//==============================================================================
+/**
+ * Main thread processing for processing command execution messages.
+ */
+void CMsgRouter::OnCommand(SMessageData *inMsgData)
+{
+ CCmd *theCommand = static_cast<CCmd *>(inMsgData->Data);
+ CCore *theCore = static_cast<CCore *>(inMsgData->Data2);
+ theCore->ExecuteCommand(theCommand, true);
+}
+
+
+
+CMsgRouter::SMessageData::SMessageData(int eventType) :
+ QEvent(static_cast<QEvent::Type>(eventType))
+{
+}
diff --git a/src/Authoring/Studio/_Win/Application/StudioApp.cpp b/src/Authoring/Studio/_Win/Application/StudioApp.cpp
new file mode 100644
index 00000000..fa160431
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/StudioApp.cpp
@@ -0,0 +1,1835 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+#ifdef _WIN32
+#pragma warning(disable : 4100) // unreferenced formal parameter
+#endif
+#include "StudioApp.h"
+#include "SubPresentationsDlg.h"
+#include "UICStateApplication.h"
+
+#include <QtGui/qsurfaceformat.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qurl.h>
+
+int main(int argc, char *argv[])
+{
+ // to enable QOpenGLWidget to work on macOS, we must set the default
+ // QSurfaceFormat before QApplication is created. Otherwise context-sharing
+ // fails and QOpenGLWidget breaks.
+
+ // fortunately, we know which OpenGL version we can use on macOS, so we
+ // can simply hard-code it here.
+#if defined(Q_OS_MACOS)
+ QSurfaceFormat openGL33Format;
+ openGL33Format.setRenderableType(QSurfaceFormat::OpenGL);
+ openGL33Format.setProfile(QSurfaceFormat::CoreProfile);
+ openGL33Format.setMajorVersion(3);
+ openGL33Format.setMinorVersion(3);
+ openGL33Format.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(openGL33Format);
+#endif
+
+ // init runtime static resources
+ Q_INIT_RESOURCE(res);
+
+ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication guiApp(argc, argv);
+ // Load and apply stylesheet for the application
+ QFile styleFile(":/style.qss");
+ styleFile.open(QFile::ReadOnly);
+ guiApp.setStyleSheet(styleFile.readAll());
+ g_StudioApp.InitInstance(argc, argv);
+ return g_StudioApp.Run();
+}
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Exceptions.h"
+#include "IOLibraryException.h"
+#include "Strings.h"
+#include "MainFrm.h"
+#include "AboutDlg.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "StackOps.h"
+#include "Views.h"
+#endif
+#include "StringLoader.h"
+#include "Doc.h"
+#include "Dialogs.h"
+#include "Dispatch.h"
+#include "StartupDlg.h"
+#include "RecentItems.h"
+#include "StudioPreferences.h"
+#include "MsgRouter.h"
+#include "SplashView.h"
+#include "Views.h"
+#include "UICLog.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "WinUtils.h"
+#include "CrashDlg.h"
+#endif
+#include "UICFile.h"
+#include "UICFileTools.h"
+#include "ITickTock.h"
+#include "IStudioRenderer.h"
+#include "IDocumentEditor.h"
+#include "StudioUtils.h"
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "IObjectReferenceHelper.h"
+#endif
+#include "ClientDataModelBridge.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "CommonConstants.h"
+#include "IOLibraryException.h"
+
+#ifdef _DEBUG
+#include "UICMemoryLeak.h"
+#endif
+#include "UICDMErrors.h"
+
+#include <iostream>
+#include <fstream>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#endif
+#include <string.h>
+
+#include <QApplication>
+#include <QSettings>
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "..\Build\versionnumber.h"
+
+#include "UICDESKey.h" // g_DESKey
+#endif
+
+#include "Core.h"
+#include "HotKeys.h"
+#include "StudioTutorialWidget.h"
+#include "GuideInspectable.h"
+#include "UICDMStudioSystem.h"
+#include "UICDMInspectable.h"
+#include "UICDMSlides.h"
+#include "UICDMMaterialInspectable.h"
+#include "UICDMSceneInspectable.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "UICDMAnimation.h"
+#include "UICDMDataCore.h"
+#include "SlideControl.h"
+#endif
+#include "IDirectoryWatchingSystem.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "ITickTock.h"
+#include "UICFileTools.h"
+#include "foundation/Qt3DSLogging.h"
+
+#endif
+#ifdef USE_LICENSE_HANDLER
+#include "licensehandler.h"
+#endif
+
+CStudioApp g_StudioApp;
+long g_UICErrorCode = 0;
+
+using namespace Q3DStudio;
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+char g_DumpPath[MAX_PATH] = { 0 };
+
+#ifndef WIN32
+namespace qt3ds
+{
+void NVAssert(const char *exp, const char *file, int line, bool *igonore)
+{
+ qFatal("NVAssertion thrown %s(%d): %s", file, line, exp);
+}
+}
+#endif
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CStudioApp::CStudioApp()
+#ifdef KDAB_TEMPORARILY_REMOVED
+ , m_SplashPalette(nullptr)
+ , m_OldHelpFilePath(nullptr)
+ , m_UnitTestResults(0)
+ , m_IsSilent(false)
+ , m_Core(NULL)
+ , m_Views(NULL)
+ , m_Dialogs(NULL)
+ , m_ManipulationMode(StudioManipulationModes::Local)
+ , m_PlaybackTime(0)
+ , m_pGdiToken(0)
+ , m_AuthorZoom(false)
+#endif
+ : m_ToolMode(STUDIO_TOOLMODE_MOVE)
+ , m_SelectMode(STUDIO_SELECTMODE_GROUP)
+ , m_welcomeShownThisSession(false)
+ , m_goStraightToWelcomeFileDialog(false)
+ , m_tutorialPage(0)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+#ifdef _DEBUG
+ AfxEnableMemoryTracking(TRUE);
+ CMemoryLeak::SetXMLFileName("StudioMemoryLeaks.xml");
+ CMemoryLeak::SetShowDialogOnExit(false);
+#endif // _DEBUG
+ g_DumpPath[0] = 0;
+#endif
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CStudioApp::~CStudioApp()
+{
+ // Do not call PerformShutdown from here as the C has already been shutdown (!!)
+}
+
+void CStudioApp::PerformShutdown()
+{
+ m_DirectoryWatcherTicker = std::shared_ptr<UICDM::ISignalConnection>();
+
+ // Dispatch un-registration
+ if (m_Core) {
+ m_Core->GetDispatch()->RemoveAppStatusListener(this);
+ m_Core->GetDispatch()->RemoveCoreAsynchronousEventListener(this);
+ qCInfo(qt3ds::TRACE_INFO) << "Studio exiting successfully";
+ }
+
+ if (m_Renderer) {
+ m_Renderer->Close();
+ m_Renderer = std::shared_ptr<Q3DStudio::IStudioRenderer>();
+ }
+
+ // No need to delete m_SplashPalette. You should not call delete on CFrameWnds,
+ // you should call DestroyWindow( ) instead. See the MSDN.
+ // delete m_SplashPalette;
+#ifdef KDAB_TEMPORARILY_REMOVED
+ delete m_Views;
+ m_Views = nullptr;
+ delete m_Dialogs;
+ m_Dialogs = nullptr;
+ delete m_Core;
+ m_Core = nullptr;
+#endif
+
+ CStringLoader::UnloadStrings();
+
+ // Get rid of the temp files
+ CUICFile::ClearCurrentTempCache();
+
+ qApp->exit();
+}
+
+//=============================================================================
+/**
+ * Entry location for the creation of this application.
+ * This creates the all the views, then returns if everything
+ * was successful.
+ */
+BOOL CStudioApp::InitInstance(int argc, char* argv[])
+{
+ QApplication::setOrganizationName("The Qt Company");
+ QApplication::setOrganizationDomain("qt.io");
+ QApplication::setApplicationName("Qt 3D Studio");
+ QApplication::setApplicationVersion(
+ QString::fromWCharArray(CStudioPreferences::GetVersionString().c_str()));
+
+ qCInfo(qt3ds::TRACE_INFO) << "Studio: " << QApplication::applicationFilePath();
+
+ // Load the strings used by the app gui
+ // This needs to occur prior to parsing command line arguments
+ // since access to the strings are required by some of the cases
+ CUICFile theResDir(CString::fromQString(resourcePath() + QStringLiteral("/strings")));
+ CStringLoader::LoadStrings(theResDir);
+ qCInfo(qt3ds::TRACE_INFO) << "Version: "
+ << CStudioPreferences::GetVersionString().GetCharStar();
+
+ std::vector<wchar_t*> wargv;
+ wargv.resize(argc);
+ for (int i = 0; i < argc; ++i) {
+ QString arg = argv[i];
+ wargv[i] = new wchar_t[arg.size() + 1];
+ wargv[i][arg.size()] = L'\0';
+ arg.toWCharArray(wargv[i]);
+ }
+
+ // Parse the command line so we know what's up
+ m_CmdLineParser.ParseArguments(argc, &(wargv[0]));
+
+ // Silent mode indicates that Studio will be operated in a muted "no-GUI" mode
+ m_IsSilent = m_CmdLineParser.IsSilent();
+
+ // If we're just running unit tests, return before creating windows/MFC controls
+ if (m_CmdLineParser.IsRunUnitTests()) {
+ {
+ RunCmdLineTests(m_CmdLineParser.GetFilename());
+ return FALSE; // return false so we bail from loading the app
+ }
+ }
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Standard initialization
+ // If you are not using these features and wish to reduce the size
+ // of your final executable, you should remove from the following
+ // the specific initialization routines you do not need
+ // Change the registry key under which our settings are stored
+ Q3DStudio::CString theRegistryKey =
+ ::LoadResourceString(IDS_UICOMPOSER_PALETTE_SETTINGS_REGISTRY_KEY);
+ SetRegistryKey(theRegistryKey);
+#endif
+
+ CFilePath thePreferencesPath = CFilePath::GetUserApplicationDirectory();
+ thePreferencesPath = CFilePath::CombineBaseAndRelative(
+ thePreferencesPath, CFilePath(L"UIComposer\\Preferences.setting"));
+ CPreferences::SetPreferencesFile(thePreferencesPath);
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Set up the path to the help file
+ InitHelpSystem();
+#endif
+
+ CStudioPreferences::LoadPreferences();
+
+ m_Dialogs = new CDialogs(!m_IsSilent);
+
+ if (!m_IsSilent) {
+ // Show the splash screen
+ m_SplashPalette = new CSplashView();
+ m_SplashPalette->setWindowTitle(::LoadResourceString(IDS_PROJNAME).toQString());
+ m_SplashPalette->show();
+
+ m_Views = new CViews(this);
+ }
+
+ // All custom icons (PNGs) are drawn using Gdiplus so that transparency are handled.
+ // note that gdiplus.dll are included with WinXP ( previous OS versions require the gdi
+ // redistributables to be installed and unless
+ // support for those OS becomes a priority, we assume Studio is run minimally on a XP machine )
+#ifdef KDAB_TEMPORARILY_REMOVED
+ Gdiplus::GdiplusStartup(&m_pGdiToken, &m_gdiplusStartupInput, nullptr); // gdi+ init
+#endif
+
+ m_Core = new CCore();
+ GetRenderer();
+ m_Core->GetDoc()->SetSceneGraph(m_Renderer);
+
+ // Dispatch registration
+ m_Core->GetDispatch()->AddAppStatusListener(this);
+ m_Core->GetDispatch()->AddCoreAsynchronousEventListener(this);
+
+ return TRUE;
+}
+
+//=============================================================================
+/**
+ * Exit location for the destruction of this application.
+ * @return 0 on success; -1 on failure
+ */
+int CStudioApp::ExitInstance()
+{
+ int theResult = -1;
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // CWinApp::InitInstance returns 1(TRUE) on success; 0(FALSE) on failure
+ if (CWinApp::InitInstance() == TRUE)
+ theResult = 0;
+
+ if (m_CmdLineParser.IsRunUnitTests() && 0 == m_UnitTestResults)
+ theResult = m_UnitTestResults; // unit tests return 0 on success; 1 on failure
+
+#ifdef _DEBUG
+ // If appears that MFC takes over dumping memory leaks on exit
+ // this is the only way I've been able to turn it off.
+
+ // Turn off dumping CRT memory leaks
+ ::_CrtSetReportMode(_CRT_WARN, 0);
+ ::_CrtSetReportMode(_CRT_ERROR, 0);
+ ::_CrtSetReportMode(_CRT_ASSERT, 0);
+#endif
+
+ // Make sure any GDI resources have been cleaned up
+ CResourceCache::GetInstance()->Clear();
+ // Called in conjuction with Gdiplus::GdiplusStartup
+ Gdiplus::GdiplusShutdown(m_pGdiToken);
+#endif
+
+ return theResult;
+}
+
+//=============================================================================
+/**
+ * Command handler to display the about dialog.
+ */
+void CStudioApp::OnAppAbout()
+{
+ CAboutDlg aboutDlg;
+ aboutDlg.exec();
+}
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+//=============================================================================
+/**
+ * Overrides registry access member functions, direct settings to a XML file
+ */
+UINT CStudioApp::GetProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault)
+{
+ QT3DS_ASSERT(lpszSection != nullptr);
+ QT3DS_ASSERT(lpszEntry != nullptr);
+
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%s\\%s\\%s", PALETTE_KEY, PALETTE_SUBKEY, lpszSection);
+ return CPreferences::GetUserPreferences(theSubKeyName).GetLongValue(lpszEntry, nDefault);
+}
+BOOL CStudioApp::WriteProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue)
+{
+ QT3DS_ASSERT(lpszSection != nullptr);
+ QT3DS_ASSERT(lpszEntry != nullptr);
+
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%s\\%s\\%s", PALETTE_KEY, PALETTE_SUBKEY, lpszSection);
+ CPreferences::GetUserPreferences(theSubKeyName).SetLongValue(lpszEntry, nValue);
+ return TRUE;
+}
+::CString CStudioApp::GetProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
+ LPCTSTR lpszDefault /* = nullptr */)
+{
+ QT3DS_ASSERT(lpszSection != nullptr);
+ QT3DS_ASSERT(lpszEntry != nullptr);
+
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%s\\%s\\%s", PALETTE_KEY, PALETTE_SUBKEY, lpszSection);
+ return CPreferences::GetUserPreferences(theSubKeyName).GetStringValue(lpszEntry, lpszDefault);
+}
+BOOL CStudioApp::WriteProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue)
+{
+ QT3DS_ASSERT(lpszSection != nullptr);
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%s\\%s\\%s", PALETTE_KEY, PALETTE_SUBKEY, lpszSection);
+
+ if (lpszEntry == nullptr) // delete whole section
+ {
+ CPreferences::GetUserPreferences(theSubKeyName).Clear();
+ } else if (lpszValue == nullptr) {
+ CPreferences::GetUserPreferences(theSubKeyName).RemoveKey(lpszEntry);
+ } else {
+ CPreferences::GetUserPreferences(theSubKeyName).SetStringValue(lpszEntry, lpszValue);
+ }
+ return TRUE;
+}
+#endif
+
+//=============================================================================
+/**
+ * Main application execution loop.
+ * The application's main thread stays in this until the app exits.
+ * @return 0 on success; -1 on failure
+ */
+int CStudioApp::Run()
+{
+ int theRetVal = -1;
+
+#ifdef USE_LICENSE_HANDLER
+ LicenseHandler lh;
+ if (!lh.handleLicense())
+ return theRetVal;
+#endif
+
+ try {
+ UIC_LOGSTART;
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Set the crash handler for all unhandled exceptions.
+ CStackOps::SetCrashProc(&StudioUnhandledCrashHandler);
+ CStackOps::RegisterCrashHandler();
+ CStackOps::EnableCrashingOnCrashes();
+#endif
+
+ CCmdLineParser::EExecutionMode theMode = m_CmdLineParser.PopExecutionMode();
+ if (CCmdLineParser::END_OF_CMDS == theMode)
+ theMode = CCmdLineParser::NORMAL;
+
+ for (; CCmdLineParser::END_OF_CMDS != theMode;
+ theMode = m_CmdLineParser.PopExecutionMode()) {
+ // This just switches the execution mode of the app and starts it in the correct state.
+ switch (theMode) {
+ case CCmdLineParser::TEST_CMD_LINE:
+ theRetVal = RunSystemTests(m_CmdLineParser.GetFilename());
+ break;
+ case CCmdLineParser::OPEN_FILE:
+ theRetVal = OpenAndRunApplication(m_CmdLineParser.GetFilename());
+ break;
+ default:
+ theRetVal = BlankRunApplication();
+ break;
+ }
+
+ // if any operations returned a bad value, stop following operations
+ if (-1 == theRetVal)
+ break;
+ }
+ PerformShutdown();
+
+ UIC_LOGSTOP;
+ } catch (CUICExceptionClass &inException) {
+ g_UICErrorCode = inException.GetErrorCode();
+ throw;
+ } catch (UICDM::UICDMError &uicdmError) {
+ Q_UNUSED(uicdmError);
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ EXCEPTION_POINTERS *pExPtrs;
+ CStackOps::GetExceptionPointers(1, &pExPtrs);
+ StudioUnhandledCrashHandler(pExPtrs);
+#endif
+ exit(1);
+ } catch (...) {
+ throw;
+ }
+
+ return theRetVal;
+}
+
+bool CStudioApp::HandleWelcomeRes(int res, bool recursive)
+{
+ int theReturn = true;
+ switch (res) {
+ case StudioTutorialWidget::createNewResult: {
+ std::pair<CUICFile, bool> theFile = m_Dialogs->
+ GetNewDocumentChoice(Q3DStudio::CString("."));
+ if (theFile.first.GetPath() != "") {
+ m_Core->OnNewDocument(theFile.first, theFile.second);
+ theReturn = true;
+ m_welcomeShownThisSession = true;
+ } else {
+ // User Cancels the dialog. Show the welcome screen.
+ if (recursive) {
+ m_welcomeShownThisSession = false;
+ m_goStraightToWelcomeFileDialog = true;
+ theReturn = ShowStartupDialog();
+ } else {
+ theReturn = false;
+ }
+ }
+ } break;
+
+ case StudioTutorialWidget::openSampleResult: {
+ // Try three options:
+ // - open a specific example directory with .uip file in it
+ // - failing that, show the main example root dir
+ // - failing all previous, show Qt3DStudio dir
+ Q3DStudio::CFilePath filePath;
+
+ filePath = CUICFile::GetApplicationDirectory().GetPath()+
+ Q3DStudio::CString("../examples/qmldynamickeyframes/presentation");
+
+ if (!filePath.Exists()) {
+ filePath = CUICFile::GetApplicationDirectory().GetPath()+
+ Q3DStudio::CString("../examples");
+ }
+ if (!filePath.Exists()) {
+ filePath = CUICFile::GetApplicationDirectory().GetPath()+
+ Q3DStudio::CString(".");
+ }
+
+ CUICFile theFile = m_Dialogs->GetFileOpenChoice(filePath);
+
+ if (theFile.GetPath() != "") {
+ OnLoadDocument(theFile);
+ theReturn = true;
+ m_welcomeShownThisSession = true;
+ } else {
+ // User Cancels the dialog. Show the welcome screen.
+ if (recursive) {
+ m_welcomeShownThisSession = false;
+ m_goStraightToWelcomeFileDialog = true;
+ theReturn = ShowStartupDialog();
+ } else {
+ theReturn = false;
+ }
+ }
+ } break;
+
+ default:
+ ASSERT(false); // Should not reach this block.
+ theReturn = false;
+ break;
+ }
+ return theReturn;
+}
+
+//=============================================================================
+/**
+ * Show startup dialog and perform necessary action such as create new doc or load doc.
+ * Return false if user requests to exit
+ */
+bool CStudioApp::ShowStartupDialog()
+{
+ int welcomeRes = QDialog::Rejected;
+ bool theReturn = true;
+
+ if (!m_welcomeShownThisSession){
+ m_welcomeShownThisSession = true;
+
+ bool show = false;
+ QSettings settings;
+
+ if (!settings.contains("showWelcomeScreen")) {
+ settings.setValue("showWelcomeScreen", 1);
+ show = true;
+ } else {
+ // if we are returning to welcome dialog page after canceling
+ // file dialog, do not care about settings but always show
+ // welcome
+ show = settings.value("showWelcomeScreen").toBool()
+ || m_goStraightToWelcomeFileDialog;
+ }
+
+ if (show) {
+ StudioTutorialWidget tutorial(m_goStraightToWelcomeFileDialog);
+ welcomeRes = tutorial.exec();
+ }
+ }
+
+ // show the usual startup dialog only if user rejected tutorial
+ // ( = did not open samples or create new project)
+ if (welcomeRes == QDialog::Rejected) {
+ CStartupDlg theStartupDlg;
+
+ // Populate recent items
+ Q3DStudio::CFilePath theMostRecentDirectory;
+ if (m_Views) {
+ CRecentItems *theRecentItems = m_Views->GetMainFrame()->GetRecentItems();
+ for (long theIndex = 0; theIndex < theRecentItems->GetItemCount(); ++theIndex) {
+ if (theIndex == 0) {
+ theMostRecentDirectory =
+ Q3DStudio::CFilePath(theRecentItems->GetItem(0).GetAbsolutePath())
+ .GetDirectory();
+ }
+ theStartupDlg.AddRecentItem(theRecentItems->GetItem(theIndex));
+ }
+ }
+
+ theStartupDlg.exec();
+ CStartupDlg::EStartupChoice theChoice = theStartupDlg.GetChoice();
+
+ switch (theChoice) {
+ case CStartupDlg::EStartupChoice_Exit:
+ theReturn = false;
+ break;
+
+ case CStartupDlg::EStartupChoice_NewDoc: {
+ std::pair<CUICFile, bool> theFile = m_Dialogs->
+ GetNewDocumentChoice(theMostRecentDirectory);
+ if (theFile.first.GetPath() != "") {
+ m_Core->OnNewDocument(theFile.first, theFile.second);
+ theReturn = true;
+ } else {
+ // User Cancels the dialog. Show startup dialog again.
+ theReturn = ShowStartupDialog();
+ }
+ } break;
+
+ case CStartupDlg::EStartupChoice_OpenDoc: {
+ CUICFile theFile = m_Dialogs->GetFileOpenChoice(theMostRecentDirectory);
+ if (theFile.GetPath() != "") {
+ OnLoadDocument(theFile);
+ theReturn = true;
+ } else {
+ // User Cancels the dialog. Show startup dialog again.
+ theReturn = ShowStartupDialog();
+ }
+ } break;
+
+ case CStartupDlg::EStartupChoice_OpenRecent: {
+ CUICFile theFile = theStartupDlg.GetRecentDoc();
+ if (theFile.GetPath() != "") {
+ OnLoadDocument(theFile);
+ // throw SlideNotFound( L"");
+ theReturn = true;
+ } else {
+ // User Cancels the dialog. Show startup dialog again.
+ theReturn = ShowStartupDialog();
+ }
+ } break;
+
+ default:
+ ASSERT(false); // Should not reach this block.
+ theReturn = false;
+ break;
+ }
+ } else { // open sample or create new
+ theReturn = HandleWelcomeRes(welcomeRes, true);
+ }
+ return theReturn;
+}
+
+//=============================================================================
+/**
+ * Start the app.
+ */
+int CStudioApp::BlankRunApplication()
+{
+ InitCore();
+
+ if (ShowStartupDialog())
+ return RunApplication();
+ return -1;
+}
+
+//=============================================================================
+/**
+ * Run the unit tests specified on the command line then return.
+ * @param inTestPath input unit test path
+ * @return 0 on success; 1 on failure
+ */
+int CStudioApp::RunCmdLineTests(const Q3DStudio::CString &inTestPath)
+{
+ Q_UNUSED(inTestPath);
+
+ return m_UnitTestResults;
+}
+
+//=============================================================================
+/**
+ * Run the system level tests specified on the command line then return.
+ * @param inTestPath
+ * @return 0 on success; -1 on failure
+ */
+int CStudioApp::RunSystemTests(const Q3DStudio::CString &inTestPath)
+{
+ int theSystemTestsResult = -1;
+
+ Q_UNUSED(inTestPath);
+
+ if (0 == m_UnitTestResults)
+ theSystemTestsResult = m_UnitTestResults; // unit tests return 0 on success; 1 on failure
+
+ return theSystemTestsResult;
+}
+
+//=============================================================================
+/**
+ * Open the specified file and run the application.
+ * This will load the file then go into the standard app loop.
+ * On load with the -silent flag, this would force the application to exit on
+ * load failures.
+ * @return 0 on success; -1 on failure
+ */
+int CStudioApp::OpenAndRunApplication(const Q3DStudio::CString &inFilename)
+{
+ int theSuccess = -1;
+ InitCore();
+ if (OnLoadDocument(
+ inFilename,
+ false)) // Load document. Upon failure, don't show startup dialog but exit immediately.
+ theSuccess = RunApplication();
+ return theSuccess;
+}
+
+//=============================================================================
+/**
+ * This is the app execution loop, the main thread loops here until the app exits.
+ * @return 0 on success; -1 on failure
+ */
+int CStudioApp::RunApplication()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ BOOL bIdle = TRUE;
+ LONG lIdleCount = 0;
+ MSG theMessage;
+
+ // CStackOps::RegisterCrashHandler( );
+
+ // acquire and dispatch messages until a WM_QUIT message is received.
+ for (;;) {
+ // phase1: check to see if we can do idle work
+ while (bIdle && !::PeekMessage(&theMessage, nullptr, nullptr, nullptr, PM_NOREMOVE)) {
+ // call OnIdle while in bIdle state
+ if (!OnIdle(lIdleCount++))
+ bIdle = FALSE; // assume "no idle" state
+ }
+
+ // phase2: pump messages while available
+ do {
+ try {
+ // Pump message, but quit on WM_QUIT
+ if (!PumpMessage())
+ return ExitInstance();
+ } catch (...) {
+ // Throw here so that any crash goes through the crash reporter
+ throw;
+ }
+
+ // Reset "no idle" state after pumping "normal" message
+ if (IsIdleMessage(&theMessage)) {
+ bIdle = TRUE;
+ lIdleCount = 0;
+ }
+ } while (::PeekMessage(&theMessage, nullptr, nullptr, nullptr, PM_NOREMOVE));
+ }
+#endif
+ return qApp->exec();
+}
+
+//=============================================================================
+/**
+ * Initialize the core and all the views.
+ */
+void CStudioApp::InitCore()
+{
+ // Initialize and cache the RenderSelector values for the first time,
+ // this way, subsequent attempts to instantiate a RenderSelector would circumvent the need
+ // for any extra (unneccesary) creation of render contexts which inadvertently cause exceptions
+ // to be thrown.
+
+ m_Core->Initialize();
+
+ if (m_Views) {
+ m_Views->CreateViews();
+ m_pMainWnd = m_Views->GetMainFrame();
+ } else {
+ ASSERT(0); // No views? wha?
+ }
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CMsgRouter::GetInstance()->SetMainframe(m_pMainWnd);
+#endif
+
+ // At this point, get rid of the splash screen, otherwise any errors dialog would be hidden
+ // behind.
+ // Could happen when this is directly activated due to a uip file being dbl-clicked or dragged
+ // into the executable.
+ if (m_SplashPalette) {
+ m_SplashPalette->deleteLater();
+ m_SplashPalette = nullptr;
+ }
+
+ RegisterGlobalKeyboardShortcuts(m_Core->GetHotKeys());
+ m_Core->GetDispatch()->AddPresentationChangeListener(this);
+}
+
+struct SIImportFailedHandler : public Q3DStudio::IImportFailedHandler
+{
+ CDialogs &m_Dialogs;
+ SIImportFailedHandler(CDialogs &dialogs)
+ : m_Dialogs(dialogs)
+ {
+ }
+ void DisplayImportFailed(const QString &inDocumentPath,
+ const QString &inDescription, bool inWarningsOnly) override
+ {
+ m_Dialogs.DisplayImportFailed(QUrl(inDocumentPath), inDescription, inWarningsOnly);
+ }
+};
+
+struct SIDeletingReferencedObjectHandler : public Q3DStudio::IDeletingReferencedObjectHandler
+{
+ CDialogs &m_Dialogs;
+
+ SIDeletingReferencedObjectHandler(CDialogs &dialogs)
+ : m_Dialogs(dialogs)
+ {
+ }
+
+ void DisplayMessageBox(const Q3DStudio::CString &inDescription) override
+ {
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_DELETE_OBJ_CONFIRM_TITLE));
+ Q3DStudio::CString theFormat(::LoadResourceString(IDS_DELETE_OBJ_CONFIRM_MSG));
+ Q3DStudio::CString theMessage;
+ theMessage.Format(theFormat, static_cast<const wchar_t *>(inDescription));
+
+ m_Dialogs.DisplayMessageBox(theTitle, theMessage, CUICMessageBox::ICON_WARNING, false);
+ }
+};
+
+void CStudioApp::SetupTimer(long inMessageId, QWidget *inWnd)
+{
+ m_TickTock = ITickTock::CreateTickTock(inMessageId, inWnd);
+ GetDirectoryWatchingSystem();
+ m_Core->GetDoc()->SetDirectoryWatchingSystem(m_DirectoryWatchingSystem);
+ m_Core->GetDoc()->SetImportFailedHandler(
+ std::make_shared<SIImportFailedHandler>(std::ref(*GetDialogs())));
+ m_Core->GetDoc()->SetDocMessageBoxHandler(
+ std::make_shared<SIDeletingReferencedObjectHandler>(std::ref(*GetDialogs())));
+}
+
+ITickTock &CStudioApp::GetTickTock()
+{
+ if (m_TickTock == nullptr)
+ throw std::runtime_error("Uninitialized TickTock");
+ return *m_TickTock;
+}
+
+Q3DStudio::IStudioRenderer &CStudioApp::GetRenderer()
+{
+ if (!m_Renderer)
+ m_Renderer = Q3DStudio::IStudioRenderer::CreateStudioRenderer();
+ return *m_Renderer;
+}
+
+void CStudioApp::ClearGuides()
+{
+ SCOPED_DOCUMENT_EDITOR(*m_Core->GetDoc(), QObject::tr("Clear Guides"))->ClearGuides();
+}
+
+void SendAsyncCommand(CDispatch &inDispatch, Q3DStudio::TCallbackFunc inFunc)
+{
+ inDispatch.FireOnAsynchronousCommand(inFunc);
+}
+
+IDirectoryWatchingSystem &CStudioApp::GetDirectoryWatchingSystem()
+{
+ if (m_DirectoryWatchingSystem == nullptr) {
+ Q3DStudio::TCallbackCaller theCaller =
+ std::bind(SendAsyncCommand, std::ref(*m_Core->GetDispatch()), std::placeholders::_1);
+ m_DirectoryWatchingSystem =
+ IDirectoryWatchingSystem::CreateThreadedDirectoryWatchingSystem(theCaller);
+ }
+ return *m_DirectoryWatchingSystem;
+}
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+bool s_testVar = true;
+//==============================================================================
+/**
+ * This gets called any time an unhandled exception is thrown anywhere in the
+ * application.
+ *
+ * @param pExPtrs a description of the exception.
+ * @return 0 if not handled, 1 if it was handled.
+ */
+void CStudioApp::StudioUnhandledCrashHandler(EXCEPTION_POINTERS *pExPtrs)
+{
+ TCHAR theTraceBuffer[4096];
+ CCrashDlg theDlg;
+ Q3DStudio::CString errorMessage;
+
+ CStackOps::GetExceptionStackTrace(theTraceBuffer, sizeof(theTraceBuffer),
+ pExPtrs->ContextRecord);
+ if (g_UICErrorCode != 0) {
+ // switch (g_UICErrorCode)
+ //{
+ // default:
+ Q3DStudio::CString description;
+ description.Format(_UIC("Custom error 0x%X "), g_UICErrorCode);
+ errorMessage = CStackOps::GetExceptionDescription(description, pExPtrs);
+ // break;
+ //}
+ } else {
+ errorMessage = CStackOps::GetExceptionDescription(pExPtrs);
+ }
+ theDlg.SetStackTrace(theTraceBuffer);
+ qCCritical(qt3ds::INTERNAL_ERROR) << "Crash!" << theTraceBuffer;
+ // Attempt to save the current project (although this may crash).
+ CDoc &theDoc = *g_StudioApp.m_Core->GetDoc();
+ CFilePath theFullPath = theDoc.GetDocumentPath().GetAbsolutePath();
+ CFilePath theDir = theFullPath.GetDirectory();
+ CFilePath theStem = theFullPath.GetFileStem();
+ theStem.append(L"_Crash");
+ // Attempt to open a dump file ideally next to the log file.
+ CFilePath theSaveFile;
+ {
+ TFilePtr theSaveFilePtr =
+ SFileTools::FindUniqueDestFile(theDir, theStem, CFilePath(L"uip"));
+ theSaveFile = theSaveFilePtr->m_Path;
+ }
+ theSaveFile.DeleteThisFile();
+ theDoc.SaveDocument(CUICFile(theSaveFile));
+ qCInfo(qt3ds::TRACE_INFO) << "Project successfully saved: " << theSaveFile.GetCharStar();
+
+ ::CString fileSaveLocationMessage;
+ fileSaveLocationMessage.Format(L"Project saved as %ls",
+ static_cast<const wchar_t *>(theSaveFile));
+
+ theDlg.SetErrorMessage(errorMessage);
+ theDlg.SetFilename(fileSaveLocationMessage);
+ theDlg.DoModal();
+}
+
+void CStudioApp::InitHelpSystem()
+{
+ Q3DStudio::CString theHelpFile = ::LoadResourceString(IDS_HELP_FILE_NAME);
+
+ ATL::CStringT<TCHAR, StrTraitMFC<TCHAR>> theOldHelpPath(m_pszHelpFilePath);
+ ATL::CStringT<TCHAR, StrTraitMFC<TCHAR>> theNewHelpPath;
+
+ long theIndex = theOldHelpPath.ReverseFind('\\');
+ theOldHelpPath.Delete(theIndex + 1, (theOldHelpPath.GetLength() - theIndex));
+ theNewHelpPath = theOldHelpPath;
+ ::CString theMFCHelpFile(theHelpFile.GetMulti());
+ theNewHelpPath += theMFCHelpFile;
+
+ // Save the m_pszHelpFilePath variable so that CWinApp::SetCurrentHandles()
+ // in appinit.cpp can deallocate the memory it allocated, which causes a
+ // mmgr.h assert when we free it in here
+ m_OldHelpFilePath = const_cast<LPTSTR>(m_pszHelpFilePath);
+
+ long theLength = theNewHelpPath.GetLength();
+ m_pszHelpFilePath = new TCHAR[theLength + 1];
+ ::lstrcpyn(const_cast<LPTSTR>(m_pszHelpFilePath), theNewHelpPath, theLength + 1);
+}
+#endif
+
+CCore *CStudioApp::GetCore()
+{
+ return m_Core;
+}
+
+//=============================================================================
+/**
+ * Get the view manager for this core to communicate to the views.
+ */
+CViews *CStudioApp::GetViews()
+{
+ return m_Views;
+}
+
+//=============================================================================
+/**
+ * Get the dialog manager for this core for displaying dialogs.
+ */
+CDialogs *CStudioApp::GetDialogs()
+{
+ return m_Dialogs;
+}
+
+long CStudioApp::GetToolMode()
+{
+ return m_ToolMode;
+}
+
+void CStudioApp::SetToolMode(long inToolMode)
+{
+ if (m_ToolMode != inToolMode) {
+ m_ToolMode = inToolMode;
+ m_Core->GetDispatch()->FireOnToolbarChange();
+ }
+}
+
+long CStudioApp::GetSelectMode()
+{
+ return m_SelectMode;
+}
+
+void CStudioApp::SetSelectMode(long inSelectMode)
+{
+ if (m_SelectMode != inSelectMode) {
+ m_SelectMode = inSelectMode;
+ m_Core->GetDispatch()->FireOnToolbarChange();
+ }
+}
+
+StudioManipulationModes::Enum CStudioApp::GetMinpulationMode() const
+{
+ return m_ManipulationMode;
+}
+void CStudioApp::SetMinpulationMode(StudioManipulationModes::Enum inManipulationMode)
+{
+ if (m_ManipulationMode != inManipulationMode) {
+ m_ManipulationMode = inManipulationMode;
+ m_Core->GetDispatch()->FireOnToolbarChange();
+ }
+}
+
+//=============================================================================
+/**
+ * return true if undo is possible
+ */
+bool CStudioApp::CanUndo()
+{
+ return m_Core->GetCmdStack()->CanUndo();
+}
+
+//=============================================================================
+/**
+ * return true if redo is possible
+ */
+bool CStudioApp::CanRedo()
+{
+ return m_Core->GetCmdStack()->CanRedo();
+}
+
+void CStudioApp::OnCopy()
+{
+ m_Core->GetDoc()->HandleCopy();
+}
+
+bool CStudioApp::CanCopy()
+{
+ return m_Core->GetDoc()->CanCopy();
+}
+
+//=============================================================================
+/**
+ * Get a string describing the type of the copy operation that can be done.
+ * Precedence of copying is 1) Actions; 2) Keyframes; 3) Objects
+ */
+Q3DStudio::CString CStudioApp::GetCopyType()
+{
+ Q3DStudio::CString theCopyType;
+
+ CDoc *theDoc = m_Core->GetDoc();
+ if (theDoc->CanCopyAction())
+ theCopyType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_ACTION);
+ else if (theDoc->CanCopyKeyframe())
+ theCopyType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_KEYFRAMES);
+ else
+ theCopyType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_OBJECT);
+
+ return theCopyType;
+}
+
+//=============================================================================
+/**
+ * Cuts the selected object or keys
+ */
+void CStudioApp::OnCut()
+{
+ m_Core->GetDoc()->HandleCut();
+}
+
+bool CStudioApp::CanCut()
+{
+ return m_Core->GetDoc()->CanCut();
+}
+
+//=============================================================================
+/**
+ * Paste keys from the copied list yo
+ */
+void CStudioApp::OnPaste()
+{
+ m_Core->GetDoc()->HandlePaste();
+}
+
+bool CStudioApp::CanPaste()
+{
+ return m_Core->GetDoc()->CanPaste();
+}
+
+//=============================================================================
+/**
+ * Get a string describing the type of the paste operation that can be done.
+ * Precedence of paste is 1) Actions; 2) Object ; 3) Keyframes
+ */
+Q3DStudio::CString CStudioApp::GetPasteType()
+{
+ Q3DStudio::CString thePasteType;
+
+ CDoc *theDoc = m_Core->GetDoc();
+ if (theDoc->CanPasteAction())
+ thePasteType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_ACTION);
+ else if (theDoc->CanPasteObject())
+ thePasteType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_OBJECT);
+ else
+ thePasteType = ::LoadResourceString(IDS_MENU_COPYPASTE_TYPE_KEYFRAMES);
+
+ return thePasteType;
+}
+
+bool CStudioApp::CanChangeTimebarColor()
+{
+ bool theRetVal = true;
+ UICDM::CUICDMInstanceHandle theSelectedInstance = m_Core->GetDoc()->GetSelectedInstance();
+ if (!theSelectedInstance.Valid()
+ || m_Core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge()->IsSceneInstance(
+ theSelectedInstance))
+ theRetVal = false;
+
+ return theRetVal;
+}
+
+//=============================================================================
+/**
+ * Sets any changed keyframes on the selected object
+ */
+void CStudioApp::HandleSetChangedKeys()
+{
+ m_Core->GetDoc()->SetChangedKeyframes();
+}
+
+//=============================================================================
+/**
+ * Deletes all selected keys
+ */
+void CStudioApp::DeleteSelectedKeys()
+{
+ m_Core->GetDoc()->DeleteSelectedKeys();
+}
+
+//=============================================================================
+/**
+ * Handles the duplicate object command
+ */
+void CStudioApp::HandleDuplicateCommand()
+{
+ m_Core->GetDoc()->HandleDuplicateCommand();
+}
+
+//=============================================================================
+/**
+ * return true if the selected object is duplicatable
+ */
+bool CStudioApp::CanDuplicateObject()
+{
+ // Get the currently selected object
+ UICDM::CUICDMInstanceHandle theSelectedInstance = m_Core->GetDoc()->GetSelectedInstance();
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Check if the object can be duplicated
+ return m_Core->GetDoc()->GetStudioSystem()->GetClientDataModelBridge()->IsDuplicateable(
+ theSelectedInstance);
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * Toggles the state of autoset keyframes.
+ */
+void CStudioApp::OnToggleAutosetKeyframes()
+{
+ SetAutosetKeyframes(!CStudioPreferences::IsAutosetKeyframesOn());
+
+ m_Core->GetDispatch()->FireOnToolbarChange();
+}
+
+//==============================================================================
+/**
+ * Updates the preferences, and AnimationSystem.
+ */
+void CStudioApp::SetAutosetKeyframes(bool inFlag)
+{
+ CStudioPreferences::SetAutosetKeyframesOn(inFlag);
+
+ m_Core->GetDoc()->GetStudioSystem()->GetAnimationSystem()->SetAutoKeyframe(inFlag);
+}
+
+//==============================================================================
+/**
+ * If the presentation is not currently playing, this function will make it
+ * start playing from the current position. The starting point of the playhead
+ * is saved so that it can be restored later.
+ */
+void CStudioApp::PlaybackPlay()
+{
+ CDoc *theDoc = m_Core->GetDoc();
+ if (!theDoc->IsPlaying()) {
+ m_PlaybackTime = theDoc->GetCurrentViewTime();
+ m_PlaybackOriginalSlide = theDoc->GetActiveSlide();
+ theDoc->SetPlayMode(PLAYMODE_PLAY);
+ }
+}
+
+//==============================================================================
+/**
+ * If the presentation is currently playing, it is stopped. The playhead is
+ * left wherever it was stopped at (hence it's not restored).
+ */
+void CStudioApp::PlaybackStopNoRestore()
+{
+ m_Core->GetDoc()->SetPlayMode(PLAYMODE_STOP);
+}
+
+//==============================================================================
+/**
+ * Moves the playhead back to time zero.
+ */
+void CStudioApp::PlaybackRewind()
+{
+ CDoc *theDoc = m_Core->GetDoc();
+ if (theDoc->IsPlaying()) {
+ theDoc->SetPlayMode(PLAYMODE_STOP, 0);
+ theDoc->SetPlayMode(PLAYMODE_PLAY);
+ } else {
+ m_Core->GetDoc()->NotifyTimeChanged(0);
+ }
+}
+
+//=============================================================================
+/**
+ * Performs a file revert.
+ * This will revert the doc to the last saved version.
+ */
+void CStudioApp::OnRevert()
+{
+ if (!m_Core->GetDoc()->IsModified() || m_Dialogs->ConfirmRevert()) {
+ CUICFile theCurrentDoc = m_Core->GetDoc()->GetDocumentPath();
+ OnLoadDocument(theCurrentDoc);
+ }
+}
+
+//=============================================================================
+/**
+ * Check to see if it is possible to perform a revert.
+ */
+bool CStudioApp::CanRevert()
+{
+ return m_Core->GetDoc()->IsModified() && m_Core->GetDoc()->GetDocumentPath().GetPath() != "";
+}
+
+//==============================================================================
+/**
+ * Handles the recent list.
+ */
+void CStudioApp::OnFileOpenRecent(const CUICFile &inDocument)
+{
+ if (PerformSavePrompt())
+ OnLoadDocument(inDocument);
+}
+
+//==============================================================================
+/**
+ * Called when closing the current doc, this prompts the user to save the doc.
+ * This will only prompt if the doc is modified, and if the user selects save
+ * then this will perform the save operation.
+ * @return true if the operation should continue, false if not.
+ */
+bool CStudioApp::PerformSavePrompt()
+{
+ if (m_Core->GetDoc()->IsModified()) {
+ CDialogs::ESavePromptResult theResult = m_Dialogs->PromptForSave();
+ if (theResult == CDialogs::SAVE_FIRST) {
+ bool onSaveResult = OnSave();
+ if (onSaveResult)
+ return true;
+ } else if (theResult == CDialogs::CONTINUE_NO_SAVE)
+ return true;
+
+ return false;
+ }
+ return true;
+}
+
+//==============================================================================
+/**
+ * If the presentation is currently playing, it is stopped. The playhead is
+ * restored to the position found in m_PlaybackTime.
+ */
+void CStudioApp::PlaybackStop()
+{
+ CDoc *theDoc = m_Core->GetDoc();
+ // change it back to the original slide first before restoring the original time
+ if (m_PlaybackOriginalSlide.Valid()) {
+ if (m_PlaybackOriginalSlide != theDoc->GetActiveSlide())
+ theDoc->NotifyActiveSlideChanged(m_PlaybackOriginalSlide);
+ theDoc->SetPlayMode(PLAYMODE_STOP, m_PlaybackTime);
+ }
+ // Invalidate the playback original slide so we don't inadvertently trigger this code later.
+ m_PlaybackOriginalSlide = 0;
+}
+
+//=============================================================================
+/**
+ * Used for track wheel to do smooth tracking on mac, just scrolls the playhead.
+ */
+void CStudioApp::AdvanceTime()
+{
+ long theDeltaTime = CStudioPreferences::GetTimeAdvanceAmount();
+ long theTime =
+ (m_Core->GetDoc()->GetCurrentViewTime() + theDeltaTime) / theDeltaTime * theDeltaTime;
+ m_Core->GetDoc()->NotifyTimeChanged(theTime);
+}
+
+//=============================================================================
+/**
+ * Used for track wheel to do smooth tracking on mac, just scrolls the playhead.
+ */
+void CStudioApp::ReduceTime()
+{
+ long theDeltaTime = CStudioPreferences::GetTimeAdvanceAmount();
+ long theTime = (m_Core->GetDoc()->GetCurrentViewTime() - 1) / theDeltaTime * theDeltaTime;
+ m_Core->GetDoc()->NotifyTimeChanged(theTime);
+}
+
+//=============================================================================
+/**
+ * Used for track wheel to do smooth tracking on mac, just scrolls the playhead.
+ */
+void CStudioApp::AdvanceUltraBigTime()
+{
+ long theDeltaTime = CStudioPreferences::GetBigTimeAdvanceAmount();
+ long theTime =
+ (m_Core->GetDoc()->GetCurrentViewTime() + theDeltaTime) / theDeltaTime * theDeltaTime;
+ m_Core->GetDoc()->NotifyTimeChanged(theTime);
+}
+
+//=============================================================================
+/**
+ * Used for track wheel to do smooth tracking on mac, just scrolls the playhead.
+ */
+void CStudioApp::ReduceUltraBigTime()
+{
+ long theDeltaTime = CStudioPreferences::GetBigTimeAdvanceAmount();
+ long theTime = (m_Core->GetDoc()->GetCurrentViewTime() - 1) / theDeltaTime * theDeltaTime;
+ m_Core->GetDoc()->NotifyTimeChanged(theTime);
+}
+
+//==============================================================================
+/**
+ * If the presentation is currently playing, it is stopped. Otherwise, the
+ * presetation starts playing from its current position. Called when the user
+ * presses the Enter key.
+ */
+void CStudioApp::PlaybackToggle()
+{
+ // If the presentation is playing, stop it and leave the playhead where it is
+ if (m_Core->GetDoc()->IsPlaying())
+ PlaybackStopNoRestore();
+ // Otherwise, the presentation is stopped, so start it playing
+ else
+ PlaybackPlay();
+}
+
+CInspectableBase *CStudioApp::GetInspectableFromSelectable(Q3DStudio::SSelectedValue inSelectable)
+{
+ CInspectableBase *theInspectableBase = nullptr;
+ if (inSelectable.empty() == false) {
+ switch (inSelectable.getType()) {
+ case Q3DStudio::SelectedValueTypes::Slide:
+ theInspectableBase = new CUICDMInspectable(
+ *this, m_Core, inSelectable.getData<Q3DStudio::SSlideInstanceWrapper>().m_Instance);
+ break;
+ case Q3DStudio::SelectedValueTypes::MultipleInstances:
+ case Q3DStudio::SelectedValueTypes::Instance: {
+
+ // We need to decide whether to display SlideInspectable or UICDMInspectable
+ // We display SlideInspectable if user selects a Scene or Component where the current
+ // active slide belongs,
+ // for example when user selects the Root in Timeline Palette
+ CDoc *theDoc = m_Core->GetDoc();
+ UICDM::TInstanceHandleList theSelectedInstances =
+ theDoc->GetSelectedValue().GetSelectedInstances();
+ UICDM::CUICDMInstanceHandle theSelectedInstance;
+ if (theSelectedInstances.size() == 1)
+ theSelectedInstance = theSelectedInstances[0];
+
+ if (m_Core->GetDoc()->GetDocumentReader().IsInstance(theSelectedInstance)) {
+ CClientDataModelBridge *theBridge =
+ theDoc->GetStudioSystem()->GetClientDataModelBridge();
+ UICDM::CUICDMSlideHandle theCurrentActiveSlide = theDoc->GetActiveSlide();
+
+ // Slide, scene or component
+ if (theSelectedInstance
+ == theBridge->GetOwningComponentInstance(theCurrentActiveSlide)) {
+ CUICDMInstanceHandle theCurrentActiveSlideInstance =
+ theDoc->GetStudioSystem()->GetSlideSystem()->GetSlideInstance(
+ theCurrentActiveSlide);
+
+ if (theBridge->IsSceneInstance(theSelectedInstance))
+ theInspectableBase = new CUICDMSceneInspectable(
+ *this, m_Core, theSelectedInstance, theCurrentActiveSlideInstance);
+ else if (theBridge->IsComponentInstance(theSelectedInstance))
+ theInspectableBase = new CUICDMInspectable(
+ *this, m_Core, theSelectedInstance, theCurrentActiveSlideInstance);
+ }
+ if (theInspectableBase == nullptr) {
+ if (theBridge->IsMaterialBaseInstance(theSelectedInstance))
+ theInspectableBase =
+ new CUICDMMaterialInspectable(*this, m_Core, theSelectedInstance);
+ else
+ theInspectableBase =
+ new CUICDMInspectable(*this, m_Core, theSelectedInstance);
+ }
+ }
+ } break;
+ case Q3DStudio::SelectedValueTypes::Guide: {
+ UICDM::CUICDMGuideHandle theGuide = inSelectable.getData<UICDM::CUICDMGuideHandle>();
+ theInspectableBase = CGuideInspectable::CreateInspectable(*m_Core, theGuide);
+ } break;
+ };
+ }
+
+ return theInspectableBase;
+}
+
+void CStudioApp::RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler)
+{
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::AdvanceTime), 0, Qt::Key_Period);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::ReduceTime), 0, Qt::Key_Comma);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::AdvanceUltraBigTime),
+ Qt::ShiftModifier, Qt::Key_Greater);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::ReduceUltraBigTime),
+ Qt::ShiftModifier, Qt::Key_Less);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnToggleAutosetKeyframes), 0, Qt::Key_K);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnSaveAs),
+ Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_S);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnSave), Qt::ControlModifier,
+ Qt::Key_S);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnSaveCopy),
+ Qt::ControlModifier | Qt::AltModifier, Qt::Key_S);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnFileNew),
+ Qt::ControlModifier, Qt::Key_N);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::OnFileOpen),
+ Qt::ControlModifier, Qt::Key_O);
+ inShortcutHandler->RegisterKeyUpEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::PlaybackStop), 0,
+ Qt::Key_Space);
+ inShortcutHandler->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::PlaybackPlay), 0,
+ Qt::Key_Space);
+ inShortcutHandler->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CStudioApp>(this, &CStudioApp::PlaybackToggle), 0,
+ Qt::Key_Return);
+
+ if (m_Views)
+ m_Views->RegisterGlobalKeyboardShortcuts(inShortcutHandler);
+}
+
+//=============================================================================
+/**
+ * Handles the Save command
+ * This will save the file, if the file has not been saved before this will
+ * do a save as operation.
+ * @return true if the file was successfully saved.
+ */
+bool CStudioApp::OnSave()
+{
+ CUICFile theCurrentDoc = m_Core->GetDoc()->GetDocumentPath();
+ if (!theCurrentDoc.IsFile()) {
+ return OnSaveAs();
+ } else if (!theCurrentDoc.CanWrite()) {
+ m_Dialogs->DisplaySavingPresentationFailed();
+ return false;
+ } else {
+ m_Core->OnSaveDocument(theCurrentDoc);
+ return true;
+ }
+}
+
+//=============================================================================
+/**
+ * Command handler for the File Save As menu option.
+ * This will prompt the user for a location to save the file out to then
+ * will perform the save.
+ * @return true if the file was successfully saved.
+ */
+bool CStudioApp::OnSaveAs()
+{
+ CUICFile theFile = m_Dialogs->GetSaveAsChoice().first;
+ if (theFile.GetPath() != "") {
+ m_Core->OnSaveDocument(theFile);
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Command handler for the File Save As menu option.
+ * This will prompt the user for a location to save the file out to then
+ * save a copy, leaving the original file open in the editor.
+ * @return true if the file was successfully saved.
+ */
+bool CStudioApp::OnSaveCopy()
+{
+ CUICFile theFile = m_Dialogs->GetSaveAsChoice().first;
+ if (theFile.GetPath() != "") {
+ // Send in a "true" to teh save function to indicate this is a copy
+ m_Core->OnSaveDocument(theFile, true);
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Call to load a new document.
+ * There should not be a currently active document when this is called.
+ * @param inDocument the path to the UIP file to be loaded.
+ * @param inShowStartupDialogOnError true to show startup dialog if loading document is error
+ * @return true if loading was successful
+ */
+bool CStudioApp::OnLoadDocument(const CUICFile &inDocument, bool inShowStartupDialogOnError)
+{
+ m_Core->GetDispatch()->FireOnProgressBegin(CString::fromQString(QObject::tr("Loading...")), inDocument.GetName(),
+ CString::fromQString(QObject::tr("Loading...")));
+
+ bool theLoadResult = false;
+ int theLoadErrorParameter = -1;
+ Q3DStudio::CString theErrorText;
+ try {
+ OnLoadDocumentCatcher(inDocument);
+ m_Core->GetDispatch()->FireOnOpenDocument(inDocument, true);
+ // Loading was successful
+ theLoadResult = true;
+ } catch (CUnsupportedFileFormatException &) {
+ theErrorText = CString::fromQString(QObject::tr("The file could not be opened. It is either invalid or was made with an "
+ "old version of Studio."));
+ // We've encountered a file format that is older than the current, OR
+ // corrupt files, unsupported file formats and illegal types.
+ } catch (CInvalidFileFormatException &) {
+ theErrorText = CString::fromQString(QObject::tr("The file could not be opened. It appears to have been made with a newer "
+ "version of Studio."));
+ // Cannot support opening newer file format, the UIP or (AP ie client portion)'s version is
+ // mismatched.
+ } catch (CLoadReferencedFileException &inError) {
+ // referenced files (e.g. Data Files) failed to load
+ theErrorText.Format(L"%ls failed to load due to invalid referenced file: %ls.",
+ static_cast<const wchar_t *>(inDocument.GetName()),
+ inError.GetFilePath()); // TODO: Localize
+ const wchar_t *theDesc = inError.GetDescription();
+ if (theDesc && wcslen(theDesc) > 0) {
+ // append any description is provided
+ theErrorText += L"\n";
+ theErrorText += inError.GetDescription();
+ }
+ } catch (CIOException &) { // provide specific error message if possible
+ if (inDocument.Exists() == false)
+ theLoadErrorParameter = IDS_ERROR_LOADFILENOTEXIST;
+ qCCritical(qt3ds::INTERNAL_ERROR)
+ << "Failed to load document, IO error (file may be unreadable or nonexistent)";
+ } catch (...) {
+ qCCritical(qt3ds::INTERNAL_ERROR) << "Failed to load document, uknown error";
+ // We don't know exactly what went wrong during a load, but let studio 'fail gracefully'.
+ }
+
+ if (theErrorText.Length()) {
+ qCCritical(qt3ds::INTERNAL_ERROR) << "Failed to load document: "
+ << theErrorText.GetCharStar();
+ }
+
+ m_Core->GetDispatch()->FireOnProgressEnd();
+
+ // load fail
+ if (!theLoadResult) {
+ if (!theErrorText.IsEmpty())
+ m_Dialogs->DisplayKnownErrorDialog(theErrorText);
+ else
+ m_Dialogs->DisplayLoadingPresentationFailed(inDocument, theLoadErrorParameter);
+
+ m_Core->GetDispatch()->FireOnOpenDocument(inDocument, false);
+
+ // Show startup dialog
+#ifdef KDAB_TEMPORARILY_REMOVED
+ if (inShowStartupDialogOnError)
+ if (!ShowStartupDialog())
+ PostQuitMessage(WM_QUIT);
+#endif
+ } else {
+ m_Dialogs->ResetSettings(inDocument.GetPath());
+
+ m_subpresentations.clear();
+ m_Core->GetDoc()->LoadUIASubpresentations(m_Core->GetDoc()->GetDocumentUIAFile(),
+ m_subpresentations);
+ }
+
+ m_AuthorZoom = false;
+
+ m_Core->GetDispatch()->FireAuthorZoomChanged();
+
+ return theLoadResult;
+}
+
+
+//=============================================================================
+/**
+ *
+ */
+void CStudioApp::SaveUIAFile()
+{
+ QStringList list;
+ for (SubPresentationRecord r : m_subpresentations) {
+ list.append(r.m_type);
+ list.append(r.m_id);
+ list.append(r.m_argsOrSrc);
+ }
+ Q3DStudio::CFilePath doc(GetCore()->GetDoc()->GetDocumentPath().GetAbsolutePath());
+ uic::state::IApplication::EnsureApplicationFile(doc.GetCharStar(), list);
+}
+
+//=============================================================================
+/**
+ * Called by OnLoadDocument, to allow the error reporting to be inserted.
+ * Because of the nature of the error reporting, OnLoadDocument has to have
+ * a certain structure that limits it (C type variables, no object destructors).
+ */
+void CStudioApp::OnLoadDocumentCatcher(const CUICFile &inDocument)
+{
+ {
+ CDispatchDataModelNotificationScope __scope(*m_Core->GetDispatch());
+ m_Core->GetDoc()->CloseDocument();
+ m_Core->GetDoc()->LoadDocument(inDocument);
+ }
+
+ // Make sure the client scene is resized properly
+ if (m_Views)
+ m_Views->RecheckMainframeSizingMode();
+}
+
+void CStudioApp::OnFileOpen()
+{
+ if (PerformSavePrompt()) {
+ CUICFile theFile = m_Dialogs->GetFileOpenChoice();
+ if (theFile.GetPath() != "")
+ OnLoadDocument(theFile);
+ }
+}
+using namespace std;
+
+void CStudioApp::OnFileNew()
+{
+ if (PerformSavePrompt()) {
+ pair<CUICFile, bool> theFile = m_Dialogs->GetNewDocumentChoice();
+ if (theFile.first.GetPath() != "")
+ m_Core->OnNewDocument(theFile.first, theFile.second);
+ }
+}
+
+bool CStudioApp::IsAuthorZoom()
+{
+ return m_AuthorZoom;
+}
+
+void CStudioApp::SetAuthorZoom(bool inZoom)
+{
+ if (m_AuthorZoom != inZoom) {
+ m_AuthorZoom = inZoom;
+ m_Core->GetDispatch()->FireAuthorZoomChanged();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// These commands come over the dispatch from inside the core. The core doesn't
+// have access to the CMsgRouter at the moment, so this relays the message.
+void CStudioApp::OnAsynchronousCommand(CCmd *inCmd)
+{
+ CMsgRouter::GetInstance()->SendCommand(inCmd, m_Core);
+}
+
+void CStudioApp::OnDisplayAppStatus(Q3DStudio::CString &inStatusMsg)
+{
+ // Do nothing, it was used to show this in the status bar
+}
+
+void CStudioApp::OnProgressBegin(const Q3DStudio::CString &inActionText,
+ const Q3DStudio::CString &inFileName,
+ const Q3DStudio::CString &inWindowTitle)
+{
+ m_Dialogs->DisplayProgressScreen(inActionText, inFileName, inWindowTitle);
+}
+
+void CStudioApp::OnProgressEnd()
+{
+ m_Dialogs->DestroyProgressScreen();
+}
+
+void CStudioApp::OnAssetDeleteFail()
+{
+ m_Dialogs->DisplayAssetDeleteFailed();
+}
+
+void CStudioApp::OnPasteFail()
+{
+ m_Dialogs->DisplayPasteFailed();
+}
+
+void CStudioApp::OnBuildconfigurationFileParseFail(const Q3DStudio::CString &inMessage)
+{
+ m_Dialogs->DisplayMessageBox(::LoadResourceString(IDS_BUILDCONFIGS_ERROR_TITLE), inMessage,
+ CUICMessageBox::ICON_ERROR, false);
+}
+
+void CStudioApp::OnSaveFail(bool inKnownError)
+{
+ qCCritical(qt3ds::INTERNAL_ERROR) << "Failed to save project: "
+ << (inKnownError ? "KnownError" : "UnknownError");
+ if (inKnownError) {
+ m_Dialogs->DisplaySavingPresentationFailed();
+ } else {
+ m_Dialogs->DisplayKnownErrorDialog(::LoadResourceString(IDS_SAVE_UNKNOWN_ERROR));
+ }
+}
+
+void CStudioApp::OnProjectVariableFail(const Q3DStudio::CString &inMessage)
+{
+ m_Dialogs->DisplayEnvironmentVariablesError(inMessage);
+}
+
+void CStudioApp::OnErrorFail(const Q3DStudio::CString &inText)
+{
+ qCCritical(qt3ds::INTERNAL_ERROR) << inText.GetCharStar();
+ m_Dialogs->DisplayMessageBox(::LoadResourceString(IDS_PROJNAME), inText,
+ CUICMessageBox::ICON_ERROR, false);
+}
+
+void CStudioApp::OnRefreshResourceFail(const Q3DStudio::CString &inResourceName,
+ const Q3DStudio::CString &inDescription)
+{
+ qCCritical(qt3ds::INTERNAL_ERROR) << "Failed to refresh resource: "
+ << inResourceName.GetCharStar();
+ qCCritical(qt3ds::INTERNAL_ERROR) << inDescription.GetCharStar();
+ m_Dialogs->DisplayRefreshResourceFailed(inResourceName, inDescription);
+}
+
+void CStudioApp::OnNewPresentation()
+{
+ m_Core->GetDoc()->GetStudioSystem()->GetAnimationSystem()->SetAutoKeyframe(
+ CStudioPreferences::IsAutosetKeyframesOn());
+ qCInfo(qt3ds::TRACE_INFO) << "New Presentation: "
+ << m_Core->GetDoc()->GetDocumentPath().GetAbsolutePath().GetCharStar();
+}
+
+void CStudioApp::OnPresentationModifiedExternally()
+{
+ int theUserChoice = m_Dialogs->DisplayChoiceBox(
+ ::LoadResourceString(IDS_TITLE_WARNING),
+ ::LoadResourceString(IDS_PRESENTATION_MODIFIED_EXTERNALLY), CUICMessageBox::ICON_WARNING);
+ if (theUserChoice == IDYES) {
+ CUICFile theCurrentDoc = m_Core->GetDoc()->GetDocumentPath();
+ OnLoadDocument(theCurrentDoc);
+ }
+}
diff --git a/src/Authoring/Studio/_Win/Application/StudioApp.h b/src/Authoring/Studio/_Win/Application/StudioApp.h
new file mode 100644
index 00000000..caea5158
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/StudioApp.h
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STUDIO_APP_H
+#define INCLUDED_STUDIO_APP_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "CmdLineParser.h"
+#include "StudioObjectTypes.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include <minmax.h>
+#include <gdiplus.h>
+#endif
+#include "DispatchListeners.h"
+#include "UICDMHandles.h"
+
+#include <QApplication>
+
+namespace Q3DStudio {
+class IInternalDirectoryWatchingSystem;
+class IDirectoryWatchingSystem;
+class ITickTock;
+class IStudioRenderer;
+struct SSelectedValue;
+};
+
+namespace UICDM {
+class ISignalConnection;
+};
+
+struct StudioManipulationModes
+{
+ enum Enum {
+ Local,
+ Global,
+ };
+};
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CCore;
+class CDialogs;
+class CSplashView;
+class CInspectableBase;
+class CDirectoryWatchingSystemWrapper;
+class CHotKeys;
+class CViews;
+class CMainFrame;
+enum EStudioObjectType;
+struct SubPresentationRecord;
+
+class CStudioApp : public CCoreAsynchronousEventListener,
+ public CAppStatusListener,
+ public CFailListener,
+ public CPresentationChangeListener // to setup auto set keyframes
+{
+public:
+ CStudioApp();
+ virtual ~CStudioApp();
+
+ // Overrides
+public:
+ virtual BOOL InitInstance(int argc, char *argv[]);
+ virtual int ExitInstance();
+ virtual int Run();
+#ifdef KDAB_TEMPORARILY_REMOVED
+ virtual UINT GetProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault);
+ virtual BOOL WriteProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue);
+ virtual ::CString GetProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
+ LPCTSTR lpszDefault = nullptr);
+ virtual BOOL WriteProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue);
+#endif
+
+ void OnAppAbout();
+
+public:
+ void PerformShutdown();
+#ifdef KDAB_TEMPORARILY_REMOVED
+ static void StudioUnhandledCrashHandler(EXCEPTION_POINTERS *pExPtrs);
+#endif
+ Q3DStudio::IDirectoryWatchingSystem &GetDirectoryWatchingSystem();
+ void SetupTimer(long inMessageId, QWidget *inWnd);
+ Q3DStudio::ITickTock &GetTickTock();
+ Q3DStudio::IStudioRenderer &GetRenderer();
+ void ClearGuides();
+
+protected:
+ int RunApplication();
+ int BlankRunApplication();
+ int RunCmdLineTests(const Q3DStudio::CString &inTestArgs);
+ int RunSystemTests(const Q3DStudio::CString &inTestArgs);
+ int OpenAndRunApplication(const Q3DStudio::CString &inFilename);
+ void InitCore();
+ void InitHelpSystem();
+ bool ShowStartupDialog();
+ bool HandleWelcomeRes(int res, bool recursive);
+
+ CCore *m_Core;
+ CSplashView *m_SplashPalette; ///< Startup splash palette
+ QString m_OldHelpFilePath; ///< Stores a pointer to the old
+ CCmdLineParser m_CmdLineParser; ///< Stores and returns execution modes
+ int m_UnitTestResults; ///< 0 on success; 1 on failure
+ bool m_IsSilent; ///< true indicates Studio running in silent mode (no GUI)
+ CViews *m_Views;
+ long m_ToolMode;
+ StudioManipulationModes::Enum
+ m_ManipulationMode; ///< Controls what space the tras,rot,and scale manipulations work in.
+ long m_SelectMode;
+ CDialogs *m_Dialogs;
+ long m_PlaybackTime; ///< Stores the playhead's starting position so that it can be restored
+ ///after playing the presentation for a little while
+ UICDM::CUICDMSlideHandle
+ m_PlaybackOriginalSlide; ///< Stores the current slide handle before playback started.
+
+ std::shared_ptr<Q3DStudio::ITickTock> m_TickTock;
+ std::shared_ptr<Q3DStudio::IDirectoryWatchingSystem> m_DirectoryWatchingSystem;
+ std::shared_ptr<UICDM::ISignalConnection> m_DirectoryWatcherTicker;
+ std::shared_ptr<Q3DStudio::IStudioRenderer> m_Renderer;
+ bool m_AuthorZoom;
+
+private:
+#ifdef KDAB_TEMPORARILY_REMOVED
+ Gdiplus::GdiplusStartupInput m_gdiplusStartupInput;
+ ULONG_PTR m_pGdiToken;
+#endif
+
+ bool m_welcomeShownThisSession;
+ // are we are launching welcome screen again due to
+ // user canceling file dialog?
+ bool m_goStraightToWelcomeFileDialog;
+ int m_tutorialPage;
+public:
+ CMainFrame* m_pMainWnd;
+
+ CCore *GetCore();
+ CViews *GetViews();
+ CDialogs *GetDialogs();
+ long GetToolMode();
+ void SetToolMode(long inToolMode);
+ long GetSelectMode();
+ void SetSelectMode(long inSelectMode);
+
+ StudioManipulationModes::Enum GetMinpulationMode() const;
+ void SetMinpulationMode(StudioManipulationModes::Enum inManipulationMode);
+
+ bool CanUndo();
+ bool CanRedo();
+ void OnCopy();
+ bool CanCopy();
+ Q3DStudio::CString GetCopyType();
+ void OnCut();
+ bool CanCut();
+ void OnPaste();
+ bool CanPaste();
+ Q3DStudio::CString GetPasteType();
+ void SetSelectedObjectTimebarColor();
+ bool CanChangeTimebarColor();
+ void HandleSetChangedKeys();
+ void DeleteSelectedKeys();
+ void HandleDuplicateCommand();
+ bool CanDuplicateObject();
+ void OnToggleAutosetKeyframes();
+ void SetAutosetKeyframes(bool inFlag);
+ void PlaybackPlay();
+ void PlaybackStopNoRestore();
+ void PlaybackRewind();
+ void OnRevert();
+ bool CanRevert();
+ void OnFileOpenRecent(const CUICFile &inDocument);
+ bool PerformSavePrompt();
+ void PlaybackStop();
+ void AdvanceTime();
+ void ReduceTime();
+ void AdvanceUltraBigTime();
+ void ReduceUltraBigTime();
+ void PlaybackToggle();
+ CInspectableBase *GetInspectableFromSelectable(Q3DStudio::SSelectedValue inSelectable);
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+ bool OnSave();
+ bool OnSaveAs();
+ bool OnSaveCopy();
+ bool OnLoadDocument(const CUICFile &inDocument, bool inShowStartupDialogOnError = true);
+ void OnLoadDocumentCatcher(const CUICFile &inLocation);
+ void OnFileOpen();
+ void OnFileNew();
+ bool IsAuthorZoom();
+ void SetAuthorZoom(bool inZoom);
+
+ // CCoreAsynchronousEventListener
+ void OnAsynchronousCommand(CCmd *inCmd) override;
+
+ // CAppStatusListener
+ void OnDisplayAppStatus(Q3DStudio::CString &inStatusMsg) override;
+ void OnProgressBegin(const Q3DStudio::CString &inActionText,
+ const Q3DStudio::CString &inFileName,
+ const Q3DStudio::CString &inWindowTitle) override;
+ void OnProgressEnd() override;
+
+ // CFailListener
+ void OnAssetDeleteFail() override;
+ void OnPasteFail() override;
+ void OnBuildconfigurationFileParseFail(const Q3DStudio::CString &inMessage) override;
+ void OnSaveFail(bool inKnownError) override;
+ void OnProjectVariableFail(const Q3DStudio::CString &inMessage) override;
+ void OnErrorFail(const Q3DStudio::CString &inText) override;
+ void OnRefreshResourceFail(const Q3DStudio::CString &inResourceName,
+ const Q3DStudio::CString &inDescription) override;
+
+ // CPresentationChangeListener
+ void OnNewPresentation() override;
+ void OnPresentationModifiedExternally() override;
+
+ Q3DStudio::CString m_pszHelpFilePath;
+
+ QVector<SubPresentationRecord> m_subpresentations;
+ void SaveUIAFile();
+};
+
+extern CStudioApp g_StudioApp;
+
+#endif // INCLUDED_STUDIO_APP_H
diff --git a/src/Authoring/Studio/_Win/Application/StudioColors.h b/src/Authoring/Studio/_Win/Application/StudioColors.h
new file mode 100644
index 00000000..776047a5
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/StudioColors.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 Anark Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STUDIOCOLORS_H
+#define INCLUDED_STUDIOCOLORS_H
+
+#pragma once
+
+//==============================================================================
+// Studio colors
+//==============================================================================
+
+const COLORREF STUDIO_MATTE_COLOR = RGB(119, 122, 125); // GetSysColor( COLOR_APPWORKSPACE );
+const COLORREF MENU_IMAGELIST_BACKCOLOR = RGB(255, 0, 255); // RGB( 255, 128, 0 );
+
+//==============================================================================
+// TimeBar colors
+//==============================================================================
+
+// TimeBar pen colors
+
+const COLORREF TIMEBAR_PEN_COLOR_INNER = RGB(254, 254, 254);
+const COLORREF TIMEBAR_PEN_COLOR_INACTIVE = RGB(132, 130, 132);
+const COLORREF TIMEBAR_PEN_COLOR_ACTIVE = RGB(0, 0, 0);
+
+// TimeBar shadow colors
+
+const COLORREF TIMEBAR_INACTIVE_SHADOW_COLOR = RGB(0xC4, 0xC4, 0xC4);
+const COLORREF TIMEBAR_ACTIVE_SHADOW_COLOR = RGB(0x84, 0x84, 0x84);
+
+// TimeBar comment colors
+
+const COLORREF TIMEBAR_COMMENT_ACTIVE_TC = RGB(0x00, 0x00, 0x00);
+const COLORREF TIMEBAR_COMMENT_INACTIVE_TC = RGB(0x72, 0x72, 0x72);
+
+const COLORREF COLOR_TIMEBAR_DISABLED = RGB(207, 207, 207);
+const COLORREF COLOR_TIMEBAR_BEHAVIOR =
+ RGB(148, 189, 150); // RGB( 92, 164, 131 );//RGB( 243, 243, 243 );
+const COLORREF COLOR_TIMEBAR_MATERIAL =
+ RGB(194, 155, 181); // RGB( 190, 184, 152 ); //RGB( 239, 230, 210 );
+const COLORREF COLOR_TIMEBAR_IMAGE =
+ RGB(194, 155, 181); // RGB( 190, 184, 152 ); //RGB( 198, 225, 199 );
+const COLORREF COLOR_TIMEBAR_LOCKED = RGB(228, 228, 228);
+
+const COLORREF TIMEBAR_COLOR_TRANSPARENT = RGB(255, 0, 255);
+
+// A constant to represent color for inner white rectangle of center bar
+const COLORREF COLOR_INNER_BAR_RECTANGLE = RGB(253, 253, 254);
+// Default timebar fill color
+const COLORREF COLOR_TIMEBAR_DEFAULT_FILL = RGB(
+ 163, 178, 227); // RGB( 174, 183, 210 ); //RGB( 148, 166, 189 ); //RGB( 211, 226, 240 );
+
+// Selected time bar colors
+
+const COLORREF COLOR_TIMEBAR_SELECTED = RGB(75, 75, 75); // RGB( 14, 49, 98 );
+const COLORREF COLOR_TIMEBAR_SELECTED_TEXT = RGB(255, 255, 255);
+const COLORREF COLOR_TIMEBAR_SELECTED_BEHAVIOR = RGB(174, 216, 176);
+const COLORREF COLOR_TIMEBAR_SELECTED_HIGHLIGHT = RGB(27, 73, 136);
+
+// const COLORREF COLOR_SELECTED_TIMEBAR_3DOBJECT = RGB( 190, 191, 202
+// );
+
+const COLORREF COLOR_TIMEBAR_HIGHLIGHT = RGB(190, 191, 202);
+const COLORREF COLOR_TIMEBAR_HIGHLIGHT_GROUP = RGB(174, 216, 176);
+
+const COLORREF COLOR_PLAYHEADSCRUBTIME = RGB(0, 85, 0);
+
+//==============================================================================
+// Timeline colors
+//==============================================================================
+
+const BOOL INVERT_TIMELINE_TEXT_COLOR = TRUE;
+
+const COLORREF COLOR_SELECTEDTEXT = RGB(255, 255, 255);
+const COLORREF COLOR_SELECTEDTEXTBKGRND = RGB(14, 49, 98);
+const COLORREF COLOR_DRAGDROPTARGETTEXT = RGB(255, 255, 255);
+const COLORREF COLOR_DRAGDROPTARGETBKGRND = RGB(24, 98, 198);
+
+const COLORREF COLOR_SHADOW = RGB(165, 162, 161); // RGB( 132, 130, 132 );
+
+const COLORREF COLOR_PLAYHEADLINE = RGB(0xDD, 0x04, 0x05);
+const COLORREF COLOR_BACKGRND =
+ GetSysColor(COLOR_3DFACE); // RGB( 206, 203, 198 ); //RGB( 0xE7, 0xE7, 0xE7 );
+const COLORREF COLOR_SELECTED = RGB(255, 255, 255);
+const COLORREF COLOR_DEFAULTOBJECTS = RGB(206, 203, 198); // RGB( 0xE7, 0xE7, 0xE7 );
+const COLORREF COLOR_3DOBJECTS = RGB(206, 203, 198); // RGB( 0xC7, 0xD4, 0xE1 );
+const COLORREF COLOR_DEFAULTOBJECTHIGHLIGHT = RGB(222, 223, 222); // RGB( 255, 255, 255 );
+const COLORREF COLOR_DRAGHIGHLIGHT = RGB(255, 255, 255);
+const COLORREF COLOR_INSERTMARK = RGB(0x00, 0x00, 0x00);
+const COLORREF COLOR_TIMELINEINSERTMARK = RGB(0x00, 0x00, 0xFF);
+const COLORREF COLOR_TEXTCOLOR = RGB(0x00, 0x00, 0x00);
+const COLORREF COLOR_CONTAINERTEXTCOLOR = RGB(80, 80, 140);
+const COLORREF COLOR_LOCKEDTEXTCOLOR = RGB(0x80, 0x80, 0x80);
+const COLORREF COLOR_INDEPENDENTOBJECT = RGB(0x80, 0x80, 0x80);
+const COLORREF COLOR_SPLITLINE = RGB(0x00, 0x00, 0x7F);
+const COLORREF COLOR_SEPARATORLINE = RGB(0x00, 0x00, 0x20);
+const COLORREF COLOR_LIGHTMODEL = RGB(206, 203, 198); // RGB( 198, 211, 224 );
+const COLORREF COLOR_GROUP =
+ RGB(206, 203, 198); // RGB( 198, 211, 224 ); //RGB( 199, 220, 193 );
+const COLORREF COLOR_PROPERTY = RGB(228, 225, 219); // RGB( 241, 241, 241 );
+const COLORREF COLOR_CHANNEL = RGB(228, 225, 219); // RGB( 241, 241, 241 );
+const COLORREF COLOR_CONTAINER =
+ RGB(228, 225, 219); // RGB( 206, 203, 198 ); //RGB( 231, 231, 231 );
+const COLORREF COLOR_MATERIAL = RGB(210, 210, 210); // RGB( 170, 224, 170 );
+const COLORREF COLOR_CONTAINERHIGHLIGHT = RGB(193, 180, 180);
+const COLORREF COLOR_BEHAVIOR = RGB(199, 220, 193);
+const COLORREF COLOR_BEHAVIORHIGHLIGHT = RGB(255, 255, 255);
+const COLORREF COLOR_IMGLISTTRANSPARENT = RGB(0xFF, 0x00, 0xFF);
+const COLORREF COLOR_TIMEMEASURE = GetSysColor(COLOR_3DFACE); // RGB( 170, 166, 160 );
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.cpp b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.cpp
new file mode 100644
index 00000000..e3e25fa1
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "StudioDefs.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SubPresentationsDlg.h"
+#include "ui_SubPresentationsDlg.h"
+#include "ProductInfo.h"
+#include "HotKeys.h"
+#include "Preferences.h"
+#include "StudioPreferences.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qpainter.h>
+#include <QtCore/qtimer.h>
+#include <QtWidgets/qfiledialog.h>
+
+CSubPresentationsDlg::CSubPresentationsDlg(
+ const QString &directory,
+ const QVector<SubPresentationRecord> &subpresentations,
+ QWidget *parent)
+ : QDialog(parent, Qt::MSWindowsFixedSizeDialogHint)
+ , m_directory(directory)
+ , m_records(subpresentations)
+ , m_ui(new Ui::SubPresentationsDlg)
+{
+ m_ui->setupUi(this);
+
+ connect(m_ui->deleteButton, &QPushButton::clicked, this,
+ &CSubPresentationsDlg::OnDeletePresentation);
+ connect(m_ui->pushButtonNew, &QPushButton::clicked, this,
+ &CSubPresentationsDlg::OnNewPresentation);
+ connect(m_ui->pushButtonBrowse, &QPushButton::clicked, this,
+ &CSubPresentationsDlg::OnBrowsePresentation);
+ connect(m_ui->comboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &CSubPresentationsDlg::OnSelectPresentation);
+ connect(m_ui->lineEditId, &QLineEdit::textEdited, this, &CSubPresentationsDlg::OnIdChanged);
+
+ OnInitDialog();
+}
+
+CSubPresentationsDlg::~CSubPresentationsDlg()
+{
+ delete m_ui;
+}
+
+QVector<SubPresentationRecord> CSubPresentationsDlg::subpresentations() const
+{
+ return m_records;
+}
+
+void CSubPresentationsDlg::OnInitDialog()
+{
+ current = -1;
+ if (m_records.size() > 0) {
+ for (SubPresentationRecord rec : m_records)
+ m_ui->comboBox->addItem(rec.m_id);
+ current = 0;
+ m_ui->lineEditId->setText(m_records[0].m_id);
+ m_ui->lineEditPreview->setText(m_records[0].m_argsOrSrc);
+ }
+ m_ui->lineEditPreview->setReadOnly(true);
+ m_ui->buttonBox->setFocusPolicy(Qt::NoFocus);
+}
+
+void CSubPresentationsDlg::OnSelectPresentation(int selectionIndex)
+{
+ if (selectionIndex >= 0 && selectionIndex < m_records.size()) {
+ current = selectionIndex;
+ m_ui->lineEditId->setText(m_records[current].m_id);
+ m_ui->lineEditPreview->setText(m_records[current].m_argsOrSrc);
+ } else {
+ m_ui->lineEditId->setText(QString());
+ m_ui->lineEditPreview->setText(QString());
+ }
+}
+
+void CSubPresentationsDlg::OnDeletePresentation()
+{
+ if (current >= 0 && current < m_records.size()) {
+ m_records.remove(current);
+ m_ui->comboBox->removeItem(current);
+ current = qMax(current - 1, 0);
+ if (m_records.size() == 0)
+ current = -1;
+ OnSelectPresentation(current);
+ }
+}
+
+void CSubPresentationsDlg::OnNewPresentation()
+{
+ int index = m_records.size();
+ m_records.push_back(
+ SubPresentationRecord(QStringLiteral("presentation-qml"),
+ QStringLiteral("presentation-id"), QString()));
+ m_ui->comboBox->addItem(QStringLiteral("presentation-id"));
+ m_ui->comboBox->setCurrentIndex(index);
+ OnSelectPresentation(index);
+}
+
+void CSubPresentationsDlg::OnBrowsePresentation()
+{
+ const QString file = QDir::toNativeSeparators(
+ QFileDialog::getOpenFileName(nullptr, nullptr, m_directory,
+ QStringLiteral("*.qml; *.uip")));
+ QString shortFile = file;
+ int subdir = file.indexOf(m_directory);
+ if (subdir >= 0)
+ shortFile.remove(subdir, m_directory.size() + 1);
+
+ QFileInfo fileInfo(file);
+ if (fileInfo.exists()) {
+ if (fileInfo.suffix() == QStringLiteral("qml"))
+ m_records[current].m_type = QStringLiteral("presentation-qml");
+ else
+ if (fileInfo.suffix() == QStringLiteral("uip"))
+ m_records[current].m_type = QStringLiteral("presentation");
+
+ m_records[current].m_argsOrSrc = shortFile;
+ m_ui->lineEditPreview->setText(shortFile);
+ }
+}
+
+void CSubPresentationsDlg::OnIdChanged(const QString &text)
+{
+ m_records[current].m_id = text;
+ m_ui->comboBox->setItemText(current, text);
+}
+
+void CSubPresentationsDlg::on_buttonBox_accepted()
+{
+ QDialog::accept();
+}
+
+void CSubPresentationsDlg::on_buttonBox_rejected()
+{
+ QDialog::reject();
+}
diff --git a/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.h b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.h
new file mode 100644
index 00000000..75828c4e
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SUBPRESENTATIONSDLG_H
+#define SUBPRESENTATIONSDLG_H
+
+#include <QtWidgets/qdialog.h>
+
+#include "UICString.h"
+#include "Doc.h"
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class SubPresentationsDlg;
+}
+QT_END_NAMESPACE
+
+class CSubPresentationsDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ CSubPresentationsDlg(const QString &directory,
+ const QVector<SubPresentationRecord> &subpresentations,
+ QWidget* parent = nullptr);
+ ~CSubPresentationsDlg();
+
+ // Implementation
+protected:
+
+ void OnInitDialog();
+
+public:
+
+ QVector<SubPresentationRecord> subpresentations() const;
+
+ void OnDeletePresentation();
+ void OnNewPresentation();
+ void OnBrowsePresentation();
+ void OnSelectPresentation(int selectionIndex);
+ void OnIdChanged(const QString &text);
+
+private Q_SLOTS:
+ void on_buttonBox_accepted();
+ void on_buttonBox_rejected();
+
+private:
+ QString m_directory;
+ QVector<SubPresentationRecord> m_records;
+ Ui::SubPresentationsDlg *m_ui;
+ int current;
+};
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.ui b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.ui
new file mode 100644
index 00000000..db673925
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/SubPresentationsDlg.ui
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SubPresentationsDlg</class>
+ <widget class="QDialog" name="SubPresentationsDlg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>512</width>
+ <height>169</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Sub-presentations</string>
+ </property>
+ <widget class="QWidget" name="horizontalLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>130</y>
+ <width>491</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="focusPolicy">
+ <enum>Qt::NoFocus</enum>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="horizontalLayoutWidget_2">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>491</width>
+ <height>111</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Sub-presentation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Presentation Id</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Filename</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QComboBox" name="comboBox"/>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineEditId">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Edit the name of the sub-presentation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineEditPreview">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QPushButton" name="deleteButton">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Delete currently selected sub-presentation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButtonNew">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add new sub-presentation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="pushButtonBrowse">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Browse for the sub-presentation source&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/_Win/Application/TextLink.cpp b/src/Authoring/Studio/_Win/Application/TextLink.cpp
new file mode 100644
index 00000000..a68aaea8
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/TextLink.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+#ifdef _DEBUG
+//#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TextLink.h"
+#include "Resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CTextLink class
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CTextLink::CTextLink()
+ : CBaseLink()
+{
+ // Set default colors
+ m_ColorText = CStudioPreferences::GetNormalColor();
+ m_ColorBackground = CStudioPreferences::GetBaseColor();
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CTextLink::~CTextLink()
+{
+}
+
+//==============================================================================
+/**
+ * SetColor: Sets the text color and background color
+ *
+ * @param inText Text color for the control.
+ * @param inBackground Background color for the control.
+ */
+//==============================================================================
+void CTextLink::SetColor(COLORREF inText, COLORREF inBackground)
+{
+ m_ColorText = inText;
+ m_ColorBackground = inBackground;
+}
+
+//==============================================================================
+/**
+ * ShowLink: Shows the link.
+ *
+ * @param None
+ */
+//==============================================================================
+void CTextLink::ShowLink()
+{
+ if (IsWindow(m_hWnd)) {
+ ShowTextLink(m_ColorText, m_ColorBackground, DT_VCENTER | DT_SINGLELINE, m_bCapture);
+ }
+}
diff --git a/src/Authoring/Studio/_Win/Application/TextLink.h b/src/Authoring/Studio/_Win/Application/TextLink.h
new file mode 100644
index 00000000..85f5fedf
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/TextLink.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef _TEXTLINK_H_
+#define _TEXTLINK_H_
+
+#include "BaseLink.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CTextLink window
+
+class CTextLink : public CBaseLink
+{
+ // Construction
+public:
+ CTextLink();
+
+ // Attributes
+public:
+ // Operations
+public:
+ virtual void SetColor(COLORREF inText, COLORREF inBackground);
+
+protected:
+ COLORREF m_ColorText;
+ COLORREF m_ColorBackground;
+
+ virtual void ShowLink();
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CTextLink)
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ virtual ~CTextLink();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CTextLink)
+ //}}AFX_MSG
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Application/WebLink.cpp b/src/Authoring/Studio/_Win/Application/WebLink.cpp
new file mode 100644
index 00000000..258bdffb
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/WebLink.cpp
@@ -0,0 +1,440 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+#ifdef _DEBUG
+//#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "WebLink.h"
+#include "Resource.h"
+#include "BCMenu.h"
+#include "StudioColors.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CWebLink class
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CWebLink::CWebLink()
+ : CBaseLink()
+{
+ // Set default link colors
+ m_ColorLink = CStudioPreferences::GetMasterColor(); // green
+ m_ColorHighlight = RGB(0xFF, 0x00, 0x00); // red
+ m_ColorVisited = RGB(0x7F, 0x00, 0x7F); // purple
+ m_ColorBackground = CStudioPreferences::GetDarkBaseColor();
+
+ m_bVisited = FALSE;
+
+ m_URL.Empty();
+
+ m_Bitmap = nullptr;
+
+ m_bPopup = FALSE;
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CWebLink::~CWebLink()
+{
+}
+
+//==============================================================================
+// Message Map
+//==============================================================================
+
+BEGIN_MESSAGE_MAP(CWebLink, CWnd)
+//{{AFX_MSG_MAP(CWebLink)
+ON_WM_PAINT()
+ON_WM_SETCURSOR()
+ON_WM_LBUTTONDOWN()
+ON_WM_MOUSEMOVE()
+ON_WM_CONTEXTMENU()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * SetURL: Sets the URL for the control.
+ *
+ * @param inURL URL to point the default browser to when the control is clicked.
+ */
+//==============================================================================
+void CWebLink::SetURL(CString inURL)
+{
+ m_URL = inURL;
+}
+
+//==============================================================================
+/**
+ * SetImage: Sets an bitmap image for the control.
+ *
+ * @param inResBmp BITMAP resource for the image to load.
+ * @param inAutoSizeFlag TRUE if the control should be resized to the image size.
+ */
+//==============================================================================
+void CWebLink::SetImage(UINT inResBmp, bool inAutoSizeFlag)
+{
+ HBITMAP theBitmap = (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inResBmp),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ if (theBitmap != nullptr) {
+ this->SetImage(theBitmap, inAutoSizeFlag);
+ }
+}
+
+//==============================================================================
+/**
+ * SetImage: Sets an bitmap image for the control.
+ *
+ * @param inBitmap HBITMAP handle of the loaded bitmap.
+ * @param inAutoSizeFlag TRUE if the control should be resized to the image size.
+ */
+//==============================================================================
+void CWebLink::SetImage(HBITMAP inBitmap, bool inAutoSizeFlag)
+{
+ m_Bitmap = inBitmap;
+
+ if (inAutoSizeFlag && m_Bitmap != nullptr) {
+ // Resize this control based on the size of the bitmap.
+ BITMAP theBitmapStruct;
+ int theWidth, theHeight;
+
+ GetObject(m_Bitmap, sizeof(BITMAP), &theBitmapStruct);
+ theWidth = theBitmapStruct.bmWidth;
+ theHeight = theBitmapStruct.bmHeight;
+
+ SetWindowPos(nullptr, 0, 0, theWidth, theHeight, SWP_NOZORDER | SWP_NOMOVE);
+ }
+
+ this->ShowLink();
+}
+
+//==============================================================================
+/**
+ * SetText: Sets the text for the control.
+ *
+ * @param inText Text for the control.
+ */
+//==============================================================================
+void CWebLink::SetText(CString inText)
+{
+ SetWindowText(inText);
+ m_Bitmap = nullptr;
+ this->ShowLink();
+}
+
+COLORREF CWebLink::GetColor()
+{
+ if (m_bCapture) {
+ return m_ColorHighlight;
+ } else {
+ if (m_bVisited)
+ return m_ColorVisited;
+ else
+ return m_ColorLink;
+ }
+}
+
+//==============================================================================
+/**
+ * ShowLink: Shows the link.
+ *
+ * @param None
+ */
+//==============================================================================
+void CWebLink::ShowLink()
+{
+ if (IsWindow(m_hWnd)) {
+ // Is the link represented by an image?
+ if (m_Bitmap != nullptr) {
+ ShowBitmapLink();
+ } else {
+ ShowTextLink(GetColor(), m_ColorBackground, DT_WORDBREAK | SS_LEFT, true);
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * ShowLink: Shows the bitmap link.
+ *
+ * @param None
+ */
+//==============================================================================
+void CWebLink::ShowBitmapLink()
+{
+ if (IsWindow(m_hWnd)) {
+ CRect theRect;
+ CDC *theDC;
+ CDC theMemDC;
+
+ BITMAP theBitmapStruct;
+ long theWidth, theHeight;
+ CBrush theBrush;
+
+ theDC = GetDC();
+ theMemDC.CreateCompatibleDC(theDC);
+ CBitmap *theBitmap = CBitmap::FromHandle(m_Bitmap);
+
+ CBitmap *theOldBmp = (CBitmap *)theMemDC.SelectObject(theBitmap);
+
+ GetObject(m_Bitmap, sizeof(BITMAP), &theBitmapStruct);
+ theWidth = theBitmapStruct.bmWidth;
+ theHeight = theBitmapStruct.bmHeight;
+
+ theDC->BitBlt(0, 0, theWidth, theHeight, &theMemDC, 0, 0, SRCCOPY);
+
+ COLORREF theColor = GetColor();
+ if (theColor == m_ColorHighlight || theColor == m_ColorVisited) {
+ // Draw a highlight frame
+ GetClientRect(&theRect);
+
+ theBrush.CreateSolidBrush(theColor);
+ theDC->FrameRect(&theRect, &theBrush);
+ theBrush.DeleteObject();
+ }
+
+ theMemDC.SelectObject(theOldBmp);
+
+ theMemDC.DeleteDC();
+ ReleaseDC(theDC);
+ }
+}
+
+//==============================================================================
+/**
+ * JumpLink: Jumps to the link URL.
+ *
+ * @param None
+ */
+//==============================================================================
+void CWebLink::JumpLink()
+{
+ CString theWebLink;
+
+ GetWindowText(theWebLink);
+
+ if (!m_URL.IsEmpty())
+ theWebLink = m_URL;
+
+ if (!theWebLink.IsEmpty()) {
+ this->OpenURL(theWebLink);
+ }
+}
+
+//==============================================================================
+/**
+ * OpenURL: Opens the specified URL with the default browser.
+ *
+ * @param inURL URL to open with the browser.
+ */
+//==============================================================================
+void CWebLink::OpenURL(CString inURL)
+{
+ HCURSOR thePrevCursor;
+
+ thePrevCursor = SetCursor(LoadCursor(nullptr, IDC_APPSTARTING));
+ ShellExecute(GetSafeHwnd(), L"open", inURL, nullptr, nullptr, SW_SHOWNORMAL);
+ SetCursor(thePrevCursor);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CWebLink message handlers
+
+//==============================================================================
+/**
+ * OnSetCursor
+ *
+ * Handle the WM_SETCURSOR message.
+ *
+ * @param inWnd Pointer to the parent CWnd object.
+ * @param inHitTest Specifies the hit-test area code. The hit test determines the
+ *cursor�s location.
+ * @param inMessage Specifies the mouse message number.
+ */
+//==============================================================================
+BOOL CWebLink::OnSetCursor(CWnd *inWnd, UINT inHitTest, UINT inMessage)
+{
+ UNREFERENCED_PARAMETER(inWnd);
+ UNREFERENCED_PARAMETER(inHitTest);
+ UNREFERENCED_PARAMETER(inMessage);
+
+ if (m_bPopup) {
+ SetCursor(LoadCursor(nullptr, IDC_ARROW));
+ } else {
+ SetCursor(LoadCursor(nullptr, IDC_HAND));
+ }
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * OnLButtonDown
+ *
+ * Windows callback handler for the WM_LBUTTONDOWN message
+ *
+ * @param inFlags Flags for the left button down message
+ * @param inPoint Point where the mouse was left clicked (client coordinates)
+ */
+//==============================================================================
+void CWebLink::OnLButtonDown(UINT inFlags, CPoint inPoint)
+{
+ UNREFERENCED_PARAMETER(inFlags);
+ UNREFERENCED_PARAMETER(inPoint);
+
+ if (IsWindowEnabled()) {
+ m_bCapture = FALSE;
+ ReleaseCapture();
+
+ m_bVisited = TRUE;
+ this->ShowLink();
+
+ this->JumpLink();
+ }
+}
+
+//==============================================================================
+/**
+ * OnContextMenu: Handle for the WM_CONTEXTMENU message.
+ *
+ * @param inWnd Window for the context menu.
+ * @param inPoint Mouse position when the right button was clicked.
+ */
+//==============================================================================
+void CWebLink::OnContextMenu(CWnd *inWnd, CPoint inPoint)
+{
+ UNREFERENCED_PARAMETER(inWnd);
+
+ BCMenu theContextMenu;
+ BCMenu *thePopupMenu = nullptr;
+ long theMenuCmd;
+
+ ReleaseCapture();
+
+ // Load the context menu
+ theContextMenu.LoadMenu(IDR_WEBLINK_CONTEXT_MENU);
+#ifdef _USECONTENTMENUIMAGES_
+ theContextMenu.SetBitmapBackground(MENU_IMAGELIST_BACKCOLOR);
+ theContextMenu.LoadToolbar(IDR_WEBLINKCONTEXTTOOLBAR);
+#endif
+
+ thePopupMenu = (BCMenu *)theContextMenu.GetSubMenu(0);
+
+ // Set the default menu item to "Open"
+ SetMenuDefaultItem(thePopupMenu->m_hMenu, IDM_OPENLINK, FALSE);
+
+ m_bPopup = TRUE;
+
+ // Show the context menu
+ theMenuCmd = (long)thePopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ inPoint.x, inPoint.y, this);
+
+ m_bPopup = FALSE;
+
+ m_bCapture = FALSE;
+
+ theContextMenu.DestroyMenu();
+
+ this->ShowLink();
+
+ // Handle the popup menu command
+
+ switch (theMenuCmd) {
+ case IDM_OPENLINK:
+ this->JumpLink();
+ break;
+
+ case IDM_COPYSHORTCUT2:
+ this->CopyShortcut();
+ break;
+
+ default:
+ break;
+ }
+
+ theContextMenu.DestroyMenu();
+}
+
+//==============================================================================
+/**
+ * CopyShortcut: Copy the link URL text to the clipboard.
+ *
+ * @param None
+ */
+//==============================================================================
+void CWebLink::CopyShortcut()
+{
+ // Open the clipboard so we can copy data to it.
+ if (OpenClipboard()) {
+ DWORD theBufferSize;
+ HGLOBAL theGlobalBuffer;
+ LPSTR theClipboardText;
+
+ // Determine the buffer size, based on the length of the URL
+ theBufferSize = m_URL.GetLength();
+
+ // Clear the contents of the clipboard
+ ::EmptyClipboard();
+
+ // Allocate a global buffer and copy the text to it
+ theGlobalBuffer = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, theBufferSize + 1);
+ theClipboardText = (LPSTR)GlobalLock(theGlobalBuffer);
+
+ CopyMemory(theClipboardText, m_URL, theBufferSize + 1);
+
+ // Unlock the buffer
+ GlobalUnlock(theGlobalBuffer);
+
+ // Pass the data to the clipboard
+ SetClipboardData(CF_TEXT, theGlobalBuffer);
+
+ // Close the clipboard, but do not GlobalFree() the global buffer
+ CloseClipboard();
+ }
+}
diff --git a/src/Authoring/Studio/_Win/Application/WebLink.h b/src/Authoring/Studio/_Win/Application/WebLink.h
new file mode 100644
index 00000000..4ca3dede
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Application/WebLink.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef _WEBLINK_H_
+#define _WEBLINK_H_
+
+#include "BaseLink.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CWebLink window
+
+class CWebLink : public CBaseLink
+{
+ // Construction
+public:
+ CWebLink();
+
+ // Attributes
+public:
+ // Operations
+public:
+ virtual void SetImage(UINT uiResBmp, bool bAutoSize = true);
+ virtual void SetImage(HBITMAP hBmp, bool bAutoSize = true);
+ virtual void SetText(CString inText);
+ virtual void SetURL(CString inURL);
+
+protected:
+ COLORREF m_ColorLink;
+ COLORREF m_ColorHighlight;
+ COLORREF m_ColorVisited;
+ COLORREF m_ColorBackground;
+
+ bool m_bVisited;
+ bool m_bPopup;
+
+ HBITMAP m_Bitmap;
+
+ CString m_URL;
+
+ virtual COLORREF GetColor();
+
+ virtual void CopyShortcut();
+
+ virtual void JumpLink();
+
+ virtual void OpenURL(CString inURL);
+
+ virtual void ShowLink();
+
+ virtual void ShowBitmapLink();
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CWebLink)
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ virtual ~CWebLink();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CWebLink)
+ virtual afx_msg BOOL OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message);
+ virtual afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ virtual afx_msg void OnContextMenu(CWnd *pWnd, CPoint pos);
+ //}}AFX_MSG
+
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Controls/AppFonts.cpp b/src/Authoring/Studio/_Win/Controls/AppFonts.cpp
new file mode 100644
index 00000000..54039331
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/AppFonts.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "AppFonts.h"
+#include "StudioPreferences.h"
+#include "StringLoader.h"
+
+//=============================================================================
+/**
+ * Constructor for a CAppFonts object.
+ */
+//=============================================================================
+CAppFonts::CAppFonts()
+{
+ // Normal font
+ const QString theFontFace = CStudioPreferences::GetFontFaceName();
+ Q3DStudio::CString theFontSize = ::LoadResourceString(IDS_STUDIOFONT_SIZE);
+
+ m_NormalFont.setFamily(theFontFace);
+ m_NormalFont.setPointSize(13); // atoi( theFontSize.GetCharStar( ) );
+}
+
+//==============================================================================
+/**
+ * Releases the object.
+ */
+//==============================================================================
+CAppFonts::~CAppFonts()
+{
+}
+
+//==============================================================================
+/**
+ * Returns a pointer to the only instance of this class. Automatically cleaned
+ * up when the program exits.
+ */
+//==============================================================================
+CAppFonts *CAppFonts::GetInstance()
+{
+ static CAppFonts theAppFonts;
+ return &theAppFonts;
+}
+
+//==============================================================================
+/**
+ * Returns the normal font for use on GUI elements for the application.
+ */
+//==============================================================================
+QFont CAppFonts::GetNormalFont()
+{
+ return m_NormalFont;
+}
diff --git a/src/Authoring/Studio/_Win/Controls/AppFonts.h b/src/Authoring/Studio/_Win/Controls/AppFonts.h
new file mode 100644
index 00000000..e2036c0e
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/AppFonts.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#ifndef INCLUDED_APP_FONTS_H
+#define INCLUDED_APP_FONTS_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include <QFont>
+//==============================================================================
+/**
+ * Dispatches commands
+ */
+//==============================================================================
+class CAppFonts
+{
+public:
+ CAppFonts();
+ virtual ~CAppFonts();
+ static CAppFonts *GetInstance();
+ QFont GetNormalFont();
+
+private:
+ QFont m_NormalFont;
+};
+
+#endif // INCLUDED_APP_FONTS_H
diff --git a/src/Authoring/Studio/_Win/Controls/BufferedRenderer.cpp b/src/Authoring/Studio/_Win/Controls/BufferedRenderer.cpp
new file mode 100644
index 00000000..34076704
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/BufferedRenderer.cpp
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "BufferedRenderer.h"
+#include "AppFonts.h"
+
+
+//=============================================================================
+/**
+ * Create a buffered renderer.
+ * @param inSize the size of this renderer.
+ */
+CBufferedRenderer::CBufferedRenderer(const QSize &inSize)
+ : CWinRenderer()
+{
+ m_CurrentBitmap = QPixmap(inSize);
+
+ // KDAB_TODO: I don't see why m_OldBitmap is needed, nor what is the difference between CBufferedRenderer and COffscreenRenderer
+ m_OldBitmap = m_CurrentBitmap;
+
+ m_painter = new QPainter(&m_CurrentBitmap);
+ QFont font = CAppFonts::GetInstance()->GetNormalFont();
+ m_painter->setFont(font);
+
+ m_Size = inSize;
+
+ PushClippingRect(QRect(QPoint(0,0), inSize));
+}
+
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CBufferedRenderer::~CBufferedRenderer()
+{
+ delete m_painter;
+ m_painter = nullptr;
+}
+
+//=============================================================================
+/**
+ * Copy this BufferedRenderer to inRenderer using alpha blend.
+ * This will semi-transparently copy the contents of this buffer to inRenderer
+ * using the alpha.
+ * @param inRenderer the destination renderer.
+ * @param inAlpha the opacity, 255 = opaque 0 = transparent.
+ */
+/*void CBufferedRenderer::TransparentBltTo( CRenderer* inRenderer, short inAlpha )
+{
+
+ CPt theDestination;
+ theDestination.Offset( inRenderer->GetTranslation( ) );
+
+ AlphaBlendExt( inRenderer->GetGraphicsDevice( )->m_hDC, (short)theDestination.x,
+(short)theDestination.y, (short)m_Size.x, (short)m_Size.y, m_DC->m_hDC, 0, 0, (short)m_Size.x,
+(short)m_Size.y, inAlpha );
+// ::DrawAlphaBlendBitmap( inRenderer->GetDC( ), (short)theDestination.x,
+(short)theDestination.y, m_CurrentBitmap, (short)m_Size.x, (short)m_Size.y, 0, 0, inAlpha );
+
+}*/
+
+//==============================================================================
+/**
+ * Draws a bitmap using alpha blending. Works in NT/2000/9x
+ *
+ * @param inDCDest Destination device context
+ * @param inX X coordinate for drawing
+ * @param inY Y coordinate for drawing
+ * @param inWidth Drawing width
+ * @param inHeight Drawing height
+ * @param inDCSrc Source device context
+ * @param inSourceX X source coordinate
+ * @param inSourceY Y source coordinate
+ * @param inSourceWidth Source drawing width
+ * @param inSourceHeight Source drawing height
+ * @param inAlpha Alpha blend: 0-255, where 255 is opaque
+ */
+//==============================================================================
+/*bool CBufferedRenderer::AlphaBlendExt( HDC inDCDest, short inX, short inY, short inWidth, short
+inHeight, HDC inDCSrc, short inSourceX, short inSourceY, short inSourceWidth, short inSourceHeight,
+short inAlpha )
+{
+ BITMAPINFOHEADER theBMI;
+
+ theBMI.biSize = sizeof( BITMAPINFOHEADER );
+ theBMI.biWidth = inWidth;
+ theBMI.biHeight = inHeight;
+ theBMI.biPlanes = 1;
+ theBMI.biBitCount = 32; // 24 bits + alpha channel
+ theBMI.biCompression = BI_RGB; // no compression
+ theBMI.biSizeImage = 0;
+ theBMI.biXPelsPerMeter = 0;
+ theBMI.biYPelsPerMeter = 0;
+ theBMI.biClrUsed = 0; // use the whole palette
+ theBMI.biClrImportant = 0;
+
+ BYTE* theSrcBits;
+ HBITMAP theBmpSrc;
+
+ // Create DIB section in shared memory
+ theBmpSrc = CreateDIBSection( inDCSrc, ( BITMAPINFO* ) &theBMI, DIB_RGB_COLORS, ( void** )
+&theSrcBits, 0, 0L );
+
+ BYTE* theDestBits;
+ HBITMAP theBmpDest;
+
+ // Create DIB section in shared memory
+ theBmpDest = CreateDIBSection( inDCDest, ( BITMAPINFO* ) &theBMI, DIB_RGB_COLORS, ( void** )
+&theDestBits, 0, 0L );
+
+ // Copy our source and destination bitmaps onto our DIBSections.
+ // so we can get access to their bits using the BYTE*'s we used
+ // in the CreateDIBSection()s above.
+
+ HDC theDC = CreateCompatibleDC( nullptr );
+
+ HBITMAP theDCOld = ( HBITMAP ) SelectObject( theDC, theBmpSrc );
+
+ if ( !StretchBlt( theDC, 0, 0, inWidth, inHeight, inDCSrc, inSourceX, inSourceY,
+inSourceWidth, inSourceHeight, SRCCOPY ) )
+ {
+ SelectObject( theDC, theDCOld );
+ DeleteDC( theDC );
+ DeleteObject( theBmpSrc );
+ DeleteObject( theBmpDest );
+ return false;
+ }
+
+ SelectObject( theDC, theBmpDest );
+
+ if (! StretchBlt( theDC, 0, 0, inWidth, inHeight, inDCDest, inX, inY, inWidth, inHeight,
+SRCCOPY ) )
+ {
+ SelectObject( theDC, theDCOld );
+ DeleteDC( theDC );
+ DeleteObject( theBmpSrc );
+ DeleteObject( theBmpDest );
+ return true;
+ }
+
+ SelectObject( theDC, theDCOld );
+ DeleteDC( theDC );
+
+ short theXLoop, theYLoop;
+
+ for( theYLoop=0; theYLoop<inHeight; ++theYLoop )
+ {
+ LPBYTE theDestRGB = ( LPBYTE ) &( ( DWORD* ) theDestBits)[theYLoop * inWidth];
+ LPBYTE theSrcRGB = ( LPBYTE ) &( ( DWORD* ) theSrcBits)[theYLoop * inWidth];
+
+ for( theXLoop=0; theXLoop<inWidth; theXLoop++ )
+ {
+ theSrcRGB[0] = ( BYTE ) ( ( theDestRGB[0] * ( 255 - inAlpha ) + theSrcRGB[0]
+* inAlpha ) >> 8 );
+ theSrcRGB[1] = ( BYTE ) ( ( theDestRGB[1] * ( 255 - inAlpha ) + theSrcRGB[1]
+* inAlpha ) >> 8 );
+ theSrcRGB[2] = ( BYTE ) ( ( theDestRGB[2] * ( 255 - inAlpha ) + theSrcRGB[2]
+* inAlpha ) >> 8 );
+
+ theSrcRGB += 4;
+ theDestRGB += 4;
+ }
+ }
+
+ theDC = CreateCompatibleDC( nullptr );
+
+ theDCOld = ( HBITMAP ) SelectObject( theDC, theBmpSrc );
+
+ if ( !BitBlt( inDCDest, inX, inY, inWidth, inHeight, theDC, 0, 0, SRCCOPY ) )
+ {
+ SelectObject( theDC, theDCOld );
+ DeleteDC( theDC );
+ DeleteObject( theBmpSrc );
+ DeleteObject( theBmpDest );
+ return false;
+ }
+
+ SelectObject( theDC, theDCOld );
+ DeleteDC( theDC );
+
+ DeleteObject( theBmpSrc );
+ DeleteObject( theBmpDest );
+
+ return true;
+}*/
diff --git a/src/Authoring/Studio/_Win/Controls/BufferedRenderer.h b/src/Authoring/Studio/_Win/Controls/BufferedRenderer.h
new file mode 100644
index 00000000..19cc8f78
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/BufferedRenderer.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_BUFFERED_RENDERER_H
+#define INCLUDED_BUFFERED_RENDERER_H 1
+
+#pragma once
+
+#include "WinRenderer.h"
+
+#include <QPainter>
+#include <QPixmap>
+#include <QFont>
+
+class CBufferedRenderer : public CWinRenderer
+{
+public:
+ CBufferedRenderer(const QSize &inSize);
+
+ virtual ~CBufferedRenderer();
+ QPixmap pixmap() const override { return m_CurrentBitmap;}
+
+ // void TransparentBltTo( CRenderer* inRenderer, short inAlpha );
+
+protected:
+ QPixmap m_OldBitmap;
+ QPixmap m_CurrentBitmap;
+ QSize m_Size;
+
+ // bool AlphaBlendExt( HDC inDCDest, short inX, short inY, short inWidth, short inHeight, HDC
+ //inDCSrc, short inSourceX, short inSourceY, short inSourceWidth, short inSourceHeight, short
+ //inAlpha );
+};
+#endif // INCLUDED_BUFFERED_RENDERER_H
diff --git a/src/Authoring/Studio/_Win/Controls/MFCEditControl.cpp b/src/Authoring/Studio/_Win/Controls/MFCEditControl.cpp
new file mode 100644
index 00000000..19b263d9
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/MFCEditControl.cpp
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MFCEditControl.h"
+
+//==============================================================================
+// Implementation
+//==============================================================================
+
+#ifdef USE_RICHEDIT
+IMPLEMENT_DYNAMIC(CMFCEditControl, CRichEditCtrl)
+#else
+IMPLEMENT_DYNAMIC(CMFCEditControl, CEdit)
+#endif
+
+CMFCEditControl::CMFCEditControl()
+ : m_Changed(false)
+ , m_HotKeys(nullptr)
+{
+}
+
+CMFCEditControl::~CMFCEditControl()
+{
+ // This control's window has been destroyed by PlatformEditControl,
+ // so a SigTextCommit is potentially dangerous as it will lead to GetText (below)
+ // which will try to get text from a destroyed window.
+ // However, if this is needed, note that CEdit works, but CRichEditCtrl don't.
+ // Can revert to using CEdit by commenting out the line in MFCEditControl.h
+
+ // if ( m_Changed )
+ //{
+ // m_Changed = false;
+ // SigTextCommit( );
+ //}
+}
+
+Q3DStudio::CString CMFCEditControl::GetText()
+{
+ Q3DStudio::CString theResult;
+
+#ifdef USE_RICHEDIT
+ GETTEXTLENGTHEX getTextLengthEx;
+ getTextLengthEx.flags = GTL_DEFAULT | GTL_USECRLF;
+ getTextLengthEx.codepage = 1200;
+ LRESULT lResult = SendMessage(EM_GETTEXTLENGTHEX, (WPARAM)&getTextLengthEx, 0);
+ if (lResult > 0) {
+ GETTEXTEX getTextEx;
+ getTextEx.cb = (DWORD)(lResult + 2);
+ getTextEx.codepage = 1200;
+ getTextEx.flags = GT_DEFAULT | GT_USECRLF;
+ getTextEx.lpDefaultChar = nullptr;
+ getTextEx.lpUsedDefChar = nullptr;
+ wchar_t *ws = new wchar_t[lResult / 2 + 1];
+ SendMessage(EM_GETTEXTEX, (WPARAM)&getTextEx, (LPARAM)ws);
+ theResult = ws;
+ delete[] ws;
+ }
+#else
+ ::CString theWindowText;
+ GetWindowText(theWindowText);
+ // normal gettext, which depends on machine's locale
+ theResult = theWindowText;
+#endif
+
+ return theResult;
+}
+
+void CMFCEditControl::SetText(const Q3DStudio::CString &inText)
+{
+ // The text manipulation is actually already being handled by the CEdit/CWnd
+ // This SetText is called by our custom CPropertyMultilineEdit and screws with the MFC's
+ // own text handling functions.
+ // Essentially, we still need this to set up the initial use of the MFC Edit.
+ if (GetText() == "") {
+#ifdef USE_RICHEDIT
+ SETTEXTEX setTextEx;
+ setTextEx.codepage = 1200;
+ setTextEx.flags = ST_DEFAULT;
+ SendMessage(EM_SETTEXTEX, (WPARAM)&setTextEx,
+ (LPARAM)(static_cast<const wchar_t *>(inText)));
+#else
+ // normal settext, which depends on machine's locale
+ SetWindowText(inText.GetMulti());
+#endif
+ }
+}
+
+#ifdef USE_RICHEDIT
+BEGIN_MESSAGE_MAP(CMFCEditControl, CRichEditCtrl)
+#else
+BEGIN_MESSAGE_MAP(CMFCEditControl, CEdit)
+#endif
+ON_CONTROL_REFLECT(EN_CHANGE, OnEnChange)
+ON_WM_LBUTTONDBLCLK()
+ON_WM_SETFOCUS()
+ON_WM_KILLFOCUS()
+END_MESSAGE_MAP()
+
+// CMFCEditControl message handlers
+
+void CMFCEditControl::OnEnChange()
+{
+ m_Changed = true;
+ SigTextChanged();
+}
+
+void CMFCEditControl::OnLButtonDblClk(UINT inSomething, CPoint inPoint)
+{
+#ifdef USE_RICHEDIT
+ CRichEditCtrl::OnLButtonDblClk(inSomething, inPoint);
+#else
+ CEdit::OnLButtonDblClk(inSomething, inPoint);
+#endif
+
+ // select all text in this CEdit/CRichEditCtrl
+ SetSel(0, -1);
+}
+
+void CMFCEditControl::OnSetFocus(CWnd *inWnd)
+{
+#ifdef USE_RICHEDIT
+ CRichEditCtrl::OnSetFocus(inWnd);
+#else
+ CEdit::OnSetFocus(inWnd);
+#endif
+
+ m_Changed = false;
+}
+
+void CMFCEditControl::OnKillFocus(CWnd *inWnd)
+{
+#ifdef USE_RICHEDIT
+ CRichEditCtrl::OnKillFocus(inWnd);
+#else
+ CEdit::OnKillFocus(inWnd);
+#endif
+
+ if (m_Changed) {
+ m_Changed = false;
+ SigTextCommit();
+ }
+}
+
+BOOL CMFCEditControl::PreTranslateMessage(MSG *pMsg)
+{
+ if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) {
+ // Reserve some keys for Studio's hotkeys
+ // This is only a subset of Studio's hotkeys as listed in
+ // CWorkspace::RegisterGlobalKeyboardShortcuts( CHotKeys* inShortcutHandler )
+ // We need to make sure that EditControl's hotkeys won't collide with Studio's hotkeys
+ bool theReservedHotKeys = false;
+ if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN
+ || pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP) {
+ if (pMsg->wParam >= CHotKeys::KEY_F1
+ && pMsg->wParam
+ <= CHotKeys::KEY_F12) // F1 to F12 keys, this may fail if not in windows
+ {
+ theReservedHotKeys = true;
+ } else if (CHotKeys::IsKeyDown(VK_CONTROL)) {
+ unsigned int theCharacterCode = ::MapVirtualKey(static_cast<UINT>(pMsg->wParam), 2);
+
+ switch (theCharacterCode) {
+ case 's':
+ case 'S': // save file / save file as
+ case 'o':
+ case 'O': // open file
+ case 'n':
+ case 'N': // new file
+ theReservedHotKeys = true;
+ }
+ }
+ }
+
+ if (theReservedHotKeys) {
+ if (m_HotKeys != nullptr)
+ m_HotKeys->PreTranslateMessage(pMsg);
+ } else {
+ // no idea what all that filtering does, since Bug 1695 appears to be working
+ // and it is causing problem with Japanese text input (2639)
+ ::TranslateMessage(pMsg);
+ ::DispatchMessage(pMsg);
+ }
+ return TRUE;
+
+ //// This is REALLY bad, but it's basically filtering all the messages that it thinks it
+ ///might want and handles those.
+ // if ((::GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0 && (::GetAsyncKeyState(VK_RBUTTON) &
+ // 0x8000) == 0)
+ //{
+ // unsigned int theChar = pMsg->wParam;
+
+ // /*
+ // * VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
+ // * 0x40 : unassigned
+ // * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
+ // */
+
+ // /*
+ // #define VK_OEM_1 0xBA // ';:' for US
+ // #define VK_OEM_PLUS 0xBB // '+' any country
+ // #define VK_OEM_COMMA 0xBC // ',' any country
+ // #define VK_OEM_MINUS 0xBD // '-' any country
+ // #define VK_OEM_PERIOD 0xBE // '.' any country
+ // #define VK_OEM_2 0xBF // '/?' for US
+ // #define VK_OEM_3 0xC0 // '`~' for US
+ // #define VK_OEM_4 0xDB // '[{' for US
+ // #define VK_OEM_5 0xDC // '\|' for US
+ // #define VK_OEM_6 0xDD // ']}' for US
+ // #define VK_OEM_7 0xDE // ''"' for US
+ // #define VK_OEM_8 0xDF
+ // */
+
+ // if ( theChar == VK_RETURN || theChar == VK_BACK || theChar == VK_MENU ||
+ // ( theChar >= VK_SPACE && theChar < VK_LWIN ) ||
+ // ( theChar >= VK_OEM_1 && theChar <= VK_OEM_8 )
+ // )
+ // {
+ // ::TranslateMessage( pMsg );
+ // ::DispatchMessage( pMsg );
+ // return TRUE;
+ // }
+ //}
+ }
+
+#ifdef USE_RICHEDIT
+ return CRichEditCtrl::PreTranslateMessage(pMsg);
+#else
+ return CEdit::PreTranslateMessage(pMsg);
+#endif
+}
+
+BOOL CMFCEditControl::Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID)
+{
+// TODO: Add your specialized code here and/or call the base class
+
+#ifdef USE_RICHEDIT
+ BOOL theResult = CRichEditCtrl::Create(dwStyle, rect, pParentWnd, nID);
+ SetEventMask(GetEventMask() | ENM_CHANGE);
+#else
+ BOOL theResult = CEdit::Create(dwStyle, rect, pParentWnd, nID);
+#endif
+
+ return theResult;
+}
diff --git a/src/Authoring/Studio/_Win/Controls/MFCEditControl.h b/src/Authoring/Studio/_Win/Controls/MFCEditControl.h
new file mode 100644
index 00000000..9d688fdc
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/MFCEditControl.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "HotKeys.h"
+
+//==============================================================================
+// Class
+//==============================================================================
+
+// RichEditCtrl, though super useful, has some stability issue to be ironed out.
+// Use CEdit by commenting out the line below if it is causing crashes.
+#define USE_RICHEDIT
+
+#ifdef USE_RICHEDIT
+class CMFCEditControl : public CRichEditCtrl
+#else
+class CMFCEditControl : public CEdit
+#endif
+{
+protected:
+ DECLARE_DYNAMIC(CMFCEditControl)
+
+public:
+ boost::signal<void()> SigTextChanged;
+ boost::signal<void()> SigTextCommit;
+
+protected:
+ bool m_Changed;
+ CHotKeys *m_HotKeys; // global keyboard shortcut (Studio's hotkeys)
+
+public:
+ CMFCEditControl();
+ virtual ~CMFCEditControl();
+
+ Q3DStudio::CString GetText();
+ void SetText(const Q3DStudio::CString &inText);
+
+ void SetGlobalKeyboardShortcuts(CHotKeys *inHotKeys) { m_HotKeys = inHotKeys; }
+
+protected:
+ DECLARE_MESSAGE_MAP()
+public:
+ afx_msg void OnEnChange();
+ afx_msg void OnLButtonDblClk(UINT, CPoint);
+ afx_msg void OnSetFocus(CWnd *);
+ afx_msg void OnKillFocus(CWnd *);
+
+ virtual BOOL PreTranslateMessage(MSG *inMessage);
+ virtual BOOL Create(DWORD dwStyle, const RECT &rect, CWnd *pParentWnd, UINT nID);
+};
diff --git a/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.cpp b/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.cpp
new file mode 100644
index 00000000..72a5f17b
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "OffscreenRenderer.h"
+#include "AppFonts.h"
+
+//=============================================================================
+/**
+ * Constructor: Overrides a protected constructor on the base class. Sets up
+ * all the requirements so that this renderer is valid.
+ */
+COffscreenRenderer::COffscreenRenderer(const QRect &inClippingRect)
+ : CWinRenderer()
+{
+ m_pixmap = QPixmap(inClippingRect.size());
+ m_painter = new QPainter(&m_pixmap);
+ QFont font = CAppFonts::GetInstance()->GetNormalFont();
+ m_painter->setFont(font);
+
+ // Set up the specified clipping region. Renderer is now valid for use.
+ PushClippingRect(inClippingRect);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+COffscreenRenderer::~COffscreenRenderer()
+{
+ delete m_painter;
+ m_painter = nullptr;
+}
diff --git a/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.h b/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.h
new file mode 100644
index 00000000..dccd259f
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/OffscreenRenderer.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_OFFSCREEN_RENDERER_H
+#define INCLUDED_OFFSCREEN_RENDERER_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "WinRenderer.h"
+
+//=============================================================================
+/**
+ * Class for creating a renderer compatible with the current display, but that
+ * does not actually draw to the display. Provided so that you controls can
+ * query text size outside of their draw functions. This class could be
+ * further extended to provide offscreen drawing and blitting, though it's
+ * not currently set up to do so.
+ */
+class COffscreenRenderer : public CWinRenderer
+{
+public:
+ COffscreenRenderer(const QRect &inClippingRect);
+ virtual ~COffscreenRenderer();
+
+ QPixmap pixmap() const override { return m_pixmap;}
+
+protected:
+ QPixmap m_pixmap;
+};
+
+#endif // INCLUDED_OFFSCREEN_RENDERER_H
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformEditControl.cpp b/src/Authoring/Studio/_Win/Controls/PlatformEditControl.cpp
new file mode 100644
index 00000000..4c581806
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformEditControl.cpp
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PlatformEditControl.h"
+#include "Renderer.h"
+#include "StudioPreferences.h"
+
+#ifdef WIN32
+
+CPlatformEditControl::CPlatformEditControl(UICRenderDevice inParent)
+ : CPlatformWindowControl(inParent)
+{
+#ifdef USE_RICHEDIT
+ BOOL theReturn = m_EditWindow.Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE
+ | ES_WANTRETURN | ES_AUTOVSCROLL,
+ CRect(0, 0, 0, 0), CWnd::FromHandle(inParent), 1);
+#else
+ BOOL theReturn = m_EditWindow.CreateEx(0, _T("EDIT"), "", WS_CHILD | WS_VSCROLL | ES_MULTILINE
+ | ES_WANTRETURN | ES_AUTOVSCROLL,
+ 0, 0, 0, 0, inParent, nullptr);
+#endif
+ ASSERT(theReturn);
+
+ Q_UNUSED(theReturn); // for release builds
+
+ m_EditWindow.SigTextChanged.connect(std::bind(&CPlatformEditControl::OnTextChanged, this));
+ m_EditWindow.SigTextCommit.connect(std::bind(&CPlatformEditControl::OnTextCommit, this));
+
+ m_Font.CreatePointFont(
+ 80, CString(CStudioPreferences::GetFontFaceName())); // size specified in 1/10ths of a point
+ m_EditWindow.SetFont(&m_Font);
+ m_EditWindow.SetTextMode(TM_PLAINTEXT | TM_MULTILEVELUNDO);
+ m_EditWindow.SetBackgroundColor(FALSE, CStudioPreferences::GetLightBaseColor());
+
+ m_Window = m_EditWindow.GetSafeHwnd();
+}
+
+CPlatformEditControl::~CPlatformEditControl()
+{
+ m_EditWindow.DestroyWindow();
+}
+
+//=============================================================================
+/**
+ * Essentially, this just turns the window off. Do not use the CControl::SetVisible
+ * because that would screw with the control's size and you will notice the inspector
+ * palette having cut off rows.
+ *
+ * @param inIsVisible If the window should be visible
+ */
+void CPlatformEditControl::SetWindowVisible(bool inIsVisible)
+{
+ if (inIsVisible)
+ m_EditWindow.ShowWindow(SW_SHOWNORMAL);
+ else
+ m_EditWindow.ShowWindow(SW_HIDE);
+}
+
+void CPlatformEditControl::OnTextChanged()
+{
+ SigTextChanged();
+}
+
+void CPlatformEditControl::OnTextCommit()
+{
+ SigTextCommit();
+}
+
+void CPlatformEditControl::SetText(const Q3DStudio::CString &inText)
+{
+ m_EditWindow.SetText(inText);
+}
+
+Q3DStudio::CString CPlatformEditControl::GetText()
+{
+ return m_EditWindow.GetText();
+}
+
+void CPlatformEditControl::EnableWindow(bool inEnable)
+{
+ m_EditWindow.EnableWindow(inEnable);
+}
+
+//==============================================================================
+// CControl
+//==============================================================================
+
+void CPlatformEditControl::Draw(CRenderer *inRenderer)
+{
+ // Create a clipping region for the MFC Edit
+ CRct theUICClipRect = inRenderer->GetClippingRect();
+ RECT theRect(theUICClipRect);
+ CRgn theClipRgn;
+ theClipRgn.CreateRectRgnIndirect(&theRect);
+ HRGN theClipHandle = (HRGN)theClipRgn;
+ m_EditWindow.SetWindowRgn(theClipHandle, TRUE);
+
+ if (m_EditWindow.IsWindowVisible() == FALSE)
+ m_EditWindow.ShowWindow(SW_SHOWNORMAL);
+
+ // Draw a bounding box around the entire control
+ CColor theTopColor = CStudioPreferences::GetControlRectTopLineColor();
+ CColor theSideColor = CStudioPreferences::GetControlRectSideLineColor();
+ CColor theBottomColor = CStudioPreferences::GetControlRectBottomLineColor();
+
+ inRenderer->DrawRectOutline(GetSize(), theTopColor, theSideColor, theBottomColor, theSideColor);
+}
+
+#endif // WIN32
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformEditControl.h b/src/Authoring/Studio/_Win/Controls/PlatformEditControl.h
new file mode 100644
index 00000000..1347b217
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformEditControl.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PLATFORM_EDIT_CONTROL_H
+#define INCLUDED_PLATFORM_EDIT_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Include
+//==============================================================================
+#include "PlatformWindowControl.h"
+#include "MFCEditControl.h"
+
+//==============================================================================
+// Class
+//==============================================================================
+
+#ifdef WIN32
+
+class CPlatformEditControl : public CPlatformWindowControl
+{
+protected:
+ CFont m_Font; ///< MFC font for the edit text
+ CMFCEditControl m_EditWindow; ///< MFC Edit Window
+
+public:
+ std::signal0<void> SigTextChanged;
+ std::signal0<void> SigTextCommit;
+
+public:
+ CPlatformEditControl(UICRenderDevice inParent);
+ virtual ~CPlatformEditControl();
+
+ void SetWindowVisible(bool inIsVisible);
+
+ void SetText(const Q3DStudio::CString &inText);
+ Q3DStudio::CString GetText();
+ void EnableWindow(bool inEnable);
+
+ void SetGlobalKeyboardShortcuts(CHotKeys *inHotKeys)
+ {
+ m_EditWindow.SetGlobalKeyboardShortcuts(inHotKeys);
+ }
+
+ // CControl
+ virtual void Draw(CRenderer *inRenderer);
+
+protected:
+ void OnTextChanged();
+ void OnTextCommit();
+};
+
+#endif // WIN32
+
+#endif // INCLUDED_PLATFORM_EDIT_CONTROL_H
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.cpp b/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.cpp
new file mode 100644
index 00000000..fc2c2509
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PlatformStaticControl.h"
+
+#ifdef WIN32
+
+CPlatformStaticControl::CPlatformStaticControl(UICRenderDevice inParent)
+ : CPlatformWindowControl(inParent)
+{
+ BOOL theReturn = m_EditWindow.CreateEx(0, _T("STATIC"), _T(""), WS_VISIBLE | WS_CHILD, 0, 0, 0,
+ 0, inParent, nullptr);
+ ASSERT(theReturn);
+ theReturn;
+
+ m_Font.CreatePointFont(
+ 80, CString(CStudioPreferences::GetFontFaceName())); // size specified in 1/10ths of a point
+ m_EditWindow.SetFont(&m_Font);
+
+ m_Window = m_EditWindow.GetSafeHwnd();
+}
+
+CPlatformStaticControl::~CPlatformStaticControl()
+{
+ m_EditWindow.DestroyWindow();
+}
+
+void CPlatformStaticControl::SetText(const Q3DStudio::CString &inText)
+{
+ m_EditWindow.SetWindowText(inText);
+}
+
+#endif // WIN32 \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.h b/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.h
new file mode 100644
index 00000000..e8b68fa6
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformStaticControl.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_PLATFORM_STATIC_CONTROL_H
+#define INCLUDED_PLATFORM_STATIC_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Include
+//==============================================================================
+
+#include "PlatformWindowControl.h"
+
+//==============================================================================
+// Class
+//==============================================================================
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+class CPlatformStaticControl : public CPlatformWindowControl
+{
+protected:
+ CFont m_Font; ///< MFC font for the static text
+ CStatic m_EditWindow; ///< MFC Edit Window
+
+public:
+ CPlatformStaticControl(UICRenderDevice inParent);
+ virtual ~CPlatformStaticControl();
+
+ void SetText(const Q3DStudio::CString &inText);
+};
+
+#endif // WIN32
+
+#endif // INCLUDED_PLATFORM_STATIC_CONTROL_H
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.cpp b/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.cpp
new file mode 100644
index 00000000..89279702
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Include
+//==============================================================================
+#include "PlatformWindowControl.h"
+
+CPlatformWindowControl::CPlatformWindowControl(UICRenderDevice inParent)
+ : m_Window(nullptr)
+{
+ inParent;
+}
+
+CPlatformWindowControl::~CPlatformWindowControl()
+{
+}
+
+//==============================================================================
+// CControl
+//==============================================================================
+
+void CPlatformWindowControl::SetPosition(CPt inPosition)
+{
+ CControl::SetPosition(inPosition);
+
+ CPt theGlobalPosition = CControl::GetGlobalPosition(CPt(0, 0));
+
+ ::SetWindowPos(m_Window, nullptr, theGlobalPosition.x + 1, theGlobalPosition.y + 1, 0, 0,
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
+}
+
+void CPlatformWindowControl::SetSize(CPt inSize)
+{
+ CControl::SetSize(inSize);
+
+ ::SetWindowPos(m_Window, nullptr, 0, 0, inSize.x - 2, inSize.y - 2,
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
+}
+
+void CPlatformWindowControl::Invalidate(bool inInvalidate)
+{
+ CControl::Invalidate(inInvalidate);
+
+ // This is neccesary to handle the scroll behavior when
+ // this item is embedded in a scroll window.
+ CPt theGlobalPosition = CControl::GetGlobalPosition(CPt(0, 0));
+ ::SetWindowPos(m_Window, nullptr, theGlobalPosition.x + 1, theGlobalPosition.y + 1, 0, 0,
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
+}
+
+void CPlatformWindowControl::OnVisibleStateChange(bool inIsVisible)
+{
+ ::ShowWindow(m_Window, ((inIsVisible) ? (SW_SHOWNORMAL) : (SW_HIDE)));
+}
+
+void CPlatformWindowControl::OnParentVisibleStateChanged(bool inIsVisible)
+{
+ ::ShowWindow(m_Window, ((inIsVisible) ? (SW_SHOWNORMAL) : (SW_HIDE)));
+}
+
+UICRenderDevice CPlatformWindowControl::GetPlatformDevice()
+{
+ return m_Window;
+}
diff --git a/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.h b/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.h
new file mode 100644
index 00000000..f76697f6
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/PlatformWindowControl.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_PLATFORM_WINDOW_CONTROL_H
+#define INCLUDED_PLATFORM_WINDOW_CONTROL_H 1
+
+#pragma once
+
+//==============================================================================
+// Include
+//==============================================================================
+
+#include "Control.h"
+
+//==============================================================================
+// Class
+//==============================================================================
+
+class CPlatformWindowControl : public CControl
+{
+protected:
+ UICRenderDevice m_Window; ///<
+
+public:
+ CPlatformWindowControl(UICRenderDevice inParent);
+ virtual ~CPlatformWindowControl();
+
+ // CControl
+ void SetPosition(CPt inPosition) override;
+ void SetSize(CPt inSize) override;
+ void Invalidate(bool inInvalidate = true) override;
+
+ void OnVisibleStateChange(bool inIsVisible) override;
+ void OnParentVisibleStateChanged(bool inIsVisible) override;
+
+ UICRenderDevice GetPlatformDevice() override;
+};
+
+#endif // INCLUDED_PLATFORM_WINDOW_CONTROL_H
diff --git a/src/Authoring/Studio/_Win/Controls/WinRenderer.cpp b/src/Authoring/Studio/_Win/Controls/WinRenderer.cpp
new file mode 100644
index 00000000..3b9cddf1
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/WinRenderer.cpp
@@ -0,0 +1,525 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "WinRenderer.h"
+#include "MasterP.h"
+#include <math.h>
+#include "CoreUtils.h"
+
+//=============================================================================
+/**
+ * Leaves the Renderer in an invalid state, only for subclasses.
+ * In order for this renderer to be valid, you must do two things: (1) you must
+ * create a CDC and set m_DC equal to it, and (2) you must call PushClippingRect.
+ */
+CWinRenderer::CWinRenderer()
+ : m_painter(nullptr)
+{
+}
+
+//=============================================================================
+/**
+ * Leaves the Renderer in an invalid state, only for subclasses.
+ * PushClippingRect must be called in order for this to become valid.
+ */
+CWinRenderer::CWinRenderer(QPainter *inDC)
+{
+ m_painter = inDC;
+}
+
+CWinRenderer::CWinRenderer(QPainter *inDC, const QRect &inClippingRect)
+{
+ m_painter = inDC;
+
+ PushClippingRect(inClippingRect);
+}
+
+CWinRenderer::~CWinRenderer()
+{
+ m_Pens.clear();
+}
+
+//=============================================================================
+/**
+ * Draws a rectangle and fills it with inColor.
+ * The rectangle has no border and is solid.
+ * The coordinates are converted to global space using the current translation.
+ * @param inCoordinates the coordinates of the rectangle.
+ * @param inColor the color of the rectangle to draw.
+ */
+void CWinRenderer::FillSolidRect(const QRect &inCoordinates, const QColor &inColor)
+{
+ QRect theRect(inCoordinates.topLeft() + m_Translation,
+ inCoordinates.size());
+
+ m_painter->fillRect(theRect, inColor);
+}
+
+//=============================================================================
+/**
+ * Move the pen to the position.
+ * This will put the pen at inPoint but will not draw a line there from the
+ * current location.
+ * @param inPoint the location to move to, in local coordinates.
+ */
+void CWinRenderer::MoveTo(const QPoint &inPoint)
+{
+ m_currentPos = inPoint + m_Translation;
+}
+
+//=============================================================================
+/**
+ * Move the pen to the position.
+ * This will put the pen to the point but will not draw a line there from the
+ * current location.
+ * @param inX the X location to move to, in local coordinates.
+ * @param inY the Y location to move to, in local coordinates.
+ */
+void CWinRenderer::MoveTo(long inX, long inY)
+{
+ MoveTo(QPoint(inX, inY));
+}
+
+//=============================================================================
+/**
+ * Draw a line from the current pen location to inPoint.
+ * This will draw a line to inPoint and move the current location of the pen
+ * to inPoint.
+ * @param inPoint the location to draw to, in local coordinates.
+ */
+void CWinRenderer::LineTo(const QPoint &inPoint)
+{
+ const QPoint point = inPoint + m_Translation;
+ m_painter->drawLine(m_currentPos, point);
+ m_painter->drawLine(point, QPoint(point.x(), point.y() + 1));
+ m_currentPos = point;
+}
+
+//=============================================================================
+/**
+ * Draw a line from the current pen location to the point.
+ * This will draw a line to the point and move the current location of the pen
+ * to inPoint.
+ * @param inX the X coordinate of the point to draw to, in local coordinates.
+ * @param inY the Y coordinate of the point to draw to, in local coordinates.
+ */
+void CWinRenderer::LineTo(long inX, long inY)
+{
+ LineTo(QPoint(inX, inY));
+}
+
+//=============================================================================
+/**
+ * Change the active pen color.
+ * This acts as a stack so that it does not interfere with previous components.
+ * Once the color is done being used PopPen should be called.
+ * @param inColor the color to make the current pen.
+ * @param inWidth the pen width, in pixels.
+ */
+void CWinRenderer::PushPen(const QColor &inColor, int inWidth)
+{
+ QPen thePen = GetPen(inColor, inWidth, Qt::SolidLine);
+
+ QPen theCurrentPen = m_painter->pen();
+ m_painter->setPen(thePen);
+ m_PenList.push_back(theCurrentPen);
+}
+
+void CWinRenderer::PopPen()
+{
+ QPen thePen = m_PenList.back();
+ m_PenList.pop_back();
+ m_painter->setPen(thePen);
+}
+
+//=============================================================================
+/**
+ * Copy the bits from another renderer into this one.
+ * This will copy the rect from inRenderer into this renderer.
+ * inRect will be offset by this renderer's current translation. inXSrc and inYSrc
+ * will be offset by inRenderer's current translation.
+ * @param inRect the position and size of the area to be copied into this renderer.
+ * @param inPixmap the renderer to copy from.
+ * @param xSrc the x location of the location to copy from in inRenderer.
+ * @param ySrc the y location of the location to copy from in inRenderer.
+ */
+void CWinRenderer::BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc)
+{
+ const auto inTranslation = inRenderer->GetTranslation();
+ inXSrc += inTranslation.x();
+ inYSrc += inTranslation.y();
+
+ auto srcRect = inRect;
+ auto destRect = inRect;
+ destRect.translate(m_Translation);
+ srcRect.moveTo(inXSrc, inYSrc);
+ m_painter->save();
+ m_painter->setCompositionMode(QPainter::CompositionMode_DestinationOver);
+ m_painter->drawPixmap(destRect, inRenderer->pixmap(), srcRect);
+ m_painter->restore();
+}
+
+//=============================================================================
+/**
+ * Draws text out without clipping it.
+ * @param inPoint the point at which to draw the text. (upper left corner)
+ * @param inText the text to draw.
+ */
+void CWinRenderer::DrawText(float inX, float inY, const QString &inText)
+{
+ inX += m_Translation.x();
+ inY += m_Translation.y();
+ m_painter->drawText(QPointF(inX, inY), inText);
+}
+
+//=============================================================================
+/**
+ * Draws text out to a clipped rectangle.
+ * If any text occurs outside the bounding box then it will be clipped.
+ * @param inPoint the point at which to draw the text. (upper left corner)
+ * @param inText the text to draw.
+ * @param inBoundingBox the bounding box used to clip the text.
+ * @param inColor color to draw the text in
+ */
+void CWinRenderer::DrawText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingBox, const QColor &inColor)
+{
+ inX += m_Translation.x();
+ inY += m_Translation.y();
+
+ QRectF rect(inBoundingBox);
+ rect.translate(inX, inY);
+ m_painter->save();
+ QPen pen(inColor);
+ m_painter->setPen(pen);
+ m_painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, inText);
+ m_painter->restore();
+}
+
+//=============================================================================
+/**
+ * Draws BOLD text out to a clipped rectangle.
+ * If any text occurs outside the bounding box then it will be clipped.
+ * @param inPoint the point at which to draw the text. (upper left corner)
+ * @param inText the text to draw.
+ * @param inBoundingBox the bounding box used to clip the text.
+ * @param inColor color to draw the text in
+ */
+void CWinRenderer::DrawBoldText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingBox, const QColor &inColor)
+{
+ inX += m_Translation.x();
+ inY += m_Translation.y();
+
+ QRectF rect(inBoundingBox);
+ rect.translate(inX, inY);
+ m_painter->save();
+ QPen pen(inColor);
+ m_painter->setPen(pen);
+ QFont font = m_painter->font();
+ font.setBold(true);
+ m_painter->setFont(font);
+ m_painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, inText);
+ m_painter->restore();
+}
+
+//=============================================================================
+/**
+ * Gets the dimensions of the text string as it would be written out to the
+ * screen.
+ * @param inText the text to check the length on.
+ * @return the length of the text in pixels.
+ */
+QSize CWinRenderer::GetTextSize(const QString &inText)
+{
+ QFontMetrics fm = m_painter->fontMetrics();
+ return fm.size(Qt::TextSingleLine, inText);
+}
+
+//=============================================================================
+/**
+ * Draws a a three-dimensional rectangle with the top and left sides in the
+ * color specified by inTopLeftColor and the bottom and right sides in the color
+ * specified by inBottomRightColor.
+ *
+ * @param inRect The rectangle to draw
+ * @param inTopLeftColor Color for the top and left sides of the rect
+ * @param inBottomRightColor Color for the bottom and right sides of the rect
+ */
+void CWinRenderer::Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor,
+ const QColor &inBottomRightColor)
+{
+ auto rect = inRect;
+ rect.translate(m_Translation);
+ m_painter->drawRect(rect);
+ m_painter->save();
+ m_painter->setPen(inTopLeftColor);
+ m_painter->drawLine(rect.topLeft(), rect.bottomLeft());
+ m_painter->drawLine(rect.topLeft(), rect.topRight());
+ m_painter->setPen(inBottomRightColor);
+ m_painter->drawLine(rect.bottomLeft(), rect.bottomRight());
+ m_painter->drawLine(rect.bottomRight(), rect.topRight());
+ m_painter->restore();
+}
+
+//==============================================================================
+/**
+ * DrawBitmap
+ *
+ * Draw a bitmap given a device context, position and HBITMAP handle.
+ *
+ * @param inDC Device context for drawing
+ * @param inPos CPoint position for drawing
+ * @param inBitmap Handle of the bitmap to draw
+ */
+//==============================================================================
+void CWinRenderer::DrawBitmap(const QPoint &inPos, const QPixmap &inImage)
+{
+ m_painter->save();
+ m_painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+ m_painter->drawPixmap(inPos + m_Translation, inImage);
+ m_painter->restore();;
+}
+
+void CWinRenderer::PushTranslation(const QPoint &inTranslation)
+{
+ m_Translation += inTranslation;
+
+ m_Translations.push_back(inTranslation);
+}
+
+void CWinRenderer::PopTranslation()
+{
+ QPoint thePreviousTranslation = m_Translations.back();
+ m_Translation -= thePreviousTranslation;
+
+ m_Translations.pop_back();
+}
+
+QPoint CWinRenderer::GetTranslation()
+{
+ return m_Translation;
+}
+
+QPainter* CWinRenderer::GetPainter()
+{
+ return m_painter;
+}
+
+//==============================================================================
+/**
+ * Get the current clipping rect.
+ * The clipping rect is the boundary of pixels that will be drawn to the DC.
+ * This can be used to not draw non-visible objects.
+ * @return the clipping rect in local coordinates.
+ */
+QRect CWinRenderer::GetClippingRect()
+{
+ QRect theClippingRect = m_painter->clipBoundingRect().toRect();
+ theClippingRect.translate(-m_Translation);
+
+ return theClippingRect;
+}
+
+//==============================================================================
+/**
+ * Push a clipping rect onto the stack of clipping rects.
+ * This will cause any drawing outside of the clipping rect to be ignored. The
+ * Control class also uses this to not draw objects outside of this rect.
+ * @param inClippingRect the new clipping rect, in local coordinates.
+ */
+void CWinRenderer::PushClippingRect(const QRect &inClippingRect)
+{
+ QRect clippingRect(inClippingRect);
+ clippingRect.translate(m_Translation);
+
+ const QRegion currentRegion = m_painter->clipRegion();
+ m_painter->setClipRect(clippingRect);
+
+ m_ClippingRegions.push_back(currentRegion);
+}
+
+//==============================================================================
+/**
+ * Pop the current clipping rect.
+ * This will change the clipping rect to use the one previous to the current
+ * one.
+ */
+void CWinRenderer::PopClippingRect()
+{
+ if (m_ClippingRegions.size() > 1) {
+ QRegion thePreviousRegion = m_ClippingRegions.back();
+ m_painter->setClipRegion(thePreviousRegion);
+ m_ClippingRegions.pop_back();
+ }
+}
+
+//==============================================================================
+/**
+ * DrawGradiantBitmap
+ *
+ * Draws a gradiant based on the begin color
+ *
+ * @param inRct Rct tof the control
+ * @param inBeginColor color to start with
+ * @param inInverted true if this is inverted
+ */
+void CWinRenderer::DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted,
+ double inScalingFactor)
+{
+ QRect rect(inRct);
+ QRect theClippingRect = GetClippingRect();
+ rect &= theClippingRect;
+ rect.translate(m_Translation);
+
+ long theHeight = rect.height();
+ long theWidth = rect.width();
+
+ QImage theCompatibleBitmap(1, theHeight, QImage::Format_RGB32);
+
+ int theR = inBeginColor.red();
+ int theG = inBeginColor.green();
+ int theB = inBeginColor.blue();
+
+ double theExtraRModifier = inScalingFactor * (1.0 - (theR / 255.0));
+ double theExtraGModifier = inScalingFactor * (1.0 - (theG / 255.0));
+ double theExtraBModifier = inScalingFactor * (1.0 - (theB / 255.0));
+
+ for (long thePixel = 0; thePixel < theHeight; ++thePixel) {
+ double theNormPixel = (double)thePixel / (theHeight * 4.8);
+ double theCos = 1.0 / (theNormPixel * theNormPixel + 1.0);
+ double theRValue = (double)theR * (theCos + theExtraRModifier);
+ double theGValue = (double)theG * (theCos + theExtraGModifier);
+ double theBValue = (double)theB * (theCos + theExtraBModifier);
+
+ QColor theTempColor(::dtoi(theRValue), ::dtoi(theGValue), ::dtoi(theBValue));
+ if (inInverted) {
+ theCompatibleBitmap.setPixelColor(0, qMax(0l, theHeight - thePixel - 2), theTempColor);
+ } else {
+ theCompatibleBitmap.setPixelColor(0, thePixel, theTempColor);
+ }
+ theExtraRModifier *= 0.3;
+ theExtraGModifier *= 0.3;
+ theExtraBModifier *= 0.3;
+ }
+
+ m_painter->save();
+ m_painter->drawImage(QRect(rect.x(), rect.y(), theWidth, theHeight), theCompatibleBitmap);
+ m_painter->restore();
+}
+
+//=============================================================================
+/**
+ * Scroll the selected area of the current view by the specified amount.
+ * This will take the rectangle represented by inVisibleSize and 'scroll' it
+ * by inAmount. The section that goes out of the visible size will be clipped
+ * off and the area entering the visible area will be empty.
+ * @param inAmount the amount to scroll by.
+ * @param inVisibleSize the rectangle to be scrolled.
+ * @return the smallest rect of area that needs to be redrawn.
+ */
+QRect CWinRenderer::Scroll(const QPoint &inAmount, const QRect &inVisibleSize)
+{
+#if KDAB_TEMPORARILY_REMOVED
+ inAmount.Offset(m_Translation);
+ inVisibleSize.Offset(m_Translation);
+
+ QRect theVisibleRect(QPoint(inVisibleSize.position.x, inVisibleSize.position.y),
+ QSize(inVisibleSize.size.x, inVisibleSize.size.y));
+
+ CRgn theDirtyRegion;
+ CRect theDirtyRect;
+ m_painter->ScrollDC(inAmount.x, inAmount.y, theVisibleRect, theVisibleRect, &theDirtyRegion,
+ &theDirtyRect);
+
+ QRect theDirtyRct(theDirtyRect.left, theDirtyRect.top, theDirtyRect.right - theDirtyRect.left,
+ theDirtyRect.bottom - theDirtyRect.top);
+
+ theDirtyRct.Offset(QPoint(-m_Translation.x, -m_Translation.y));
+
+ return theDirtyRct;
+#else
+ return {};
+#endif
+}
+
+//=============================================================================
+/**
+ * Draw a gradient over inRect.
+ * This will blend horizontally across the rect from inBeginColor into inEndColor.
+ * This does linear interpolation.
+ * @param inRect the rect to draw on.
+ * @param inBeginColor the start (left most) color.
+ * @param inEndColor the final (right most) color.
+ */
+void CWinRenderer::DrawGradient(const QRect &inRect, const QColor &inBeginColor, const QColor &inEndColor)
+{
+ const QRect rect = inRect.translated(m_Translation);
+ QLinearGradient gradient(rect.topLeft(), rect.topRight());
+ gradient.setColorAt(0, inBeginColor);
+ gradient.setColorAt(1, inEndColor);
+
+ QBrush brush(gradient);
+ m_painter->fillRect(rect, brush);
+}
+
+void CWinRenderer::FillHashed(const QRect &inRect, const QColor &inForegroundColor)
+{
+ m_painter->save();
+
+ QBrush theBrush(inForegroundColor, Qt::BDiagPattern);
+ QPen pen(inForegroundColor);
+ m_painter->setPen(pen);
+ m_painter->setBrush(theBrush);
+ m_painter->drawRect(inRect.translated(m_Translation));
+
+ m_painter->restore();
+}
+
+QPen CWinRenderer::GetPen(const QColor &inColor, int inWidth, Qt::PenStyle inStyle)
+{
+ QPen thePen;
+ SPenInfo theInfo = { inColor, inWidth, inStyle };
+ TPenMap::iterator thePos = m_Pens.find(theInfo);
+ if (thePos != m_Pens.end()) {
+ thePen = thePos->second;
+ } else {
+ thePen.setColor(inColor);
+ thePen.setWidth(inWidth);
+ m_Pens[theInfo] = thePen;
+ }
+ return thePen;
+}
+
+QPixmap CWinRenderer::pixmap() const
+{
+ return {};
+}
diff --git a/src/Authoring/Studio/_Win/Controls/WinRenderer.h b/src/Authoring/Studio/_Win/Controls/WinRenderer.h
new file mode 100644
index 00000000..4ebcc689
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/WinRenderer.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_WIN_RENDERER_H
+#define INCLUDED_WIN_RENDERER_H 1
+
+#pragma once
+
+#include <vector>
+#include <map>
+
+#include "Pt.h"
+#include "Rct.h"
+#include "Renderer.h"
+
+#include <QPainter>
+#include <QRegion>
+
+QT_BEGIN_NAMESPACE
+class QPixmap;
+QT_END_NAMESPACE
+
+class CWinRenderer : public CRenderer
+{
+ typedef std::vector<QPen> TPenList;
+ typedef std::vector<QRegion> TClippingRegions;
+
+protected:
+ CWinRenderer(); ///< Leaves CRenderer in an invalid state, only for subclasses
+ CWinRenderer(QPainter *inDC); ///< Leaves CRenderer in an invalid state, only for subclasses
+
+public:
+ CWinRenderer(QPainter *inDC, const QRect &inClippingRect);
+ virtual ~CWinRenderer();
+
+ void FillSolidRect(const QRect &inCoordinates, const QColor &inColor) override;
+
+ void MoveTo(const QPoint &inPoint) override;
+ void MoveTo(long inX, long inY) override;
+ void LineTo(const QPoint &inPoint) override;
+ void LineTo(long inX, long inY) override;
+
+ void PushPen(const QColor &inColor, int inWidth = 1) override;
+ void PopPen() override;
+ void BitBltFrom(const QRect &inRect, CRenderer *inRenderer, long inXSrc, long inYSrc) override;
+
+ void DrawText(float inX, float inY, const QString &inText) override;
+ void DrawText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingRect, const QColor &inColor = Qt::black) override;
+ void DrawBoldText(float inX, float inY, const QString &inText,
+ const QRect &inBoundingRect, const QColor &inColor = Qt::black) override;
+
+ QSize GetTextSize(const QString &inText) override;
+
+ void DrawBitmap(const QPoint &inPos, const QPixmap &inImage) override;
+ void Draw3dRect(const QRect &inRect, const QColor &inTopLeftColor,
+ const QColor &inBottomRightColor) override;
+ void DrawGradientBitmap(const QRect &inRct, const QColor &inBeginColor, bool inInverted,
+ double inScalingFactor = .99) override;
+
+ void DrawGradient(const QRect &inRect, const QColor &inBeginColor, const QColor &inEndColor) override;
+
+ void PushTranslation(const QPoint &inTranslation) override;
+ void PopTranslation() override;
+ QPoint GetTranslation() override;
+
+ QRect GetClippingRect() override;
+ void PushClippingRect(const QRect &inRect) override;
+ void PopClippingRect() override;
+ void PushAbsoluteClippingRect(const QRect &inRect) override {}
+ void FillHashed(const QRect &inRect, const QColor &inForeGroundColor) override;
+ QRect Scroll(const QPoint &inAmount, const QRect &inVisibleSize) override;
+ QPainter *GetPainter() override;
+ QPen GetPen(const QColor &inColor, int inWidth, Qt::PenStyle inStyle);
+
+ QPixmap pixmap() const override;
+
+protected:
+ TPenList m_PenList;
+ TClippingRegions m_ClippingRegions;
+ QPainter *m_painter;
+ QPoint m_currentPos;
+
+protected:
+ struct SPenInfo
+ {
+ QColor Color;
+ long Width;
+ Qt::PenStyle Style;
+ };
+
+ class CPenInfoLessThan : public std::binary_function<const SPenInfo &, const SPenInfo &, bool>
+ {
+ public:
+ inline bool operator()(const SPenInfo &inValL, const SPenInfo &inValR) const
+ {
+ return memcmp(&inValL, &inValR, sizeof(SPenInfo)) < 0;
+ }
+ };
+
+ typedef std::map<SPenInfo, QPen, CPenInfoLessThan> TPenMap;
+ TPenMap m_Pens;
+};
+#endif // INCLUDED_WIN_RENDERER_H
diff --git a/src/Authoring/Studio/_Win/Controls/WndControl.cpp b/src/Authoring/Studio/_Win/Controls/WndControl.cpp
new file mode 100644
index 00000000..c218f9b9
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/WndControl.cpp
@@ -0,0 +1,1006 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "WndControl.h"
+#include "WinRenderer.h"
+#include "Control.h"
+#include "MasterP.h"
+#include "AppFonts.h"
+#include "ContextMenu.h"
+#include "HotKeys.h"
+#include "StudioPreferences.h"
+#include "WinDnd.h"
+#include "Doc.h"
+#include "DropSource.h"
+#include "IDragable.h"
+#include "OffscreenRenderer.h"
+
+BEGIN_MESSAGE_MAP(CWndControl, CWnd)
+//{{AFX_MSG_MAP(CWndControl)
+ON_WM_KILLFOCUS()
+ON_WM_SETFOCUS()
+ON_WM_PAINT()
+ON_WM_LBUTTONUP()
+ON_WM_LBUTTONDOWN()
+ON_WM_RBUTTONDOWN()
+ON_WM_RBUTTONUP()
+ON_WM_LBUTTONDBLCLK()
+ON_WM_SIZING()
+ON_WM_ERASEBKGND()
+ON_WM_SIZE()
+ON_WM_MOUSEMOVE()
+ON_WM_CREATE()
+ON_WM_MOUSEACTIVATE()
+ON_WM_MOUSEWHEEL()
+ON_WM_CTLCOLOR()
+ON_CONTROL_REFLECT(EN_CHANGE, OnEnChange)
+//}}AFX_MSG_MAP
+ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
+ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
+END_MESSAGE_MAP()
+
+//=============================================================================
+/**
+ * Creates the pass thru class for the wnd control.
+ */
+CWndControlControlListener::CWndControlControlListener(CWndControl *inParent)
+{
+ m_Parent = inParent;
+}
+
+//=============================================================================
+/**
+ * Notification from the control that the window was invalidated.
+ */
+void CWndControlControlListener::OnControlInvalidated()
+{
+ m_Parent->OnControlInvalidated();
+}
+
+//=============================================================================
+/**
+ * Notification from the control to do a popup at the specified location.
+ */
+long CWndControlControlListener::DoPopup(CContextMenu *inContextMenu, CPt inPoint)
+{
+ return m_Parent->DoPopup(inContextMenu, inPoint);
+}
+
+//=============================================================================
+/**
+ * Get the location of the point in Screen coordinates.
+ */
+CPt CWndControlControlListener::ClientToScreen(CPt inPoint)
+{
+ CPoint thePoint(inPoint.x, inPoint.y);
+ m_Parent->ClientToScreen(&thePoint);
+
+ return CPt(thePoint.x, thePoint.y);
+}
+
+//=============================================================================
+/**
+ * Get the location of the point into client coordinates.
+ */
+CPt CWndControlControlListener::ScreenToClient(CPt inPoint)
+{
+ CPoint thePoint(inPoint.x, inPoint.y);
+ m_Parent->ScreenToClient(&thePoint);
+
+ return CPt(thePoint.x, thePoint.y);
+}
+
+//=============================================================================
+/**
+ * Get the platform dependent view that this is embedding.
+ * Used when platform dependent controls need to be embedded into the Controls.
+ */
+TPlatformView CWndControlControlListener::GetPlatformView()
+{
+ return m_Parent->m_hWnd;
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's SetIsDragging function.
+ * @param inIsDragging true to specify that a drag is occurring or false to cancel a drag
+ */
+void CWndControlControlListener::SetIsDragging(bool inIsDragging)
+{
+ m_Parent->SetIsDragging(inIsDragging);
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's ShowTooltips function.
+ * @param inLocation mid-point of the tooltip in global coordinates
+ * @param inText text to display as a tooltip
+ */
+void CWndControlControlListener::ShowTooltips(CPt inLocation, Q3DStudio::CString inText)
+{
+ m_Parent->ShowTooltips(inLocation, inText);
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's HideTooltips function.
+ */
+void CWndControlControlListener::HideTooltips()
+{
+ m_Parent->HideTooltips();
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's ShowMoveableTooltips function.
+ * @param inLocation mid-point of the tooltip in global coordinates
+ * @param inText text to display as a tooltip
+ */
+void CWndControlControlListener::ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText,
+ CRct inBoundingRct)
+{
+ m_Parent->ShowMoveableWindow(inLocation, inText, inBoundingRct);
+}
+
+//=============================================================================
+/**
+ * Pass-thru to the CWndControl's HideMoveableTooltips function.
+ */
+void CWndControlControlListener::HideMoveableWindow()
+{
+ m_Parent->HideMoveableWindow();
+}
+
+void CWndControlControlListener::DoStartDrag(IDragable *inDragable)
+{
+ m_Parent->DoStartDrag(inDragable);
+}
+
+//===============================================================================
+/**
+* performs a drag operation on a file
+*/
+void CWndControlControlListener::DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList)
+{
+ m_Parent->DoStartDrag(inDragFileNameList);
+}
+
+IMPLEMENT_OBJECT_COUNTER(CWndControl);
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CWndControl::CWndControl(CControl *inControl)
+ : m_MouseOver(false)
+ , m_MemDC(nullptr)
+ , m_IsDragging(false)
+ , m_ControlListener(this)
+ , m_IsMouseDown(false)
+{
+ ADDTO_OBJECT_COUNTER(CWndControl);
+
+ m_Control = inControl;
+
+ if (m_Control)
+ m_Control->SetWindowListener(&m_ControlListener);
+
+ RegisterWindowClass();
+
+ // Create the tooltip for this window (there's only one per view)
+ m_Tooltip.CreateEx(WS_EX_TOOLWINDOW, AfxRegisterWndClass(CS_SAVEBITS), L"CPopupWndTooltip",
+ WS_POPUP | WS_BORDER, CRect(0, 0, 20, 10), this, 0);
+ m_Tooltip.SetStationary(true);
+
+ // Create the tooltip for this window (there's only one per view)
+ m_MoveableTooltip.CreateEx(WS_EX_TOOLWINDOW, AfxRegisterWndClass(CS_SAVEBITS),
+ L"CPopupWndTooltip", WS_POPUP | WS_BORDER, CRect(0, 0, 20, 10), this,
+ 0);
+ m_MoveableTooltip.SetStationary(false);
+
+ m_Brush.CreateSolidBrush(CStudioPreferences::GetBaseColor());
+
+#ifdef _DEBUG
+ m_IsPainting = false;
+#endif
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CWndControl::~CWndControl()
+{
+ DeleteBuffers();
+
+ REMOVEFROM_OBJECT_COUNTER(CWndControl);
+}
+
+// Register the window class if it has not already been registered.
+BOOL CWndControl::RegisterWindowClass()
+{
+ WNDCLASS wndcls;
+ HINSTANCE hInst = AfxGetInstanceHandle();
+
+ if (!(::GetClassInfo(hInst, WNDCONTROL_CLASSNAME, &wndcls))) {
+ // otherwise we need to register a new class
+ wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+ wndcls.lpfnWndProc = ::DefWindowProc;
+ wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
+ wndcls.hInstance = hInst;
+ wndcls.hIcon = nullptr;
+ wndcls.hCursor = nullptr;
+ wndcls.hbrBackground = m_Brush;
+ wndcls.lpszMenuName = nullptr;
+ wndcls.lpszClassName = WNDCONTROL_CLASSNAME;
+
+ if (!AfxRegisterClass(&wndcls)) {
+ AfxThrowResourceException();
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * Create: Handles the WM_CREATE message.
+ *
+ * Creates the view, sets the font, and loads the toolbar.
+ *
+ * @param lpszClassName Names the Windows class. The class name can be any name
+ *registered with the AfxRegisterWndClass
+ * @param lpszWindowName Represents the window name. Used as text for the title bar.
+ * @param dwStyle Specifies the window style attributes.
+ * @param rect Specifies the size and position of the window.
+ * @param pParentWnd Pointer to the parent of the view.
+ * @param nID The ID for the view.
+ * @param pContext Create context for the view.
+ *
+ * @return TRUE if creation was successful, otherwise FALSE.
+ */
+//==============================================================================
+int CWndControl::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+ return CWnd::OnCreate(lpCreateStruct);
+}
+
+//=============================================================================
+/**
+ * MFC notification that this window needs to be redrawn.
+ * This buffers the draw DC, makes a Renderer and has the Control draw to the
+ * buffered DC.
+ */
+void CWndControl::OnPaint()
+{
+#ifdef _DEBUG
+ m_IsPainting = true;
+#endif
+
+ UICPROFILE(OnPaint);
+
+ CPaintDC dc(this);
+
+ HDC theDeviceContext = dc.GetSafeHdc();
+
+ RECT theSize;
+ this->GetClientRect(&theSize);
+
+ bool theRedrawAllFlag = false;
+
+ if (m_MemDC == nullptr) {
+ m_MemDC = new CDC();
+ m_MemDC->CreateCompatibleDC(&dc);
+ m_MemDC->SelectObject(CAppFonts::GetInstance()->GetNormalFont());
+
+ m_MemBitmap = ::CreateCompatibleBitmap(theDeviceContext, theSize.right, theSize.bottom);
+
+ m_OldBitmap = (HBITMAP)m_MemDC->SelectObject(m_MemBitmap);
+
+ theRedrawAllFlag = true;
+ }
+
+ CRct theDirtyRect;
+ {
+ UICPROFILE(OnPaint_Render);
+ CWinRenderer theRenderer(m_MemDC, CRct(0, 0, theSize.right, theSize.bottom));
+ if (m_Control)
+ m_Control->OnDraw(&theRenderer, theDirtyRect, theRedrawAllFlag);
+ }
+
+ // Copy (BitBlt) bitmap from bitmap memory DC to memory DC
+ // ::BitBlt( theDeviceContext, theDirtyRect.position.x, theDirtyRect.position.y,
+ //theDirtyRect.size.x, theDirtyRect.size.y, m_MemDC->GetSafeHdc( ), theDirtyRect.position.x,
+ //theDirtyRect.position.y, SRCCOPY );
+ ::BitBlt(theDeviceContext, 0, 0, theSize.right, theSize.bottom, m_MemDC->GetSafeHdc(), 0, 0,
+ SRCCOPY);
+
+#ifdef _DEBUG
+ m_IsPainting = false;
+#endif
+}
+
+void CWndControl::DeleteBuffers()
+{
+ if (m_MemDC != nullptr) {
+ m_MemDC->SelectObject(m_OldBitmap);
+ ::DeleteObject(m_MemBitmap);
+ m_MemDC->DeleteDC();
+ delete m_MemDC;
+ m_MemDC = nullptr;
+ }
+}
+
+//=============================================================================
+/**
+ * Notification to this window that it has lost focus.
+ * @param pNewWnd the window that has gained focus.
+ */
+void CWndControl::OnKillFocus(CWnd *pNewWnd)
+{
+ CWnd::OnKillFocus(pNewWnd);
+
+ // This is used for special cases where we lose control of the mouse and lose focus as well.
+ // It makes sure we are not still receiving mouse messages.
+ if (m_IsMouseDown) {
+ // Tell the control that it got a mouse up
+ if (m_Control)
+ m_Control->OnMouseUp(CPt(-1, -1), CHotKeys::GetCurrentKeyModifiers());
+ ReleaseCapture();
+ }
+
+ if (m_Control) {
+ // For the case where pNewWnd is an "embedded" control in m_Control (eg.
+ // CPlatformEditControl)
+ // m_Control should not lose focus.
+ // Note that since most of the Studio controls uses a common PlatformDevice (the Studio
+ // window),
+ // this (OnKillFocus) only occurs when 1) switching to another app, 2) popping up a dialog
+ // box or 3) an embedded control
+ // takes focus.
+ // This takes care of the case when an embedded control takes focus but unwittingly causes
+ // its parent to lose focus.
+ // This can have dire consequences if the parent control were to do something on losing
+ // focus, such as
+ // destroying all its children. For example, Bug3421.
+ if (!m_Control->IsChildPlatformDevice(pNewWnd->GetSafeHwnd()))
+ m_Control->OnLoseFocus();
+ }
+}
+
+void CWndControl::OnSetFocus(CWnd *pPrevWnd)
+{
+ CWnd::OnSetFocus(pPrevWnd);
+ if (m_Control)
+ m_Control->OnGainFocus();
+}
+
+//=============================================================================
+/**
+ * Notification of mouse up.
+ */
+void CWndControl::OnLButtonUp(UINT nFlags, CPoint inPoint)
+{
+ Q_UNUSED(nFlags);
+ m_IsMouseDown = false;
+ // Tell the control that it got a mouse up
+ if (m_Control)
+ m_Control->OnMouseUp(CPt(inPoint.x, inPoint.y), CHotKeys::GetCurrentKeyModifiers());
+
+ ReleaseCapture();
+
+ CWnd::OnLButtonUp(nFlags, inPoint);
+}
+
+//=============================================================================
+/**
+ * Notification of a mouse click.
+ */
+void CWndControl::OnLButtonDown(UINT nFlags, CPoint inPoint)
+{
+ Q_UNUSED(nFlags);
+ m_IsMouseDown = true;
+ SetCapture();
+ // Tell the control that it got a mouse down.
+ if (m_Control)
+ m_Control->OnMouseDown(CPt(inPoint.x, inPoint.y), CHotKeys::GetCurrentKeyModifiers());
+
+ CWnd::OnLButtonDown(nFlags, inPoint);
+}
+
+//=============================================================================
+/**
+ * Notification of a mouse click.
+ */
+void CWndControl::OnRButtonDown(UINT nFlags, CPoint inPoint)
+{
+ SetCapture();
+
+ if (m_IsMouseDown) {
+ // Tell the control that it got a mouse up
+ if (m_Control)
+ m_Control->OnMouseUp(CPt(inPoint.x, inPoint.y), CHotKeys::GetCurrentKeyModifiers());
+ m_IsMouseDown = false;
+ }
+
+ // Tell the control that it got a mouse down.
+ if (m_Control)
+ m_Control->OnMouseRDown(CPt(inPoint.x, inPoint.y), CHotKeys::GetCurrentKeyModifiers());
+
+ CWnd::OnRButtonDown(nFlags, inPoint);
+}
+
+//=============================================================================
+/**
+ * Notification of mouse up.
+ */
+void CWndControl::OnRButtonUp(UINT nFlags, CPoint inPoint)
+{
+ Q_UNUSED(nFlags);
+
+ // Tell the control that it got a mouse up
+ if (m_Control)
+ m_Control->OnMouseRUp(CPt(inPoint.x, inPoint.y), CHotKeys::GetCurrentKeyModifiers());
+
+ ReleaseCapture();
+}
+
+long CWndControl::DoPopup(CContextMenu *inMenu, CPt inPoint)
+{
+ CPoint thePoint(inPoint.x, inPoint.y);
+ ClientToScreen(&thePoint);
+
+ long theSelectedItem = inMenu->DoPopup(CPt(thePoint.x, thePoint.y), m_hWnd);
+ if (m_IsMouseDown) {
+ m_IsMouseDown = false;
+ if (m_Control)
+ m_Control->OnMouseUp(CPt(-1, -1), 0);
+ }
+ ReleaseCapture();
+
+ return theSelectedItem;
+}
+
+//=============================================================================
+/**
+ * Notification of a mouse double click.
+ */
+void CWndControl::OnLButtonDblClk(UINT nFlags, CPoint inPoint)
+{
+ Q_UNUSED(nFlags);
+
+ // Tell the control that it got a mouse down.
+ if (m_Control)
+ m_Control->OnMouseDoubleClick(CPt(inPoint.x, inPoint.y), nFlags);
+}
+
+//=============================================================================
+/**
+ * Notification of a key down message.
+ */
+bool CWndControl::OnKeyDown(UINT inChar, UINT, UINT)
+{
+ return m_Control->OnKeyDown(inChar, CHotKeys::GetCurrentKeyModifiers());
+}
+
+//=============================================================================
+/**
+ * Notification of a key up messages. May be hooked up later.
+ */
+bool CWndControl::OnKeyUp(UINT inChar, UINT, UINT)
+{
+ return m_Control->OnKeyUp(inChar, CHotKeys::GetCurrentKeyModifiers());
+}
+
+//=============================================================================
+/**
+ * Notification of a character press message.
+ *
+ * @return true if the character was processed by the control, otherwise false
+ */
+bool CWndControl::OnChar(UINT inChar, UINT, UINT)
+{
+ return m_Control->OnChar(inChar, CHotKeys::GetCurrentKeyModifiers());
+}
+
+//=============================================================================
+/**
+ * Displays a tooltip at the desired location, and with the desired text.
+ * @param inLocation mid-point of the tooltext message box to be displayed
+ * @param inText text to be displayed as the tooltip
+ */
+void CWndControl::ShowTooltips(CPt inLocation, Q3DStudio::CString inText)
+{
+ // If the global preference for tooltips is enabled
+ if (CStudioPreferences::ShouldShowTooltips()) {
+ // If the tooltip text is not empty, show the tooltip
+ if (!inText.IsEmpty()) {
+ // So that tooltip text renders within the window
+ // this would be nicely taken care of if this were a MFC control...
+ COffscreenRenderer theOffscreenRenderer(CRct(0, 0, 1, 1));
+ float theWidth(0);
+ float theHeight(0);
+ theOffscreenRenderer.GetTextSize(inText, theWidth, theHeight);
+
+ long theRightMargin = m_Control->GetPosition().x + m_Control->GetSize().x;
+ if (inLocation.x + ::dtol(theWidth) > theRightMargin) // re-centers this
+ inLocation.x = theRightMargin - ::dtol(theWidth) / 2;
+
+ // Convert the point to screen coordinates
+ ::CPoint theScreenCoords = ::CPoint(inLocation.x, inLocation.y);
+ ClientToScreen(&theScreenCoords);
+ m_Tooltip.UpdateToolTip(theScreenCoords, ::CString(inText.GetMulti()), true);
+ }
+ // If the tooltip text is empty, hide the tooltip
+ else
+ m_Tooltip.ShowWindow(SW_HIDE);
+ }
+}
+
+//=============================================================================
+/**
+ * Hides any tooltip currently being displayed.
+ */
+void CWndControl::HideTooltips()
+{
+ m_Tooltip.ShowWindow(SW_HIDE);
+}
+
+//=============================================================================
+/**
+ * Starts the Dragging process.
+ * On the given IDragable.
+ * @param inAsset the Asset to be drug.
+ */
+void CWndControl::DoStartDrag(IDragable *inDragable)
+{
+ // Tell the window that we are beginning a drag
+ if (inDragable && !m_IsDragging) {
+ // This should prevent the Container from dragging while it is already dragging.
+ SetIsDragging(true);
+
+ // This is a Wrapper for the OLEDROP
+ CWinDragManager theDragManager;
+
+ // Theoretically: there is no limit to the amount of data you can add.
+ // For now only one object will work, until i figure out how to drag multiple objects in
+ // windows.
+ Q3DStudio::CAutoMemPtr<CDropSource> theDropSource =
+ CDropSourceFactory::Create(inDragable->GetFlavor(), inDragable);
+
+ // Add the UIC_GESTURE_FLAVOR. This will allow us to drag to StudioControls.
+ theDragManager.AddData(inDragable->GetFlavor(), &theDropSource, sizeof(CDropSource *));
+ // This is a blocking call when we get done with this call we should have no more data in
+ // our Drag list.
+ theDragManager.StartDrag();
+
+ // Tell the window that we are done dragging
+ SetIsDragging(false);
+ }
+}
+
+//===============================================================================
+/**
+* performs a drag operation on a file
+*/
+void CWndControl::DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList)
+{
+ // Tell the window that we are beginning a drag
+ if (!m_IsDragging) {
+ SetIsDragging(true);
+
+ try {
+ CWinDragManager theDragManager;
+
+ std::vector<Q3DStudio::CString>::iterator thePos = inDragFileNameList.begin();
+ std::vector<Q3DStudio::CString>::iterator theEndPos = inDragFileNameList.end();
+
+ Q3DStudio::CAutoMemPtr<CDropSource> theDropSource;
+ Q3DStudio::CAutoMemPtr<CUICFile> theDragFile;
+ for (; thePos != theEndPos; ++thePos) {
+ Q3DStudio::CString theDragFileName = *thePos;
+ if (theDragFileName.Length() > 0) {
+ theDragFile = new CUICFile(theDragFileName);
+ theDropSource = CDropSourceFactory::Create(
+ EUIC_FLAVOR_ASSET_UICFILE, (void *)theDragFile, sizeof(theDragFile));
+ // Add the UIC_GESTURE_FLAVOR. This will allow us to drag to StudioControls.
+ theDragManager.AddData(EUIC_FLAVOR_ASSET_UICFILE, &theDropSource,
+ sizeof(CDropSource *));
+ break;
+ }
+ }
+ theDragManager.StartDrag();
+ } catch (...) { // if there are any errors that throws an exception, there
+ // there will be no more drag and drop, since the flag will not be reset.
+ }
+ // Tell the window that we are done dragging
+ SetIsDragging(false);
+ }
+}
+
+//=============================================================================
+/**
+ * Notification of a mouse wheel movement.
+ */
+BOOL CWndControl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
+{
+ ScreenToClient(&pt);
+ if (m_Control)
+ m_Control->OnMouseWheel(CPt(pt.x, pt.y), zDelta, CHotKeys::GetCurrentKeyModifiers());
+
+ //Q_UNUSED( nFlags );
+ //Q_UNUSED( zDelta );
+ //Q_UNUSED( pt );
+ return CWnd::OnMouseWheel(nFlags, zDelta, pt);
+}
+
+LRESULT CWndControl::OnRegisteredMouseWheel(WPARAM, LPARAM)
+{
+ return 0;
+}
+
+//=============================================================================
+/**
+ * Notification that this window is being resized.
+ */
+void CWndControl::OnSizing(UINT nSide, LPRECT lpRect)
+{
+ Q_UNUSED(nSide);
+ Q_UNUSED(lpRect);
+}
+
+//=============================================================================
+/**
+ * Override of erase to prevent flashing of the background.
+ */
+BOOL CWndControl::OnEraseBkgnd(CDC *pDC)
+{
+ Q_UNUSED(pDC);
+ return FALSE;
+}
+
+//=============================================================================
+/**
+ * Notification that this window has been resized.
+ */
+void CWndControl::OnSize(UINT nType, int cx, int cy)
+{
+ CWnd::OnSize(nType, cx, cy);
+
+ MoveWindow(0, 0, cx, cy);
+
+ CRect theSize;
+ GetClientRect(&theSize);
+
+ if (m_Control)
+ m_Control->SetSize(CPt(theSize.right, theSize.bottom));
+
+ // Gotta recreate the buffered objects, otherwise they will be the wrong dimensions
+ DeleteBuffers();
+}
+
+//=============================================================================
+/**
+ * Event notification that the mouse has moved.
+ */
+void CWndControl::OnMouseMove(UINT, CPoint point)
+{
+#ifdef _DEBUG
+ m_IsPainting = true;
+#endif
+
+ UICPROFILE(OnMouseMove);
+
+ // Track the mouse while it's in here, this will give us the hover msg
+ m_MouseOver = true;
+
+ TRACKMOUSEEVENT theEventStruct;
+ theEventStruct.cbSize = sizeof(TRACKMOUSEEVENT);
+ theEventStruct.dwFlags = TME_HOVER | TME_LEAVE;
+ theEventStruct.hwndTrack = GetSafeHwnd();
+ theEventStruct.dwHoverTime = HOVER_DEFAULT;
+
+ ::TrackMouseEvent(&theEventStruct);
+
+ // Notify the control that the mouse moved
+ if (m_Control) {
+ m_Control->OnMouseMove(CPt(point.x, point.y), CHotKeys::GetCurrentKeyModifiers());
+
+ // If the control invalidated because of a mouse event then we want to do an immediate
+ // redraw.
+ // this ensures consistent visible feedback.
+ if (m_Control->IsChildInvalidated()) {
+ UICPROFILE(RedrawWindow);
+ RedrawWindow(nullptr, nullptr, RDW_UPDATENOW);
+ }
+ }
+
+#ifdef _DEBUG
+ m_IsPainting = false;
+#endif
+}
+
+//=============================================================================
+/**
+ * Notification that the mouse has left this window.
+ */
+LRESULT CWndControl::OnMouseLeave(WPARAM inwParam, LPARAM inlParam)
+{
+#ifdef _DEBUG
+ if (!m_IsPainting) {
+#endif _DEBUG
+
+ Q_UNUSED(inwParam);
+ Q_UNUSED(inlParam);
+
+ CPoint thePoint;
+
+ ::GetCursorPos(&thePoint);
+
+ // This will tell me what window is under this point.
+ HWND theWindow = ::WindowFromPoint(thePoint);
+
+ if (theWindow != m_hWnd) {
+ m_MouseOver = false;
+ if (m_Control)
+ m_Control->OnMouseMove(CPt(-1, -1), 0);
+ }
+
+#ifdef _DEBUG
+ }
+#endif
+
+ return 0;
+}
+
+//=============================================================================
+/**
+ * Override to provide the hover message to the control.
+ */
+LRESULT CWndControl::OnMouseHover(WPARAM inwParam, LPARAM inlParam)
+{
+
+ long theX = inlParam & 0xFFFF;
+ long theY = (inlParam >> 16) & 0xFFFF;
+
+ if (m_Control)
+ m_Control->OnMouseHover(CPt(theX, theY), (UINT)inwParam);
+
+ TRACKMOUSEEVENT theEventStruct;
+ theEventStruct.cbSize = sizeof(TRACKMOUSEEVENT);
+ theEventStruct.dwFlags = TME_HOVER | TME_LEAVE;
+ theEventStruct.hwndTrack = GetSafeHwnd();
+ theEventStruct.dwHoverTime = HOVER_DEFAULT;
+
+ ::TrackMouseEvent(&theEventStruct);
+
+ return 0;
+}
+
+//=============================================================================
+/**
+ * Callback from the control that it has been invalidated.
+ * This will post a message for a redraw.
+ */
+void CWndControl::OnControlInvalidated()
+{
+ if (::IsWindow(m_hWnd)) {
+ Invalidate();
+ ::PostMessage(m_hWnd, WM_PAINT, 0, 0);
+ }
+}
+
+//=============================================================================
+/**
+ * Override of PreTranslateMessage for dragging implementation.
+ */
+BOOL CWndControl::PreTranslateMessage(MSG *inMessage)
+{
+ BOOL theReturn = FALSE;
+
+ switch (inMessage->message) {
+ case WM_KEYDOWN: {
+ theReturn =
+ OnKeyDown((UINT)inMessage->wParam, inMessage->lParam & 0xFFFF, (UINT)inMessage->lParam);
+
+ BYTE theKeyboardState[256];
+ ::GetKeyboardState(theKeyboardState);
+
+ theKeyboardState[VK_CONTROL] = 0;
+
+ WORD theChar = 0;
+ // Get the ascii character for the VK key.
+ ::ToAscii((UINT)inMessage->wParam, (inMessage->lParam >> 16) & 0xFF, theKeyboardState,
+ &theChar, 0);
+ unsigned int theCharacterCode = (char)theChar;
+
+ if (theCharacterCode != 0) {
+ theReturn = theReturn
+ || OnChar(theCharacterCode, inMessage->lParam & 0xFFFF, (UINT)inMessage->lParam);
+ }
+ } break;
+
+ case WM_KEYUP:
+ // Do the OnKeyUp
+ theReturn =
+ OnKeyUp((UINT)inMessage->wParam, inMessage->lParam & 0xFFFF, (UINT)inMessage->lParam);
+ break;
+
+ case WM_CHAR:
+ theReturn = OnChar((UINT)inMessage->wParam, 0, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return theReturn;
+}
+
+//=============================================================================
+/**
+ * Call this function to indicate that a drag operation is currently taking
+ * place or has just finished. If the drag operation just finished, a mouse up
+ * event will be sent to all children so that the one who initiated the drag
+ * can clean things up.
+ * @param inIsDragging true to specify that a drag is occurring or false if a drag ended
+ */
+void CWndControl::SetIsDragging(bool inIsDragging)
+{
+ m_IsDragging = inIsDragging;
+
+ // If we are no longer dragging, notify the children that a mouse up occurred since OLE won't
+ // tell us that
+ if (!m_IsDragging)
+ m_Control->OnMouseUp(CPt(-1, -1), 0);
+}
+
+//=============================================================================
+/**
+ * Override of MouseActivate to set the focus on this.
+ */
+int CWndControl::OnMouseActivate(CWnd *pDesktopWnd, UINT nHitTest, UINT message)
+{
+ CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
+ SetFocus();
+ m_Control->OnGainFocus();
+ return MA_ACTIVATE;
+}
+
+bool CWndControl::OnDragWithin(CDropSource &inSource)
+{
+ bool theReturn = false;
+ CPt thePoint = inSource.GetCurrentPoint();
+ long theFlags = inSource.GetCurrentFlags();
+ CDropTarget *theDropTarget = m_Control->FindDropCandidate(thePoint, theFlags);
+
+ if (theDropTarget) {
+ theReturn = theDropTarget->Accept(inSource);
+ delete theDropTarget;
+ }
+ return theReturn;
+}
+
+bool CWndControl::OnDragReceive(CDropSource &inSource)
+{
+ bool theReturn = false;
+ CPt thePoint = inSource.GetCurrentPoint();
+ long theFlags = inSource.GetCurrentFlags();
+
+ CDropTarget *theDropTarget = m_Control->FindDropCandidate(thePoint, theFlags);
+
+ if (theDropTarget) {
+ theReturn = theDropTarget->Drop(inSource);
+ delete theDropTarget;
+ }
+ return theReturn;
+}
+
+void CWndControl::OnDragLeave()
+{
+ m_MouseOver = false;
+ m_Control->OnMouseMove(CPt(-1, -1), 0);
+}
+
+void CWndControl::OnReflectMouse(CPt &inPoint, long inFlags)
+{
+ // Notify the control that the mouse moved
+ m_Control->OnMouseMove(inPoint, inFlags /*CHotKeys::GetCurrentKeyModifiers( )*/);
+
+ // If the control invalidated because of a mouse event then we want to do an immediate redraw.
+ // this ensures consistent visible feedback.
+ if (m_Control->IsChildInvalidated()) {
+ UICPROFILE(RedrawWindow);
+ RedrawWindow(nullptr, nullptr, RDW_UPDATENOW);
+ }
+}
+
+void CWndControl::ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct)
+{
+ // If the global preference for tooltips is enabled
+ if (CStudioPreferences::ShouldShowTooltips()) {
+ // This flag is set when the control is created, it tells itself that things should
+ // not move around outside its boundaries.
+ if (inLocation.x < inBoundingRct.position.x)
+ inLocation.x = inBoundingRct.position.x;
+ if (inLocation.y < inBoundingRct.position.y)
+ inLocation.y = inBoundingRct.position.y;
+ if (inLocation.x > (inBoundingRct.position.x + inBoundingRct.size.x))
+ inLocation.x = inBoundingRct.position.x + inBoundingRct.size.x;
+ if (inLocation.y > (inBoundingRct.position.y + inBoundingRct.size.y))
+ inLocation.y = inBoundingRct.position.y + inBoundingRct.size.y;
+
+ // Convert the point to screen coordinates
+ ::CPoint theScreenCoords = ::CPoint(inLocation.x, inLocation.y);
+ ClientToScreen(&theScreenCoords);
+ // If the tooltip text is not empty, show the tooltip
+ if (!inText.IsEmpty())
+ m_MoveableTooltip.UpdateToolTip(theScreenCoords, ::CString(inText.GetMulti()), true);
+ // If the tooltip text is empty, hide the tooltip
+ else
+ m_MoveableTooltip.ShowWindow(SW_HIDE);
+ }
+}
+
+void CWndControl::HideMoveableWindow()
+{
+ m_MoveableTooltip.ShowWindow(SW_HIDE);
+}
+
+//=============================================================================
+/**
+ * Override of OnCtlColor( ) to provide a callback when the dialog is closed.
+ */
+HBRUSH CWndControl::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)
+{
+ // Call the base class implementation first! Otherwise, it may undo what we are trying to
+ // accomplish here.
+ HBRUSH hbr = CWnd::OnCtlColor(pDC, pWnd, nCtlColor);
+
+ // Set the text color to red.
+ // pDC->SetTextColor(RGB(255, 0, 0));
+
+ // pDC->SetBkColor( CStudioPreferences::GetBaseColor( ) );
+
+ // Return handle to our CBrush object.
+ // hbr = m_Brush;
+
+ return hbr;
+}
+
+//=============================================================================
+/**
+ * Override of EN_CHANGE
+ */
+void CWndControl::OnEnChange()
+{
+}
diff --git a/src/Authoring/Studio/_Win/Controls/WndControl.h b/src/Authoring/Studio/_Win/Controls/WndControl.h
new file mode 100644
index 00000000..a7e3c72a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Controls/WndControl.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_WND_CONTROL_H
+#define INCLUDED_WND_CONTROL_H 1
+
+#pragma once
+
+#include "Control.h"
+#include "PopupWnd.h"
+#include "DropContainer.h"
+
+#define WNDCONTROL_CLASSNAME _T("WNDCONTROL")
+
+class CWndControl;
+class IDragable;
+
+class CWndControlControlListener : public CControlWindowListener
+{
+public:
+ CWndControlControlListener(CWndControl *inParent);
+ virtual void OnControlInvalidated();
+ virtual long DoPopup(CContextMenu *inMenu, CPt inLocation);
+ virtual CPt ClientToScreen(CPt inPoint);
+ virtual CPt ScreenToClient(CPt inPoint);
+ virtual TPlatformView GetPlatformView();
+ virtual void SetIsDragging(bool inIsDragging);
+ virtual void ShowTooltips(CPt inLocation, Q3DStudio::CString inText);
+ virtual void HideTooltips();
+ virtual void DoStartDrag(IDragable *inDragable);
+ virtual void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList);
+ virtual void ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct);
+ virtual void HideMoveableWindow();
+
+protected:
+ CWndControl *m_Parent;
+};
+
+class CWndControl : public CWnd, public CWinDropContainer
+{
+
+public:
+ CWndControl(CControl *inAgent);
+ virtual ~CWndControl();
+
+ DEFINE_OBJECT_COUNTER(CWndControl)
+
+ BOOL RegisterWindowClass();
+
+ virtual void OnControlInvalidated();
+ long DoPopup(CContextMenu *inMenu, CPt inLocation);
+ void SetIsDragging(bool inIsDragging);
+ void DeleteBuffers();
+ bool OnKeyDown(UINT inChar, UINT inRepCnt, UINT inFlags);
+ bool OnKeyUp(UINT inChar, UINT inRepCnt, UINT inFlags);
+ bool OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
+ void ShowTooltips(CPt inLocation, Q3DStudio::CString inText);
+ void HideTooltips();
+ void DoStartDrag(IDragable *inDragable);
+ void DoStartDrag(std::vector<Q3DStudio::CString> &inDragFileNameList);
+ virtual void ShowMoveableWindow(CPt inLocation, Q3DStudio::CString inText, CRct inBoundingRct);
+ virtual void HideMoveableWindow();
+
+ bool OnDragWithin(CDropSource &inSource);
+ bool OnDragReceive(CDropSource &inSource);
+ void OnDragLeave();
+ void OnReflectMouse(CPt &inPoint, long inFlags);
+
+ //{{AFX_MSG(CWndControl)
+ afx_msg void OnKillFocus(CWnd *pNewWnd);
+ afx_msg void OnSetFocus(CWnd *pOldWnd);
+ afx_msg void OnPaint();
+ afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
+ afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
+ afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
+ afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
+ afx_msg void OnSizing(UINT nFlags, LPRECT pRect);
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+ afx_msg LRESULT OnRegisteredMouseWheel(WPARAM wParam, LPARAM lParam);
+ afx_msg int OnMouseActivate(CWnd *pDesktopWnd, UINT nHitTest, UINT message);
+ afx_msg HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor);
+ afx_msg void OnEnChange();
+ //}}AFX_MSG
+
+ afx_msg LRESULT OnMouseLeave(WPARAM inwParam, LPARAM inlParam);
+ afx_msg LRESULT OnMouseHover(WPARAM inwParam, LPARAM inlParam);
+ DECLARE_MESSAGE_MAP()
+protected:
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CLibraryView)
+public:
+protected:
+ virtual BOOL PreTranslateMessage(MSG *pMsg);
+ //}}AFX_VIRTUAL
+
+ CControl *m_Control;
+ CControl *m_Focus;
+ CPopupWnd m_Tooltip;
+ CPopupWnd m_MoveableTooltip;
+ CBrush m_Brush;
+ bool m_MouseOver;
+ CDC *m_MemDC;
+ HBITMAP m_MemBitmap;
+ HBITMAP m_OldBitmap;
+ bool m_IsMouseDown; /// This is used for a hack to make controls get a mouse up when we lose
+ /// focuse and
+/// the mouse is down... say we try and delete a layer while dragging and it is the last layer
+
+#ifdef _DEBUG
+ bool m_IsPainting;
+#endif
+ bool m_IsDragging;
+ CWndControlControlListener m_ControlListener;
+};
+
+#endif // INCLUDED_WND_CONTROL_H
diff --git a/src/Authoring/Studio/_Win/DragNDrop/DropProxy.cpp b/src/Authoring/Studio/_Win/DragNDrop/DropProxy.cpp
new file mode 100644
index 00000000..ac835c00
--- /dev/null
+++ b/src/Authoring/Studio/_Win/DragNDrop/DropProxy.cpp
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "HotKeys.h"
+#include "DropProxy.h"
+#include "DropContainer.h"
+#include "DropSource.h"
+#include "UICFile.h"
+#include "UICString.h"
+#include "IDragable.h"
+
+#include <QDragEnterEvent>
+#include <QWidget>
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inParent gesture listener that messages will be passed to
+ */
+CDropProxy::CDropProxy(CDropContainer *inParent)
+{
+ m_Parent = inParent;
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CDropProxy::~CDropProxy()
+{
+}
+
+//=============================================================================
+/**
+ * Called when a drag begins over a window.
+ */
+void CDropProxy::dragEnterEvent(QDragEnterEvent *event)
+{
+ event->ignore();
+ if (GetDragItemCount(event->mimeData())) {
+ CDropContainer::TFlavorItr theItr = m_Parent->GetFlavorBegin();
+ CDropContainer::TFlavorItr theEnd = m_Parent->GetFlavorEnd();
+
+ for (; theItr != theEnd; ++theItr) {
+ if (HasMainFlavor(event->mimeData(), *theItr)) {
+ event->accept();
+ event->setDropAction(Qt::CopyAction);
+ break;
+ }
+ }
+ }
+}
+
+//===============================================================================
+/**
+ * This will extract the Data from the OLEDropObject and convert it to a CDropSource.
+ * @param inDataObject the Win specific dropSource.
+ * @param inFlavor the Flavor we need.
+ * @param inItem the position of the data in the DataObject.
+ * @return Newly extracted or created DropSource.
+ * Note: if we created it we need to make sure we destroy this object.
+ */
+CDropSource *CDropProxy::GetDropSource(const QMimeData *inDataObject, long inFlavor, long inItem)
+{
+ const CDropSource *theDropSource = nullptr;
+ switch (inFlavor) {
+ case EUIC_FLAVOR_FILE: {
+ // Check if we have a Drop File on our hands.
+ if (inDataObject->hasUrls()) {
+ // Get the number of files being dragged
+ short theFileCount = inDataObject->urls().count();
+
+ // Only allow single files to be dragged
+ if (theFileCount > inItem && !inDataObject->urls().isEmpty()) {
+ // Get the filename of the object being dragged
+ QString theFilename = inDataObject->urls().at(inItem).toLocalFile();
+
+ CUICFile theFile(Q3DStudio::CString::fromQString(theFilename));
+ // Dragging a single file
+ theDropSource =
+ CDropSourceFactory::Create(EUIC_FLAVOR_FILE, &theFile, sizeof(CUICFile *));
+ }
+ }
+ } break;
+ case EUIC_FLAVOR_TEXT:
+ // Don't do anythiing for this
+ case EUIC_FLAVOR_LISTBOX:
+ // Don't do anythiing for this
+ case EUIC_FLAVOR_BASIC_OBJECTS:
+ // Don't do anythiing for this
+ case EUIC_FLAVOR_ASSET_LIB:
+ // make an Aset out of this.
+ case EUIC_FLAVOR_ASSET_TL:
+ // make an Aset out of this.
+ case EUIC_FLAVOR_ASSET_UICFILE:
+ // make an Aset out of this.
+ // Get a pointer to the object
+ theDropSource = dynamic_cast<const CDropSource *>(inDataObject);
+ if (theDropSource != nullptr && theDropSource->GetFlavor() != inFlavor)
+ theDropSource = nullptr;
+ break;
+ }
+ return const_cast<CDropSource *>(theDropSource);
+}
+
+//===============================================================================
+/**
+ * This is to count the number of objects in the DataObject.
+ * @param inDataObject the WinSpecific dropobject
+ * @return The number of items found.
+ */
+long CDropProxy::GetDragItemCount(const QMimeData *inDataObject)
+{
+ long theCount = 0;
+
+ // Check if we have a Drop File on our hands.
+ if (inDataObject->hasUrls()) {
+ // Get the number of files being dragged
+ theCount = inDataObject->urls().count();
+ } else {
+ auto source = dynamic_cast<const CDropSource *>(inDataObject);
+ theCount = source != nullptr ? 1 : 0;
+ }
+
+ return theCount;
+}
+//===============================================================================
+/**
+ * This will check the DataObject for the Flavor.
+ * @param inDataObject the Win specific data object.
+ * @param inFlavor the requested Flavor.
+ * @param true if the DataObject contains the flavor.
+ */
+bool CDropProxy::HasMainFlavor(const QMimeData *inDataObject, long inFlavor)
+{
+ if (inFlavor == EUIC_FLAVOR_FILE)
+ return inDataObject->hasUrls();
+ auto source = dynamic_cast<const CDropSource *>(inDataObject);
+ return source != nullptr && source->GetFlavor() == inFlavor;
+}
+
+//=============================================================================
+/**
+ * Called when a drag event leaves the window. May cause the drag to end if
+ * the mouse button is no longer down.
+ */
+void CDropProxy::dragLeaveEvent(QDragLeaveEvent *event)
+{
+ if (m_Parent) {
+ m_Parent->OnDragLeave();
+ event->accept();
+ }
+}
+
+//=============================================================================
+/**
+ * Called when an item is being dragged over this window. Passes the message
+ * on to the gesture listener.
+ */
+void CDropProxy::dragMoveEvent(QDragMoveEvent *event)
+{
+ bool theAcceptFlag = false;
+
+ if (m_Parent) {
+ long theCount = GetDragItemCount(event->mimeData());
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ Qt::KeyboardModifiers theModifyerFlags = ReflectMouse(event->pos().x(), event->pos().y());
+
+ CDropContainer::TFlavorItr theItr = m_Parent->GetFlavorBegin();
+ CDropContainer::TFlavorItr theEnd = m_Parent->GetFlavorEnd();
+
+ for (; theItr != theEnd; ++theItr) {
+ // Find the Flavor of this Item.
+ if (HasMainFlavor(event->mimeData(), *theItr)) {
+ CDropSource *theDropSource = GetDropSource(event->mimeData(), *theItr, theIndex);
+ if (theDropSource) {
+ CPt thePoint(event->pos());
+ theModifyerFlags = CHotKeys::GetCurrentKeyModifiers();
+ theDropSource->SetCurrentPoint(thePoint);
+ theDropSource->SetCurrentFlags(theModifyerFlags);
+
+ theDropSource->InterpretKeyFlags(theModifyerFlags);
+ // This will be implemented in the cross platform code.
+ theAcceptFlag = m_Parent->OnDragWithin(*theDropSource);
+ event->setAccepted(theAcceptFlag);
+ if (theAcceptFlag) {
+ if (theDropSource->CanCopy())
+ event->setDropAction(Qt::CopyAction);
+ else
+ event->setDropAction(Qt::MoveAction);
+ // Breakout of the outer loop.
+ theIndex = theCount;
+ }
+
+ // delete the drop source if it was a file
+ if (EUIC_FLAVOR_FILE == theDropSource->GetFlavor()) {
+ delete theDropSource;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Called when an OLE item is dropped on this window.
+ * @return TRUE if the drop was valid, otherwise FALSE
+ */
+void CDropProxy::dropEvent(QDropEvent *event)
+{
+ if (m_Parent) {
+ long theCount = GetDragItemCount(event->mimeData());
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ Qt::KeyboardModifiers theModifyerFlags = ReflectMouse(event->pos().x(), event->pos().y());
+
+ theModifyerFlags = CHotKeys::GetCurrentKeyModifiers();
+
+ CDropContainer::TFlavorItr theItr = m_Parent->GetFlavorBegin();
+ CDropContainer::TFlavorItr theEnd = m_Parent->GetFlavorEnd();
+
+ for (; theItr != theEnd; ++theItr) {
+ // Find the Flavor of this Item.
+ if (HasMainFlavor(event->mimeData(), *theItr)) {
+ // This will convert all stuff into a DropSource
+ CDropSource *theDropSource = GetDropSource(event->mimeData(), *theItr, theIndex);
+
+ // This will be implemented in the cross platform code.
+ if (theDropSource) {
+ CPt thePoint(event->pos());
+ theDropSource->SetCurrentPoint(thePoint);
+ theDropSource->SetCurrentFlags(theModifyerFlags);
+
+ // Don't call the recieve if we did not have a valid droptarget to begin
+ // with.
+ if (theDropSource->GetHasValidTarget()) {
+ theDropSource->InterpretKeyFlags(theModifyerFlags);
+ // This will be implemented in the cross platform code.
+ m_Parent->OnDragReceive(*theDropSource);
+ }
+ }
+
+ // Don't delete the drop source here, the creator will destroy it
+ // delete theDropSource;
+ }
+ }
+ }
+ }
+
+ event->accept();
+}
+//===============================================================================
+/**
+ * This function is used to report back to the container where the mouse is during a drag.
+ * The container can send this to children as MouseOvers so the children can do mouseover
+ *things.
+ * @param inX the X position
+ * @param inY the Y position.
+ * @return the Key modifyers.
+ */
+Qt::KeyboardModifiers CDropProxy::ReflectMouse(long inX, long inY)
+{
+ Qt::KeyboardModifiers theModifierFlags = Qt::NoModifier;
+
+ if (m_Parent) {
+ // Get the mouse stuff
+ CPt theMouseLoc(inX, inY);
+
+ // Determine which modifier keys are down so that we can pass them along
+
+ theModifierFlags = CHotKeys::GetCurrentKeyModifiers();
+
+ // Pass the gesture into the studio control
+ m_Parent->OnReflectMouse(theMouseLoc, theModifierFlags);
+ }
+ return theModifierFlags;
+}
+
+void CDropProxy::Register(QWidget *widget)
+{
+ widget->installEventFilter(this);
+ widget->setAcceptDrops(true);
+}
+
+bool CDropProxy::eventFilter(QObject *watched, QEvent *event)
+{
+ Q_UNUSED(watched);
+
+ switch (event->type()) {
+ case QEvent::DragEnter:
+ dragEnterEvent(static_cast<QDragEnterEvent *>(event));
+ return event->isAccepted();
+ case QEvent::DragLeave:
+ dragLeaveEvent(static_cast<QDragLeaveEvent *>(event));
+ return event->isAccepted();
+ case QEvent::DragMove:
+ dragMoveEvent(static_cast<QDragMoveEvent *>(event));
+ return event->isAccepted();
+ case QEvent::Drop:
+ dropEvent(static_cast<QDropEvent *>(event));
+ return event->isAccepted();
+ default:
+ return false;
+ }
+}
diff --git a/src/Authoring/Studio/_Win/DragNDrop/DropProxy.h b/src/Authoring/Studio/_Win/DragNDrop/DropProxy.h
new file mode 100644
index 00000000..eadb9b83
--- /dev/null
+++ b/src/Authoring/Studio/_Win/DragNDrop/DropProxy.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_DROP_PROXY
+#define INCLUDED_DROP_PROXY 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <QObject>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+class CDropContainer;
+class CStudioApp;
+class CDropSource;
+
+class QDragEnterEvent;
+class QDragLeaveEvent;
+class QDragMoveEvent;
+class QDropEvent;
+class QMimeData;
+
+class CDropProxy : public QObject
+{
+ Q_OBJECT
+public:
+ CDropProxy(CDropContainer *inParent);
+ virtual ~CDropProxy();
+
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dragLeaveEvent(QDragLeaveEvent *event);
+ void dragMoveEvent(QDragMoveEvent *event);
+ void dropEvent(QDropEvent *event);
+
+ CDropSource *GetDropSource(const QMimeData *inDataObject, long inFlavor, long inItem);
+ long GetDragItemCount(const QMimeData *inDataObject);
+ bool HasMainFlavor(const QMimeData *inDataObject, long inFlavor);
+ Qt::KeyboardModifiers ReflectMouse(long inX, long inY);
+
+ void Register(QWidget* widget);
+
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
+protected:
+ CDropContainer *m_Parent;
+};
+
+#endif // INCLUDED_DROP_PROXY
diff --git a/src/Authoring/Studio/_Win/DragNDrop/WinDnd.cpp b/src/Authoring/Studio/_Win/DragNDrop/WinDnd.cpp
new file mode 100644
index 00000000..d6216200
--- /dev/null
+++ b/src/Authoring/Studio/_Win/DragNDrop/WinDnd.cpp
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "WinDnd.h"
+#include "DropSource.h"
+
+//===============================================================================
+/**
+ * Constructor
+ */
+CWinDragManager::CWinDragManager()
+{
+ memset(&m_DragItem, 0, sizeof(SDropItem));
+}
+
+//===============================================================================
+/**
+ * Dtor
+ */
+CWinDragManager::~CWinDragManager()
+{
+}
+
+//===============================================================================
+/**
+ * This will take any data flavor and wrap it up into out drag data.
+ */
+void CWinDragManager::AddData(long inFlavor, void *inData, unsigned long inSize)
+{
+ m_DragItem.m_Flavor = inFlavor;
+ m_DragItem.m_Data = inData;
+ m_DragItem.m_Size = inSize;
+}
+
+//===============================================================================
+/**
+ * I made this so it would kind of match the MAC version.
+ */
+void CWinDragManager::StartDrag()
+{
+ Track();
+}
+
+//===============================================================================
+/**
+ * This is the Blocking function that does the Dragging.
+ * It is assumed that all of the Data is is requested to be dragged, is already in the list
+ * and we take it out of the list and pack it into the Win OLEDropSource and let it go.
+ * We should only return when the whole operation is over.
+ */
+void CWinDragManager::Track()
+{
+ // Convert our data into a OLE DragSource.
+ HGLOBAL theGlobalData = nullptr;
+ COleDataSource theDataSource;
+
+ // Empty the data source
+ theDataSource.Empty();
+
+ // long theDataSize = m_DragItem.m_Size;
+ void *theData = m_DragItem.m_Data;
+ long theFlavor = m_DragItem.m_Flavor;
+
+ // if ( theDataSize > 0 )
+ {
+ // Allocate a global block of data
+ DWORD theGlobalObjectSize = sizeof(CDropSource *);//sizeof(long /*theDataSize*/);
+ theGlobalData = GlobalAlloc(GHND, theGlobalObjectSize);
+
+ // Lock down the global memory object for copying data
+ CDropSource *theDropSource = reinterpret_cast<CDropSource *>(GlobalLock(theGlobalData));
+
+ // Copy the CDropSource pointer
+ CopyMemory(theDropSource, theData, theGlobalObjectSize);
+
+ // Unlock, but do not free
+ GlobalUnlock(theGlobalData);
+
+ // Prepare OLE data for transfer
+ theDataSource.CacheGlobalData((CLIPFORMAT)theFlavor, theGlobalData, nullptr);
+ }
+
+ // This will go through the framework.
+ if (DROPEFFECT_NONE == theDataSource.DoDragDrop()) {
+ theDataSource.Empty();
+ if (theGlobalData)
+ ::GlobalFree(theGlobalData);
+ }
+}
diff --git a/src/Authoring/Studio/_Win/DragNDrop/WinDnd.h b/src/Authoring/Studio/_Win/DragNDrop/WinDnd.h
new file mode 100644
index 00000000..8af759c7
--- /dev/null
+++ b/src/Authoring/Studio/_Win/DragNDrop/WinDnd.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2003 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#ifndef INCLUDED_WINDND
+#define INCLUDED_WINDND
+
+#include <map>
+
+typedef struct _SDropItem
+{
+ long m_Flavor; // The Type of Data.
+ void *m_Data; ///< A nullptr value will cause the Data to be created Later on ( Promise )
+ unsigned long m_Size; //< The size of the Data.
+} SDropItem;
+
+//=============================================================================
+/**
+ * This class only supports dragging one item.
+ */
+class CWinDragManager
+{
+protected:
+ SDropItem m_DragItem; ///< the dragged item
+
+public:
+ CWinDragManager();
+ virtual ~CWinDragManager();
+
+ void Track();
+ void StartDrag();
+
+ void AddData(long inFlavor, void *inData, unsigned long inSize);
+};
+
+#endif
diff --git a/src/Authoring/Studio/_Win/Include/StudioPrefixWin32.h b/src/Authoring/Studio/_Win/Include/StudioPrefixWin32.h
new file mode 100644
index 00000000..114e3bf8
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Include/StudioPrefixWin32.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STUDIO_PREFIX_WIN_32_H
+#define INCLUDED_STUDIO_PREFIX_WIN_32_H 1
+
+#pragma once
+
+#define AK_LITTLE_ENDIAN
+//#define WIN32
+
+#include <float.h>
+
+#endif // INCLUDED_STUDIO_PREFIX_WIN_32_H
diff --git a/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp b/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp
new file mode 100644
index 00000000..41f0fe42
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/PaletteManager.cpp
@@ -0,0 +1,902 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PaletteManager.h"
+#include "StudioApp.h"
+#include "Views.h"
+#include "MainFrm.h"
+#include "StringLoader.h"
+#include "TimelineControl.h"
+#include "BasicObjectsView.h"
+#include "SlideView.h"
+#include "StringTokenizer.h"
+#include "Preferences.h"
+#include "WidgetControl.h"
+#include "InspectorControlView.h"
+#include "ActionView.h"
+#include "IDragable.h"
+#include "ActionView.h"
+#include "ProjectView.h"
+#include "TabOrderHandler.h"
+
+#include <QDockWidget>
+
+//==============================================================================
+/**
+ * Class for std::for_each to delete each control
+ */
+template <class T>
+class CDeleteAll
+{
+public:
+ void operator()(const T *inControl) const { delete inControl; }
+};
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CPaletteManager::CPaletteManager(CMainFrame *inMainFrame)
+ : m_MainFrame(inMainFrame)
+{
+ // Position tabs to the right
+ inMainFrame->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::East);
+
+ QDockWidget *basicObjectsDock = new QDockWidget(QObject::tr("Basic Objects"), inMainFrame);
+ basicObjectsDock->setObjectName("basic_objects");
+ basicObjectsDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea
+ | Qt::BottomDockWidgetArea);
+ auto basicObjectsView = new BasicObjectsView(basicObjectsDock);
+ basicObjectsDock->setWidget(basicObjectsView);
+ inMainFrame->addDockWidget(Qt::RightDockWidgetArea, basicObjectsDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_BASICOBJECTS, basicObjectsDock));
+
+ QDockWidget *projectDock = new QDockWidget(QObject::tr("Project"), inMainFrame);
+ projectDock->setObjectName("project");
+ projectDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea
+ | Qt::BottomDockWidgetArea);
+ auto projectView = new ProjectView(projectDock);
+ projectDock->setWidget(projectView);
+ inMainFrame->addDockWidget(Qt::RightDockWidgetArea, projectDock);
+ inMainFrame->tabifyDockWidget(basicObjectsDock, projectDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_PROJECT, projectDock));
+
+ QDockWidget *slideDock = new QDockWidget(QObject::tr("Slide"), inMainFrame);
+ slideDock->setObjectName("slide");
+ slideDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+ auto slideView = new SlideView(slideDock);
+ slideDock->setWidget(slideView);
+ inMainFrame->addDockWidget(Qt::LeftDockWidgetArea, slideDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_SLIDE, slideDock));
+
+ QDockWidget *timelineDock = new QDockWidget(QObject::tr("Timeline"), inMainFrame);
+ timelineDock->setObjectName("timeline");
+ timelineDock->setAllowedAreas(Qt::BottomDockWidgetArea);
+ auto c = new CTimelineControl();
+ auto w = new WidgetControl(c, timelineDock);
+ timelineDock->setWidget(w);
+ w->setMinimumWidth(500);
+ inMainFrame->addDockWidget(Qt::BottomDockWidgetArea, timelineDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_TIMELINE, timelineDock));
+
+ int actionViewMinWidth = CStudioPreferences::valueWidth()
+ + CStudioPreferences::idWidth() + 40; // 40 added to accommodate tabs
+
+ QDockWidget *actionDock = new QDockWidget(QObject::tr("Action"), inMainFrame);
+ actionDock->setObjectName("action");
+ actionDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea
+ | Qt::BottomDockWidgetArea);
+ auto actionView = new ActionView(actionDock);
+ actionDock->setWidget(actionView);
+ actionView->setMinimumWidth(actionViewMinWidth);
+ inMainFrame->addDockWidget(Qt::BottomDockWidgetArea, actionDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_ACTION, actionDock));
+
+ QDockWidget *inspectorDock = new QDockWidget(QObject::tr("Inspector Control"), inMainFrame);
+ inspectorDock->setObjectName("inspector_control");
+ inspectorDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea
+ | Qt::BottomDockWidgetArea);
+ auto inspectorView = new InspectorControlView(inspectorDock);
+ inspectorDock->setWidget(inspectorView);
+ inspectorView->setMinimumWidth(actionViewMinWidth); // Same min size as action view
+ inMainFrame->addDockWidget(Qt::BottomDockWidgetArea, inspectorDock);
+ inMainFrame->tabifyDockWidget(actionDock, inspectorDock);
+ m_ControlList.insert(std::make_pair(CONTROLTYPE_INSPECTOR, inspectorDock));
+
+ w->RegiserForDnd(w);
+ w->AddMainFlavor(EUIC_FLAVOR_LISTBOX);
+ w->AddMainFlavor(EUIC_FLAVOR_FILE);
+ w->AddMainFlavor(EUIC_FLAVOR_ASSET_UICFILE);
+ w->AddMainFlavor(EUIC_FLAVOR_ASSET_LIB);
+ w->AddMainFlavor(EUIC_FLAVOR_ASSET_TL);
+ w->AddMainFlavor(EUIC_FLAVOR_BASIC_OBJECTS);
+
+ // Set to a default state
+ Reset();
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CPaletteManager::~CPaletteManager()
+{
+ TControlMap::iterator theIterator = m_ControlList.begin();
+ TControlMap::iterator theEndIterator = m_ControlList.end();
+#ifdef KDAB_TEMPORARILY_REMOVED
+ for (; theIterator != theEndIterator; ++theIterator) {
+ // Remove the palette from the old master
+ long theType = theIterator->first;
+ CMasterControl *theOldMaster = FindMasterPalette(theType);
+ if (theOldMaster)
+ RemoveControlFromMaster(theOldMaster, theType);
+ }
+ std::for_each(m_PaletteList.begin(), m_PaletteList.end(), CDeleteAll<CStudioPaletteBar>());
+#endif
+ // Delete all the controls
+ for (theIterator = m_ControlList.begin(); theIterator != theEndIterator; ++theIterator)
+ delete theIterator->second;
+}
+
+//==============================================================================
+/**
+ * Clear out old lists and reset all the palettes
+ */
+void CPaletteManager::Reset()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Set the visible state to false
+ TControlMap::iterator theIterator = m_ControlList.begin();
+ TControlMap::iterator theEndIterator = m_ControlList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ CControl *theControl = theIterator->second;
+ if (theControl)
+ theControl->SetVisible(false);
+ }
+
+ // Undock all palette
+ TPaletteList::iterator thePaletteIterator = m_PaletteList.begin();
+ for (; thePaletteIterator != m_PaletteList.end(); ++thePaletteIterator) {
+ CStudioPaletteBar *thePaletteBar = (*thePaletteIterator);
+ thePaletteBar->ShowPalette(false);
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Create a palette
+ */
+CStudioPaletteBar *CPaletteManager::CreatePalette()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Build the create context
+ CCreateContext theCreateContext;
+ memset(&theCreateContext, 0, sizeof(CCreateContext));
+ theCreateContext.m_pNewViewClass = RUNTIME_CLASS(CMasterView);
+ theCreateContext.m_pCurrentFrame = m_MainFrame;
+
+ // Create the palette bar
+ CStudioPaletteBar *thePaletteBar = new CStudioPaletteBar;
+ thePaletteBar->Create(::CString("<empty>"), &theCreateContext, GetUniquePaletteId(),
+ m_MainFrame);
+
+ // Set the provider
+ CMasterView *theMasterView = (CMasterView *)thePaletteBar->GetView();
+ theMasterView->SetProvider(this);
+
+ // Must occur after the provider is set
+ thePaletteBar->InitialUpdate();
+
+ // Save the palette
+ ASSERT(thePaletteBar != nullptr);
+ m_PaletteList.push_back(thePaletteBar);
+
+ // Store the information
+ TSMasterInfo theInfo;
+ theInfo.m_Master = theMasterView->GetMasterControl();
+ theInfo.m_PaletteBar = thePaletteBar;
+
+ ASSERT(theInfo.m_PaletteBar != nullptr);
+ m_MasterList.push_back(theInfo);
+
+ return thePaletteBar;
+#endif
+ return nullptr;
+}
+
+//==============================================================================
+/**
+ * Find a master control with no contained controls
+ */
+CMasterControl *CPaletteManager::FindUnusedMaster()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (theIterator->m_Palettes.any() == false)
+ return theIterator->m_Master;
+ }
+#endif
+
+ return nullptr;
+}
+
+CMasterControl *CPaletteManager::FindMasterByPaletteId(long inPaletteId)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ CControlBarInfo theBarInfo;
+ theIterator->m_PaletteBar->GetBarInfo(&theBarInfo);
+
+ if (theBarInfo.m_nBarID == (UINT)inPaletteId)
+ return theIterator->m_Master;
+ }
+#endif
+ return nullptr;
+}
+
+CStudioPaletteBar *CPaletteManager::FindStudioPaletteBar(long inType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (theIterator->m_Palettes[inType] == true)
+ return theIterator->m_PaletteBar;
+ }
+#endif
+ return nullptr;
+}
+
+CStudioPaletteBar *CPaletteManager::FindStudioPaletteBar(CMasterControl *inMaster)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (theIterator->m_Master == inMaster)
+ return theIterator->m_PaletteBar;
+ }
+#endif
+ return nullptr;
+}
+
+//==============================================================================
+/**
+ * Add a new master to the list of masters
+ */
+CMasterControl *CPaletteManager::AddMasterPalette(long inType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Remove the palette from the old master
+ CMasterControl *theOldMaster = FindMasterPalette(inType);
+ if (theOldMaster)
+ RemoveControlFromMaster(theOldMaster, inType);
+
+ // Create a new master
+ CMasterControl *theMasterControl = FindUnusedMaster();
+ if (theMasterControl != nullptr) {
+ // Add the palette to the new master
+ AddControlToMaster(theMasterControl, inType);
+ }
+
+ return theMasterControl;
+#endif
+ return nullptr;
+}
+
+//==============================================================================
+/**
+ * Add the specified palette to the specified master
+ */
+void CPaletteManager::AddControlToMaster(CMasterControl *inMaster, long inType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Make sure this is a valid type ( for backward compatibility )
+ TControlMap::iterator theControlIterator = m_ControlList.find(inType);
+ if (theControlIterator != m_ControlList.end()) {
+ // Make sure the palette is not already on another master
+ CMasterControl *theOldMaster = FindMasterPalette(inType);
+ if (theOldMaster)
+ RemoveControlFromMaster(theOldMaster, inType);
+
+ // Add the palette to this master
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ for (; theIterator != m_MasterList.end(); ++theIterator) {
+ if (theIterator->m_Master == inMaster) {
+ theIterator->m_Palettes[inType] = true;
+ theIterator->m_Master->AddControl(GetControlName(inType), inType,
+ theControlIterator->second);
+ theIterator->m_Master->Invalidate();
+ break;
+ }
+ }
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Remove the specified palette from the specified master
+ */
+void CPaletteManager::RemoveControlFromMaster(CMasterControl *inMaster, long inType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ for (; theIterator != m_MasterList.end(); ++theIterator) {
+ if (theIterator->m_Master == inMaster) {
+ theIterator->m_Palettes[inType] = false;
+ theIterator->m_Master->RemoveControl(GetControl(inType));
+ theIterator->m_Master->Invalidate();
+ }
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Find the name of the master that contains this palette
+ */
+CMasterControl *CPaletteManager::FindMasterPalette(long inType)
+{
+ CMasterControl *theMaster = nullptr;
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (; theIterator != theEndIterator; ++theIterator) {
+ if (theIterator->m_Palettes[inType] == true) {
+ theMaster = theIterator->m_Master;
+ break;
+ }
+ }
+#endif
+ return theMaster;
+}
+
+//==============================================================================
+/**
+ * Query the number of masters
+ */
+long CPaletteManager::GetMasterCount()
+{
+ return (long)m_MasterList.size();
+}
+
+//==============================================================================
+/**
+ * Get the master at the specified index
+ */
+CMasterControl *CPaletteManager::GetMaster(long inIndex)
+{
+ CMasterControl *theControl = nullptr;
+#ifdef KDAB_TEMPORARILY_REMOVED
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ std::advance(theIterator, inIndex);
+ if (theIterator != m_MasterList.end())
+ theControl = theIterator->m_Master;
+#endif
+ return theControl;
+}
+
+//=============================================================================
+/**
+ * Force a control to become invisible
+ */
+void CPaletteManager::HideControl(long inType)
+{
+ auto dock = GetControl(inType);
+
+ if (dock) {
+ // Make sure the control is invisible
+ dock->setVisible(false);
+ }
+}
+//=============================================================================
+/**
+ * Detemine if a control is currently visible
+ */
+bool CPaletteManager::IsControlVisible(long inType) const
+{
+ auto dock = GetControl(inType);
+ return dock && dock->isVisible();
+}
+
+//=============================================================================
+/**
+ * Force a control to become visible
+ */
+void CPaletteManager::ShowControl(long inType)
+{
+ auto dock = GetControl(inType);
+
+ if (dock) {
+ // Make sure the control is visible
+ dock->setVisible(true);
+ dock->setFocus();
+ }
+}
+
+//=============================================================================
+/**
+ * Flip the visible state of a control
+ */
+void CPaletteManager::ToggleControl(long inType)
+{
+ if (IsControlVisible(inType))
+ HideControl(inType);
+ else
+ ShowControl(inType);
+}
+
+//==============================================================================
+/**
+ * Return the Control (Palette) according to its EControlTypes enum.
+ * @param inType EControlTypes
+ */
+QDockWidget *CPaletteManager::GetControl(long inType) const
+{
+ auto dock = m_ControlList.find(inType);
+ if (dock != m_ControlList.end() && dock->second)
+ return dock->second;
+ else
+ return nullptr;
+}
+
+QWidget *CPaletteManager::getFocusWidget() const
+{
+ TControlMap::const_iterator end = m_ControlList.end();
+ for (TControlMap::const_iterator iter = m_ControlList.begin(); iter != end; ++iter) {
+ if (iter->second->widget()->hasFocus())
+ return iter->second->widget();
+ }
+ return nullptr;
+}
+
+bool CPaletteManager::tabNavigateFocusedWidget(bool tabForward)
+{
+ QWidget *palette = getFocusWidget();
+ if (palette) {
+ if (auto inspector = qobject_cast<InspectorControlView *>(palette)) {
+ inspector->tabOrderHandler()->tabNavigate(tabForward);
+ return true;
+ } else if (auto actionview = qobject_cast<ActionView *>(palette)) {
+ actionview->tabOrderHandler()->tabNavigate(tabForward);
+ return true;
+ }
+ }
+ return false;
+}
+
+//==============================================================================
+/**
+ * A helper for CMainFrame::GetTimelineControl() to access the CTimelineControl
+ * inside the QDockWidget
+ */
+CTimelineControl *CPaletteManager::GetTimelineControl() const
+{
+ auto dock = GetControl(CPaletteManager::CONTROLTYPE_TIMELINE);
+
+ if (dock) {
+ auto widget = static_cast<WidgetControl *>(dock->widget());
+
+ if (widget)
+ return static_cast<CTimelineControl *>(widget->getControl());
+ }
+
+ return nullptr;
+}
+
+//==============================================================================
+// Serialization
+//==============================================================================
+
+//==============================================================================
+/**
+ * Load the palette state from the registry
+ */
+bool CPaletteManager::Load()
+{
+ // Clear out existing local stuff
+ Reset();
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Test the palette prefs, return false if they don't exist
+ if (CPreferences::GetUserPreferences().Exists(PALETTE_KEY) == false)
+ return false;
+
+ long theCurrentPalette = 0;
+ while (true) {
+ // Format the subkey name
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%ls%ld", PALETTE_SUBKEY, theCurrentPalette);
+
+ // Check to see if we should exit the for loop
+ CPreferences thePalettePrefs = CPreferences::GetUserPreferences(PALETTE_KEY);
+ if (thePalettePrefs.Exists(theSubKeyName) == false)
+ break;
+
+ // Format the subkey name
+ Q3DStudio::CString theFullKeyName;
+ theFullKeyName.Format(L"%ls\\%ls%ld", PALETTE_KEY, PALETTE_SUBKEY, theCurrentPalette);
+
+ // Grab the prefs
+ CPreferences thePrefs = CPreferences::GetUserPreferences(theFullKeyName);
+
+ // Default docking value
+ Q3DStudio::CString theDockString =
+ thePrefs.GetStringValue(PALETTE_CONTROL_DOCKID, L"0"); // left
+ long theDockId = ::atol(theDockString.GetCharStar());
+
+ // Default Rect
+ QRect theWindowRect;
+ Q3DStudio::CString theRectString =
+ thePrefs.GetStringValue(PALETTE_CONTROL_RECT, L"0,0,0,0");
+ CStringTokenizer theRectTokenizer(theRectString, L",");
+ for (long theIndex = 0; theRectTokenizer.HasNextPartition();
+ ++theRectTokenizer, ++theIndex) {
+ Q3DStudio::CString theCurrentString = theRectTokenizer.GetCurrentPartition();
+ long theCurrentItem = ::atol(theCurrentString.GetCharStar());
+
+ switch (theIndex) {
+ case 0:
+ theWindowRect.setLeft(theCurrentItem);
+ break;
+ case 1:
+ theWindowRect.setTop(theCurrentItem);
+ break;
+ case 2:
+ theWindowRect.setRight(theCurrentItem);
+ break;
+ case 3:
+ theWindowRect.setBottom(theCurrentItem);
+ break;
+ };
+ }
+
+ // Palette id
+ Q3DStudio::CString thePaletteIdString =
+ thePrefs.GetStringValue(PALETTE_CONTROL_PALETTEID, L"0");
+ long thePaletteId = ::atol(thePaletteIdString.GetCharStar());
+
+ // Read the selected item
+ Q3DStudio::CString theSelectedString =
+ thePrefs.GetStringValue(PALETTE_CONTROL_SELECTED, L"0");
+ long theSelectedType = ::atol(theSelectedString.GetCharStar());
+
+ // Grab the palette
+ CMasterControl *theMasterControl;
+ if (thePaletteId)
+ theMasterControl = FindMasterByPaletteId(thePaletteId);
+ else
+ theMasterControl = FindUnusedMaster();
+
+ if (theMasterControl != nullptr) {
+ CStudioPaletteBar *thePaletteBar = FindStudioPaletteBar(theMasterControl);
+
+ Q3DStudio::CString theContentString =
+ thePrefs.GetStringValue(PALETTE_CONTROL_LIST, L"0");
+ CStringTokenizer theTokenizer(theContentString, L",");
+ for (; theTokenizer.HasNextPartition(); ++theTokenizer) {
+ Q3DStudio::CString theCurrentString = theTokenizer.GetCurrentPartition();
+ long theCurrentItem = ::atol(theCurrentString.GetCharStar());
+
+ if (theCurrentItem != CONTROLTYPE_NONE)
+ AddControlToMaster(theMasterControl, theCurrentItem);
+ }
+
+ // Dock the bar in the appropriate location (if requested)
+ if (theDockId != 0) {
+ CDockBar *theDockBar =
+ (CDockBar *)m_MainFrame->GetControlBar(theDockId + AFX_IDW_DOCKBAR_TOP);
+ theDockBar->ClientToScreen(theWindowRect);
+
+ thePaletteBar->SetHorz(CSize(theWindowRect.Width(), theWindowRect.Height()));
+ thePaletteBar->SetVert(CSize(theWindowRect.Width(), theWindowRect.Height()));
+
+ m_MainFrame->DockControlBar(thePaletteBar, theDockBar, theWindowRect);
+ m_MainFrame->RecalcLayout(TRUE);
+ }
+
+ // Select the old item
+ theMasterControl->SelectControl(theSelectedType);
+
+ // Turn on my heart light
+ thePaletteBar->ShowPalette(true);
+ }
+
+ // Move on to the next key
+ theCurrentPalette++;
+ }
+
+ if (m_MainFrame) {
+ // Allow the control bars (docking palettes, etc) the opportunity to restore
+ // This will use a registry key based off of AFX_IDS_APP_TITLE ("Qt 3D Studio") which
+ // is different than CPreferences (".../Qt 3D Studio/Settings/...")
+ CSizingControlBar::GlobalLoadState(m_MainFrame, PALETTE_LAYOUT_KEY_NAME);
+ m_MainFrame->LoadBarState(PALETTE_LAYOUT_KEY_NAME);
+ }
+#endif
+
+ return true;
+}
+
+//==============================================================================
+/**
+ * Save the palette state to the registry
+ */
+void CPaletteManager::Save()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Destroy current information in the prefs
+ CPreferences theMasterPrefs = CPreferences::GetUserPreferences(PALETTE_KEY);
+ theMasterPrefs.Clear();
+
+ // Iterate through the m_MasterList
+ TMasterList::iterator theIterator = m_MasterList.begin();
+ TMasterList::iterator theEndIterator = m_MasterList.end();
+ for (long theMasterIndex = 0; theIterator != theEndIterator; ++theIterator) {
+ TSMasterInfo &theMasterInfo = *theIterator;
+ if (theMasterInfo.m_PaletteBar->IsVisible()) {
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%ls\\%ls%ld", PALETTE_KEY, PALETTE_SUBKEY, theMasterIndex);
+
+ CPreferences thePrefs = CPreferences::GetUserPreferences(theSubKeyName);
+
+ // Save palette contents
+ Q3DStudio::CString theTempString;
+ Q3DStudio::CString theContentString;
+ for (long theIndex = 0; theIndex < (long)theMasterInfo.m_Palettes.size(); ++theIndex) {
+ if (theMasterInfo.m_Palettes[theIndex]) {
+ theTempString.Format(L"%ld,", theIndex);
+ theContentString += theTempString;
+ }
+ }
+
+ thePrefs.SetStringValue(PALETTE_CONTROL_LIST, theContentString);
+
+ // Save palette id
+ CControlBarInfo theBarInfo;
+ theMasterInfo.m_PaletteBar->GetBarInfo(&theBarInfo);
+ theTempString.Format(L"%ld", theBarInfo.m_nBarID);
+ thePrefs.SetStringValue(PALETTE_CONTROL_PALETTEID, theTempString);
+
+ // Save selected item
+ long theSelectedIndex = theMasterInfo.m_Master->GetActiveIndex();
+ theTempString.Format(L"%ld", theSelectedIndex);
+ thePrefs.SetStringValue(PALETTE_CONTROL_SELECTED, theTempString);
+
+ // Default values
+ thePrefs.SetStringValue(PALETTE_CONTROL_DOCKID, L"0");
+ thePrefs.SetStringValue(PALETTE_CONTROL_RECT, L"0,0,0,0");
+
+ // Increment the palette counter
+ ++theMasterIndex;
+ }
+ }
+
+ // Allow the control bars (docking palettes, etc) the opportunity to store
+ // This will use a registry key based off of AFX_IDS_APP_TITLE ("Qt 3D Studio") which
+ // is different than CPreferences (".../Qt 3D Studio/Settings/...")
+ if (m_MainFrame) {
+ CSizingControlBar::GlobalSaveState(m_MainFrame, PALETTE_LAYOUT_KEY_NAME);
+ m_MainFrame->SaveBarState(PALETTE_LAYOUT_KEY_NAME);
+ }
+#endif
+}
+
+//==============================================================================
+// Defaults
+//==============================================================================
+
+//==============================================================================
+/**
+ * Set the palettes to the default state
+ *
+ * CONTROLTYPE_ACTION = 1, ///<
+ * CONTROLTYPE_BASICOBJECTS = 3, ///<
+ * CONTROLTYPE_INSPECTOR = 4, ///<
+ * CONTROLTYPE_SLIDE = 6, ///<
+ * CONTROLTYPE_TIMELINE = 7, ///<
+ * CONTROLTYPE_COMMAND = 8, ///<
+ * CONTROLTYPE_PROJECT = 9, ///<
+ *
+ * #define AFX_IDW_DOCKBAR_TOP 0xE81B = 0
+ * #define AFX_IDW_DOCKBAR_LEFT 0xE81C = 1
+ * #define AFX_IDW_DOCKBAR_RIGHT 0xE81D = 2
+ * #define AFX_IDW_DOCKBAR_BOTTOM 0xE81E = 3
+ * #define AFX_IDW_DOCKBAR_FLOAT 0xE81F = 4
+ */
+void CPaletteManager::RestoreDefaults(bool inForce)
+{
+ Q_UNUSED(inForce);
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+ // Destroy current information in the prefs (Palettes)
+ CPreferences theMasterPrefs = CPreferences::GetUserPreferences(PALETTE_KEY);
+ theMasterPrefs.Clear();
+
+ // "3,9,2,5:0:2:0,0,300,250#4,1:0:2:0,251,300,601#6:0:1:0,0,125,125#7:0:3:0,0,250,300";
+ Q3DStudio::CString thePaletteString = ::LoadResourceString(IDS_DEFAULT_PALETTE_LAYOUT);
+ CStringTokenizer thePaletteTokenizer(thePaletteString, L"#");
+ for (long thePaletteIndex = 0; thePaletteTokenizer.HasNextPartition();
+ ++thePaletteTokenizer, ++thePaletteIndex) {
+ Q3DStudio::CString theCurrentPalette = thePaletteTokenizer.GetCurrentPartition();
+ CStringTokenizer theSubTokenizer(theCurrentPalette, L":");
+
+ // No error checking, this string better be right!
+ Q3DStudio::CString theControlList = theSubTokenizer.GetCurrentPartition();
+ ++theSubTokenizer;
+ Q3DStudio::CString theSelectedControl = theSubTokenizer.GetCurrentPartition();
+ ++theSubTokenizer;
+ Q3DStudio::CString theDockId = theSubTokenizer.GetCurrentPartition();
+ ++theSubTokenizer;
+ Q3DStudio::CString theControlRect = theSubTokenizer.GetCurrentPartition();
+ ++theSubTokenizer;
+
+ // Create the new registry keys (Palettes\PaletteN)
+ Q3DStudio::CString theSubKeyName;
+ theSubKeyName.Format(L"%ls\\%s%ld", PALETTE_KEY, PALETTE_SUBKEY, thePaletteIndex);
+ CPreferences thePrefs = CPreferences::GetUserPreferences(theSubKeyName);
+
+ thePrefs.SetStringValue(PALETTE_CONTROL_LIST, theControlList);
+ thePrefs.SetStringValue(PALETTE_CONTROL_SELECTED, theSelectedControl);
+ thePrefs.SetStringValue(PALETTE_CONTROL_DOCKID, theDockId);
+ thePrefs.SetStringValue(PALETTE_CONTROL_RECT, theControlRect);
+ }
+#endif
+ // Load these items
+ Load();
+}
+
+//==============================================================================
+// Palette Context Menu
+//==============================================================================
+
+//=============================================================================
+/**
+ * Callback to create a new palette of the specified type.
+ */
+void CPaletteManager::OnNewPalette(CMasterControl *inMaster)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ ASSERT(inMaster != nullptr);
+
+ // Query the current control type
+ long theType = inMaster->GetActiveType();
+
+ // Add a new master with that control type
+ CMasterControl *theNewMaster = AddMasterPalette(theType);
+
+ // Float the new palette
+ CStudioPaletteBar *thePaletteBar = FindStudioPaletteBar(theNewMaster);
+ m_MainFrame->FloatControlBar(thePaletteBar, QPoint(0, 0));
+
+ // Turn on my heart light
+ thePaletteBar->ShowPalette(true);
+#endif
+}
+
+//=============================================================================
+/**
+ * Callback to move a control to the specified palette
+ */
+void CPaletteManager::OnMovePalette(CMasterControl *inMoveFromMaster,
+ CMasterControl *inMoveToMaster)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ ASSERT(inMoveFromMaster != nullptr);
+ ASSERT(inMoveToMaster != nullptr);
+
+ // Move the control to the new master
+ long theType = inMoveFromMaster->GetActiveType();
+ AddControlToMaster(inMoveToMaster, theType);
+
+ // Check to see if the master is empty
+ if (inMoveFromMaster->GetControlCount() <= 0) {
+ // Make sure the palette is visible
+ CStudioPaletteBar *thePaletteBar = FindStudioPaletteBar(inMoveFromMaster);
+ thePaletteBar->ShowPalette(false);
+ }
+#endif
+}
+
+//==============================================================================
+// IMasterControlProvider
+//==============================================================================
+
+void CPaletteManager::OnControlRemoved(CMasterControl *inMaster)
+{
+ (void)inMaster;
+}
+
+void CPaletteManager::OnContextMenu(CMasterControl *inMaster, const CPt &inPosition,
+ CContextMenu *inMyMenu)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CPaletteContextMenu theContextMenu(this, inMaster, inMyMenu);
+
+ // Popup the context menu
+ inMaster->DoPopup(&theContextMenu, inPosition);
+#endif
+}
+
+void CPaletteManager::OnControlSelected(CMasterControl *inMaster, CControl *, long inType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ Q3DStudio::CString theControlName = GetControlName(inType);
+
+ CStudioPaletteBar *thePalette = FindStudioPaletteBar(inMaster);
+ ASSERT(thePalette != nullptr);
+
+ thePalette->SetWindowText(theControlName);
+#endif
+}
+
+//==============================================================================
+// Static Methods
+//==============================================================================
+
+//==============================================================================
+/**
+ * Query the name of the specified palette
+ */
+Q3DStudio::CString CPaletteManager::GetControlName(long inType)
+{
+ switch (inType) {
+ case CONTROLTYPE_ACTION:
+ return ::LoadResourceString(IDS_PALETTE_ACTION);
+ case CONTROLTYPE_BASICOBJECTS:
+ return ::LoadResourceString(IDS_PALETTE_BASIC_OBJECTS);
+ case CONTROLTYPE_INSPECTOR:
+ return ::LoadResourceString(IDS_PALETTE_INSPECTOR);
+ case CONTROLTYPE_SLIDE:
+ return ::LoadResourceString(IDS_PALETTE_SLIDE);
+ case CONTROLTYPE_TIMELINE:
+ return ::LoadResourceString(IDS_PALETTE_TIMELINE);
+ case CONTROLTYPE_PROJECT:
+ return ::LoadResourceString(IDS_PALETTE_PROJECT);
+ default:
+ return L"< Empty >";
+ };
+}
+
diff --git a/src/Authoring/Studio/_Win/Palettes/PaletteManager.h b/src/Authoring/Studio/_Win/Palettes/PaletteManager.h
new file mode 100644
index 00000000..e162e7f7
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/PaletteManager.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_VIEW_MANAGER_H
+#define INCLUDED_VIEW_MANAGER_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include <bitset>
+#include "MasterControl.h"
+#include "TimelineControl.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+class CMainFrame;
+class CStudioPaletteBar;
+
+class QDockWidget;
+
+//==============================================================================
+/**
+ * @class CPaletteManager
+ */
+class CPaletteManager : public IMasterControlProvider
+{
+public:
+ // Do NOT change the order/values of this enum, these
+ // values are stored in the registry
+ enum EControlTypes {
+ CONTROLTYPE_NONE = 0, ///<
+ CONTROLTYPE_ACTION = 1, ///<
+ CONTROLTYPE_BASICOBJECTS = 3, ///<
+ CONTROLTYPE_INSPECTOR = 4, ///<
+ CONTROLTYPE_SLIDE = 6, ///<
+ CONTROLTYPE_TIMELINE = 7, ///<
+ CONTROLTYPE_PROJECT = 9, ///<
+
+ CONTROLTYPE_MAXCONTROLS = 32, ///< the maximum number of palettes( a string of this length
+ ///is saved in the registry, changing this value will require
+ ///an upgrade process )
+ };
+
+public:
+ typedef std::bitset<CONTROLTYPE_MAXCONTROLS> TPaletteSet;
+
+protected:
+ typedef struct _TSMasterInfo
+ {
+ CStudioPaletteBar *m_PaletteBar; ///< pointer to the master palette bar
+ TPaletteSet m_Palettes; ///< the palettes contained by the master
+ CMasterControl *m_Master; ///< pointer to the master control
+
+ } TSMasterInfo;
+
+ typedef std::map<long, QDockWidget *> TControlMap;
+ typedef std::vector<CStudioPaletteBar *> TPaletteList;
+ typedef std::vector<TSMasterInfo> TMasterList;
+
+protected:
+ CMainFrame *m_MainFrame; ///<
+ TControlMap m_ControlList; ///< map of EControlTypes and CControl
+ TPaletteList m_PaletteList; ///<
+ TMasterList m_MasterList; ///<
+
+ static long s_PaletteIDBase; ///<
+
+public:
+ CPaletteManager(CMainFrame *inMainFrame);
+ virtual ~CPaletteManager();
+
+ // Access
+ void AddControlToMaster(CMasterControl *inControl, long inType);
+ void RemoveControlFromMaster(CMasterControl *inControl, long inType);
+ CMasterControl *FindMasterPalette(long inType);
+ long GetMasterCount();
+ CMasterControl *GetMaster(long inIndex);
+ void HideControl(long inType);
+ bool IsControlVisible(long inType) const;
+ void ShowControl(long inType);
+ void ToggleControl(long inType);
+
+ QDockWidget *GetControl(long inType) const; ///< return corresponding Palette according to EControlTypes enum value
+ QWidget *getFocusWidget() const;
+ bool tabNavigateFocusedWidget(bool tabForward);
+ CTimelineControl *GetTimelineControl() const;
+
+ // Commands
+ void OnNewPalette(CMasterControl *inMaster);
+ void OnMovePalette(CMasterControl *inMoveFromMaster, CMasterControl *inMoveToMaster);
+
+ // Serialization
+ bool Load();
+ void Save();
+
+ // Defaults
+ void RestoreDefaults(bool inForce = false);
+
+ // IMasterControlProvider
+ void OnControlRemoved(CMasterControl *inControl) override;
+ void OnContextMenu(CMasterControl *inControl, const CPt &inPosition,
+ CContextMenu *inMyMenu) override;
+ void OnControlSelected(CMasterControl *inMaster, CControl *inNewControl, long inType) override;
+
+ // Static Methods
+ static Q3DStudio::CString GetControlName(long inType);
+
+protected:
+ CMasterControl *AddMasterPalette(long inType);
+
+ void Reset();
+ CStudioPaletteBar *CreatePalette();
+
+ CMasterControl *FindUnusedMaster();
+ CMasterControl *FindMasterByPaletteId(long inPaletteId);
+
+ CStudioPaletteBar *FindStudioPaletteBar(long inType);
+ CStudioPaletteBar *FindStudioPaletteBar(CMasterControl *inMaster);
+ void OnAsyncDestroyWindow(CStudioPaletteBar *inWnd);
+};
+
+#endif // INCLUDED_VIEW_MANAGER_H
diff --git a/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.cpp b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.cpp
new file mode 100644
index 00000000..a9f1e546
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.cpp
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ProgressPalette.h"
+#include "StudioConst.h"
+#include "ResourceCache.h"
+#include "ResImage.h"
+#include "ProgressView.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CProgressPalette::CProgressPalette()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CProgressPalette::~CProgressPalette()
+{
+}
+
+//==============================================================================
+/**
+ * A wrapper around the CMiniFrameWnd::Create() method.
+ * Simplifies the create parameters for the palette bar. Overridden because
+ * the loading screen is different from all the other palettes.
+ * @return The result from the Create() call (FALSE if unsuccessful)
+ */
+BOOL CProgressPalette::Create(CString inTitle, CCreateContext *inCreateContext,
+ CWnd *inParent /* = nullptr */)
+{
+ CString theWndClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(nullptr, IDC_WAIT));
+ BOOL theReturnValue = FALSE;
+
+ // Add the title of the bar to the registry location.
+ m_DialogName = inTitle;
+
+ // Get the rectangle that we want this splash screen to occupy in the middle of the screen
+ CPt theScreenSize = ::GetAvailableDisplaySize();
+ CResImage *theImage = CResourceCache::GetInstance()->GetBitmap("progress-screen.png");
+ CPt theImageSize = theImage->GetSize();
+ long theTitleBarHeight = ::GetSystemMetrics(SM_CYCAPTION);
+ long theXPos = theScreenSize.x / 2 - theImageSize.x / 2;
+ long theYPos = theScreenSize.y / 2 - theImageSize.y / 2;
+ CRect theWindowRect(theXPos, theYPos, theXPos + theImageSize.x,
+ theYPos + theImageSize.y + theTitleBarHeight);
+
+ // Create the dialog.
+ theReturnValue = CFrameWnd::Create(theWndClass, inTitle, WS_POPUP | WS_CAPTION, theWindowRect,
+ inParent, nullptr, 0, inCreateContext);
+
+ return theReturnValue;
+}
+
+//==============================================================================
+/**
+ * Sends a message to the view that the progress bar needs to be updated to a
+ * new percentage.
+ * @param inPercent New percent complete to be displayed
+ */
+void CProgressPalette::SetProgress(long inPercent)
+{
+ SendMessageToDescendants(WM_STUDIO_LOADPROGRESS, CProgressView::PROGRESSUPDATE_PERCENT,
+ static_cast<LPARAM>(inPercent));
+ Invalidate();
+ UpdateWindow();
+}
+
+//==============================================================================
+/**
+ * Sends a message to the view that the action text (saving/loading) needs to be changed.
+ * @param inFileName New file name to be displayed
+ */
+void CProgressPalette::SetActionText(const Q3DStudio::CString &inText)
+{
+ SendMessageToDescendants(WM_STUDIO_LOADPROGRESS, CProgressView::PROGRESSUPDATE_ACTIONTEXT,
+ reinterpret_cast<LPARAM>(&inText));
+}
+
+//==============================================================================
+/**
+ * Sends a message to the view that the file name being opened needs to be changed.
+ * @param inFileName New file name to be displayed
+ */
+void CProgressPalette::SetFileName(const Q3DStudio::CString &inFileName)
+{
+ SendMessageToDescendants(WM_STUDIO_LOADPROGRESS, CProgressView::PROGRESSUPDATE_FILENAME,
+ reinterpret_cast<LPARAM>(&inFileName));
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.h b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.h
new file mode 100644
index 00000000..7d7cb357
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressPalette.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PROGRESS_PALETTE_H
+#define INCLUDED_PROGRESS_PALETTE_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioPaletteBar.h"
+#include "ProgressCallback.h"
+
+//=============================================================================
+/**
+ * Palette for the loading screen
+ */
+class CProgressPalette : public CStudioDialog, public IProgressCallback
+{
+public:
+ CProgressPalette();
+ virtual ~CProgressPalette();
+ virtual BOOL Create(CString inTitle, CCreateContext *inCreateContext, CWnd *inParent = nullptr);
+ void SetProgress(long inPercent);
+ void SetFileName(const Q3DStudio::CString &inFileName);
+ void SetActionText(const Q3DStudio::CString &inText);
+};
+#endif // INCLUDED_PROGRESS_PALETTE_H \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.cpp b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.cpp
new file mode 100644
index 00000000..8b7e796c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ProgressView.h"
+#include "WndControl.h"
+#include "StudioApp.h"
+#include "ProgressControl.h"
+
+//==============================================================================
+// Message Maps, etc.
+//==============================================================================
+IMPLEMENT_DYNCREATE(CProgressView, CView)
+
+BEGIN_MESSAGE_MAP(CProgressView, CView)
+//{{AFX_MSG_MAP(CProgressView)
+ON_WM_SIZE()
+ON_WM_ERASEBKGND()
+//}}AFX_MSG_MAP
+ON_MESSAGE(WM_STUDIO_INITIALIZE_PALETTES, OnInitializePalettes)
+ON_MESSAGE(WM_STUDIO_LOADPROGRESS, OnUpdateProgress)
+END_MESSAGE_MAP()
+
+//=============================================================================
+/**
+ * Constructor: Protected because the view is always created dynamically.
+ * You must call Initialize() before trying to use this class.
+ */
+CProgressView::CProgressView()
+ : m_WndControl(nullptr)
+ , m_ProgressControl(nullptr)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CProgressView::~CProgressView()
+{
+ if (m_WndControl) {
+ m_WndControl->DestroyWindow();
+ m_WndControl = nullptr;
+
+ SAFE_DELETE(m_ProgressControl);
+ }
+}
+
+//==============================================================================
+/**
+ * Handles the WM_INITIALUPDATE message. Responsible for preparing the view
+ * before it is displayed for the first time.
+ */
+LRESULT CProgressView::OnInitializePalettes(WPARAM, LPARAM)
+{
+ if (!m_WndControl) {
+ m_ProgressControl = new CProgressControl;
+ m_WndControl = new CWndControl(m_ProgressControl);
+ m_ProgressControl->SetName("Progress Control");
+
+ if (!::IsWindow(m_WndControl->m_hWnd))
+ m_WndControl->CreateEx(0, AfxRegisterWndClass(CS_DBLCLKS, LoadCursor(nullptr, IDC_WAIT),
+ (HBRUSH)GetStockObject(BLACK_BRUSH)),
+ L"LoadView", WS_CHILD | WS_VISIBLE | WS_MAXIMIZE,
+ CRect(0, 0, 200, 200), this, 100);
+ }
+
+ return 0;
+}
+
+//==============================================================================
+/**
+ * Handles the WM_STUDIO_LOADPROGRESS message. Changes text displayed on the
+ * load control, depending on what we are trying to update.
+ * @param inwParam Should be an EProgressMessage to indicate what we are updating
+ * @param inlParam Depends on inwParam. If the message type is PROGRESSUPDATE_PERCENT,
+ * this parameter contains a long value indicating the percent. If the message
+ * type is PROGRESSUPDATE_FILENAME, this parameter contains a pointer to an
+ * Q3DStudio::CString, which is the name of the file that we are loading.
+ * @return 0
+ */
+LRESULT CProgressView::OnUpdateProgress(WPARAM inwParam, LPARAM inlParam)
+{
+ EProgressMessage theMessageType = static_cast<EProgressMessage>(inwParam);
+
+ switch (theMessageType) {
+ // Update the percentage completed
+ case PROGRESSUPDATE_PERCENT: {
+ long thePercent = (long)inlParam;
+ m_ProgressControl->SetProgress(thePercent);
+ } break;
+
+ // Update the name of the file being loaded
+ case PROGRESSUPDATE_FILENAME: {
+ Q3DStudio::CString *theName = reinterpret_cast<Q3DStudio::CString *>(inlParam);
+ m_ProgressControl->SetFileName(*theName);
+ } break;
+
+ // Update the text above the file name (loading or saving)
+ case PROGRESSUPDATE_ACTIONTEXT: {
+ Q3DStudio::CString *theText = reinterpret_cast<Q3DStudio::CString *>(inlParam);
+ m_ProgressControl->SetActionText(*theText);
+ } break;
+
+ // Nothing to do in the default case
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+//=============================================================================
+/**
+ * Required by base class but does nothing since all drawing is handled by the
+ * child control.
+ */
+void CProgressView::OnDraw(CDC *inDC)
+{
+ Q_UNUSED(inDC);
+}
+
+//=============================================================================
+/**
+ * Resizes the wnd control to fill the whole view.
+ */
+void CProgressView::OnSize(UINT inType, int inX, int inY)
+{
+ CView::OnSize(inType, inX, inY);
+ if (::IsWindow(m_WndControl->GetSafeHwnd()))
+ m_WndControl->MoveWindow(0, 0, inX, inY);
+}
+
+//==============================================================================
+/**
+ * Tells the view to erase before redrawing. Overridden because erasing
+ * before each draw produces a flashing effect.
+ * @param inDC the DC to erase on.
+ * @return FALSE.
+ */
+BOOL CProgressView::OnEraseBkgnd(CDC *inDC)
+{
+ Q_UNUSED(inDC);
+ return FALSE;
+}
diff --git a/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.h b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.h
new file mode 100644
index 00000000..ed023c0c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Progress/ProgressView.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PROGRESS_VIEW_H
+#define INCLUDED_PROGRESS_VIEW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CWndControl;
+class CProgressControl;
+class CStudioApp;
+
+//=============================================================================
+/**
+ * Windows view encapsulating the splash screen.
+ */
+class CProgressView : public CView
+{
+public:
+ /// Used to pass messages from the palette, down to this view
+ enum EProgressMessage {
+ PROGRESSUPDATE_PERCENT = 0, ///< Inidicates that the percentage displayed should be updated
+ PROGRESSUPDATE_FILENAME, ///< Indicates that the file name displayed should be updated
+ PROGRESSUPDATE_ACTIONTEXT, ///< Indicates that the text above the file name needs to be
+ ///updated
+ };
+
+ virtual void OnDraw(CDC *inDC);
+ virtual LRESULT OnInitializePalettes(WPARAM inwParam, LPARAM inlParam);
+ virtual LRESULT OnUpdateProgress(WPARAM inwParam, LPARAM inlParam);
+
+protected:
+ CWndControl *m_WndControl;
+ CProgressControl *m_ProgressControl;
+
+ CProgressView(); ///< Constructor is protected because you can only create this view dynamically
+ DECLARE_DYNCREATE(CProgressView)
+ virtual ~CProgressView();
+ afx_msg void OnSize(UINT inType, int inX, int inY);
+ afx_msg BOOL OnEraseBkgnd(CDC *inDC);
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // INCLUDED_PROGRESS_VIEW_H \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.cpp b/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.cpp
new file mode 100644
index 00000000..112631e6
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SplashPalette.h"
+#include "SplashControl.h" // Just for getting the control's size; the view owns the real splash control
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CSplashPalette::CSplashPalette()
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSplashPalette::~CSplashPalette()
+{
+}
+
+//==============================================================================
+/**
+ * A wrapper around the CMiniFrameWnd::Create() method.
+ * Simplifies the create parameters for the palette bar. Overridden because
+ * the splash screen is different from all the other palettes.
+ * @return The result from the Create() call (FALSE if unsuccessful)
+ */
+BOOL CSplashPalette::Create(CString inTitle, CCreateContext *inCreateContext,
+ CWnd *inParent /* = nullptr */)
+{
+ CString theWndClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(nullptr, IDC_ARROW));
+ BOOL theReturnValue;
+
+ // Add the title of the bar to the registry location.
+ m_DialogName = inTitle;
+
+ // Get the rectangle that we want this splash screen to occupy in the middle of the screen
+ CPt theScreenSize = GetAvailableDisplaySize();
+ CSplashControl theSplashControl; // Just for getting the control's size; the view owns the real
+ // splash control
+ CPt theImageSize = theSplashControl.GetSize();
+ long theXPos = theScreenSize.x / 2 - theImageSize.x / 2;
+ long theYPos = theScreenSize.y / 2 - theImageSize.y / 2;
+ CRect theWindowRect(theXPos, theYPos, theXPos + theImageSize.x, theYPos + theImageSize.y);
+
+ // Create the dialog.
+ DWORD theFlags;
+#ifdef DEBUG
+ theFlags = WS_EX_TOOLWINDOW;
+#else
+ theFlags = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+#endif
+ // Create the palette bar.
+ theReturnValue = CFrameWnd::CreateEx(theFlags, theWndClass, inTitle, WS_POPUP, theWindowRect,
+ inParent, 0, inCreateContext);
+
+ return theReturnValue;
+}
diff --git a/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.h b/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.h
new file mode 100644
index 00000000..5f11af0c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Splash/SplashPalette.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SPLASH_PALETTE_H
+#define INCLUDED_SPLASH_PALETTE_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioPaletteBar.h"
+
+//=============================================================================
+/**
+ * Palette for the splash screen
+ */
+class CSplashPalette : public CStudioDialog /*CStudioPaletteBar*/
+{
+public:
+ CSplashPalette();
+ virtual ~CSplashPalette();
+ virtual BOOL Create(CString inTitle, CCreateContext *inCreateContext, CWnd *inParent = nullptr);
+};
+#endif // INCLUDED_SPLASH_PALETTE_H \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.cpp b/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.cpp
new file mode 100644
index 00000000..fd78e604
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "SplashView.h"
+#include "WidgetControl.h"
+#include "StudioApp.h"
+#include "SplashControl.h"
+
+//=============================================================================
+/**
+ * Constructor: Protected because the view is always created dynamically.
+ * You must call Initialize() before trying to use this class.
+ */
+CSplashView::CSplashView(QWidget *parent)
+ : QWidget(parent, Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint)
+ , m_WndControl(nullptr)
+ , m_SplashControl(nullptr)
+{
+ OnInitializePalettes();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CSplashView::~CSplashView()
+{
+ delete m_WndControl;
+ m_WndControl = nullptr;
+
+ delete m_SplashControl;
+}
+
+//==============================================================================
+/**
+ * Handles the WM_INITIALUPDATE message. Responsible for preparing the view
+ * before it is displayed for the first time.
+ */
+void CSplashView::OnInitializePalettes()
+{
+ if (!m_WndControl) {
+ m_SplashControl = new CSplashControl;
+ m_WndControl = new WidgetControl(m_SplashControl, this);
+ m_SplashControl->SetName("Splash Control");
+
+ setFixedSize(m_WndControl->sizeHint());
+ }
+}
+
+//=============================================================================
+/**
+ * Resizes the wnd control to fill the whole view.
+ */
+void CSplashView::resizeEvent(QResizeEvent *event)
+{
+ Q_UNUSED(event);
+ m_WndControl->setGeometry(rect());
+}
diff --git a/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.h b/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.h
new file mode 100644
index 00000000..28498e95
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Palettes/Splash/SplashView.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SPLASH_VIEW_H
+#define INCLUDED_SPLASH_VIEW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include <QWidget>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class WidgetControl;
+class CSplashControl;
+
+//=============================================================================
+/**
+ * Windows view encapsulating the splash screen.
+ */
+class CSplashView : public QWidget
+{
+public:
+ explicit CSplashView(QWidget *parent = nullptr);
+ ~CSplashView();
+
+protected:
+ void resizeEvent(QResizeEvent *event) override;
+
+public:
+ void OnInitializePalettes();
+
+protected:
+ WidgetControl *m_WndControl;
+ CSplashControl *m_SplashControl;
+};
+
+#endif // INCLUDED_SPLASH_VIEW_H
diff --git a/src/Authoring/Studio/_Win/Studio/stdafx.cpp b/src/Authoring/Studio/_Win/Studio/stdafx.cpp
new file mode 100644
index 00000000..b6cac561
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Studio/stdafx.cpp
@@ -0,0 +1,33 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// stdafx.cpp : source file that includes just the standard includes
+// Studio.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h" \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Studio/stdafx.h b/src/Authoring/Studio/_Win/Studio/stdafx.h
new file mode 100644
index 00000000..8fb2d134
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Studio/stdafx.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef __cplusplus
+#pragma once
+
+#pragma warning(disable : 4819)
+
+#include "UICMacros.h"
+
+//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#define VC_EXTRALEAN // Exclude morerarely-used stuff from Windows headers
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
+#define _AFX_ALL_WARNINGS // turns off MFC's hiding of some common and often safely ignored warning
+ // messages
+
+#ifdef DEBUG
+#define CHECK_BOUNDS // CHECK_BOUNDS affects the way an item is retrieved from container objects
+#endif
+
+#if _MSC_VER >= 1400
+#if defined _M_IX86
+#pragma comment( \
+ linker, \
+ "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_IA64
+#pragma comment( \
+ linker, \
+ "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#elif defined _M_X64
+#pragma comment( \
+ linker, \
+ "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#else
+#pragma comment( \
+ linker, \
+ "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+#endif
+#endif
+
+//==============================================================================
+// Standard Includes
+//==============================================================================
+#include <stdio.h> // Standard includes MUST come first
+#include <stdlib.h>
+#include <float.h>
+#include <math.h>
+
+//==============================================================================
+// STL Includes
+//==============================================================================
+#pragma warning(push, 3) // Temporarily pop to warning level 3 while including standard headers
+#pragma warning(disable : 4018) // Disable mismatched < STL warning
+#include <vector>
+#include <map>
+#include <deque>
+#include <string>
+#include <stack>
+#include <set>
+#include <list>
+#include <utility>
+#include <algorithm>
+#include <stdexcept>
+#include <limits>
+#pragma warning(pop) // Pop out to previous warning level (probably level 4)
+
+//==============================================================================
+// MFC Includes
+//==============================================================================
+#pragma warning(push, 1)
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdisp.h> // MFC Automation classes
+#include <afxpriv.h> // AFX Windows Messages (WM_INITIALUPDATE for example)
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#include <atlbase.h>
+#include <atlcom.h>
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+//==============================================================================
+// ATL Includes
+//==============================================================================
+#include "atlbase.h" // For CrystalEdit
+#include "afxtempl.h" // For CrystalEdit
+#include "afxole.h" // For CrystalEdit
+#pragma warning(pop)
+
+// I put these in to help with some of the data model compile times.
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#pragma warning(disable : 4512)
+#pragma warning(disable : 4702)
+#pragma warning(disable : 4996)
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/variant.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/utility.hpp>
+#include <boost/bind.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/signals.hpp>
+#include <boost/signals/connection.hpp>
+#pragma warning(pop)
+
+//==============================================================================
+// Disable certain warnings since warnings are errors
+//==============================================================================
+#pragma warning(disable : 4100) // unreferenced formal parameter
+#pragma warning(disable : 4702) // Unreachable code
+#pragma warning(disable : 4290) // C++ Exception Specification ignored
+#pragma warning(disable : 4514) // Unreferenced inline function
+#pragma warning(disable : 4121) // Alignment of member sensative to packing
+#pragma warning(disable : 4512) // Assignment not generated
+#pragma warning(disable : 4355) // This used in member initializer list.
+#pragma warning(disable : 4127) // conditional expression is constant
+#pragma warning(disable : 4189) // local variable is initialized but not referenced
+
+//==============================================================================
+// Common Includes
+//==============================================================================
+#include "GUIDUtilities.h"
+#include "UICId.h"
+
+//==============================================================================
+// Application Includes (Aug '08 accumulated most popular include files)
+//==============================================================================
+#include "UICString.h"
+#include "UICMath.h"
+#include "StringLoader.h"
+#include "StudioException.h"
+#include "UICExceptions.h"
+#include "Exceptions.h"
+#include "UICObjectCounter.h"
+#include "STLHelpers.h"
+#include "Pt.h"
+#include "Rct.h"
+#include "SafeArray.h"
+#include "PlatformMacros.h"
+#include "PlatformTypes.h"
+#include "Multicaster.h"
+#include "GenericFunctor.h"
+#include "CColor.h"
+#include "InputStream.h"
+#include "OutputStream.h"
+#include "StudioUtils.h"
+#include "StudioDefs.h"
+#include "StudioErrorIDs.h"
+#include "Strings.h"
+#include "StudioPreferences.h"
+#include "Renderer.h"
+#include "StudioObjectTypes.h"
+#include "HotKeys.h"
+#include "Dispatch.h"
+#include "Cmd.h"
+#include "MasterP.h"
+#include "Dialogs.h"
+#include "Views.h"
+#include "ResourceCache.h"
+#include "CmdBatch.h"
+#include "DispatchListeners.h"
+#include "UICFile.h"
+#include "StudioConst.h"
+#include "resource.h"
+#include "StudioProjectSettings.h"
+#include "Preferences.h"
+#include "StudioClipboard.h"
+#include "DropTarget.h"
+#include "ContextMenu.h"
+#include "TextButton.h"
+
+#include "StudioPrefixWin32.h"
+
+//==============================================================================
+// Common Includes
+//==============================================================================
+#include "Resource.h"
+
+//==============================================================================
+// OpenGL Includes
+//==============================================================================
+#include <GL/gl.h>
+
+#define UIC_LITTLE_ENDIAN
+
+#define SAFE_DELETE(ptr) \
+ if ((ptr) != nullptr) { \
+ delete (ptr); \
+ (ptr) = nullptr; \
+ }
+#endif
diff --git a/src/Authoring/Studio/_Win/UI/ContextMenu.cpp b/src/Authoring/Studio/_Win/UI/ContextMenu.cpp
new file mode 100644
index 00000000..6aedc806
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ContextMenu.cpp
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2001 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "ContextMenu.h"
+#include "StringLoader.h"
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CContextMenu::CContextMenu()
+ : m_SelectedOption(-1)
+ , m_IndexBase(0)
+{
+ m_Menu = ::CreatePopupMenu();
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CContextMenu::~CContextMenu()
+{
+ ::DestroyMenu(m_Menu);
+}
+
+//=============================================================================
+/**
+ * Display the popup menu and perform the actions for whatever choice the
+ * user makes.
+ * @param inLocation the screen coordinates of the location to display this menu.
+ * @param inParentWindow the window to attach this menu to.
+ */
+long CContextMenu::DoPopup(CPt inLocation, HWND inParentWindow)
+{
+ // Allow derived classes to update the menu state
+ Update();
+
+ ::SetCursor(::LoadCursor(nullptr, IDC_ARROW));
+
+ // Display the menu and get the user's input
+ unsigned int theCommandID = (unsigned int)::TrackPopupMenuEx(
+ m_Menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON,
+ inLocation.x, inLocation.y, inParentWindow, nullptr);
+
+ // If an option was selected then get the performer for that option and execute it.
+ m_SelectedOption = theCommandID - 1;
+ if (m_SelectedOption >= 0) {
+ CContextMenuPerformer *thePerformer = GetSelectedMenuPerformer(m_SelectedOption);
+ if (thePerformer != nullptr) {
+ thePerformer->OnOptionSelected();
+ }
+ }
+
+ return m_SelectedOption;
+}
+
+//=============================================================================
+/**
+ * Append a separator to the list of items in this menu.
+ */
+void CContextMenu::AddSeparator()
+{
+ ::AppendMenu(m_Menu, MF_SEPARATOR, 0, nullptr);
+}
+
+//=============================================================================
+/**
+ * Append the option onto the end of this menu.
+ * @param inOptionNameStringID ID indicating the display name for the option.
+ * @param inPerformer the performer to be called if this option is selected.
+ * @param inIsEnabled false if the option is to be grayed out and disabled.
+ */
+void CContextMenu::AddOption(int inOptionNameStringID, CContextMenuPerformer *inPerformer,
+ bool inIsEnabled /* = true */)
+{
+ Q3DStudio::CString theOptionName(::LoadResourceString(inOptionNameStringID));
+ AddOption(theOptionName, inPerformer, inIsEnabled);
+}
+
+//=============================================================================
+/**
+ * Append the option onto the end of this menu.
+ * @param inOptionName the display name for the option.
+ * @param inPerformer the performer to be called if this option is selected.
+ * @param inIsEnabled false if the option is to be grayed out and disabled.
+ */
+void CContextMenu::AddOption(const Q3DStudio::CString &inOptionName,
+ CContextMenuPerformer *inPerformer, bool inIsEnabled /*= true */)
+{
+ size_t theIndex = m_IndexBase; // start from a base, so that submenu items can be distinguished
+
+ long theFlags = MF_STRING;
+ if (!inIsEnabled)
+ theFlags |= MF_GRAYED;
+
+ m_Performers.push_back(inPerformer);
+ m_Options.push_back(inOptionName);
+ CContextMenu *theSubMenu = (inPerformer ? inPerformer->GetSubMenu() : nullptr);
+ if (!theSubMenu)
+ theIndex += m_Performers.size();
+ else // if performer has a submenu.
+ {
+ theIndex = (UINT)theSubMenu->GetMenuHandle();
+ theFlags |= MF_POPUP;
+ }
+
+ ::AppendMenuW(m_Menu, theFlags, theIndex, inOptionName);
+}
+
+//=============================================================================
+/**
+ * Remove all options from the menu.
+ */
+void CContextMenu::Clear()
+{
+ long theCount = ::GetMenuItemCount(m_Menu);
+ for (long theIndex = theCount - 1; theIndex >= 0; theIndex--)
+ ::DeleteMenu(m_Menu, theIndex, MF_BYPOSITION);
+
+ m_Performers.clear();
+ m_Options.clear();
+}
+
+//=============================================================================
+/**
+ * Delete all the performers on this object.
+ * This is an ease of use method for subclasses if they allocated their
+ * performers on the heap. This just deletes all of them and clears the list.
+ */
+void CContextMenu::DeletePerformers()
+{
+ TPerformerList::iterator thePos = m_Performers.begin();
+ for (; thePos != m_Performers.end(); ++thePos) {
+ delete (*thePos);
+ }
+ m_Performers.clear();
+}
+
+//=============================================================================
+/**
+ * Adds or removes a check mark from the specified item.
+ * @param inIndex Index of context menu item to be edited
+ * @param inChecked true to set a check, false to remove a check
+ */
+void CContextMenu::SetCheck(long inIndex, bool inChecked)
+{
+ long theFlags = MF_BYPOSITION;
+ theFlags |= inChecked ? MF_CHECKED : MF_UNCHECKED;
+
+ long theActualIndex = inIndex;
+ for (long thePos = 0; thePos < (long)::GetMenuItemCount(m_Menu) && thePos <= inIndex;
+ ++thePos) {
+ // Account for separators that doesn't "contribute" to the number of performers.
+ MENUITEMINFO theMenuInfo;
+ ZeroMemory(&theMenuInfo, sizeof(MENUITEMINFO));
+ theMenuInfo.cbSize = sizeof(MENUITEMINFO);
+ theMenuInfo.fMask = MIIM_TYPE;
+ ::GetMenuItemInfo(m_Menu, thePos, TRUE, &theMenuInfo);
+ if (theMenuInfo.fType & MFT_SEPARATOR)
+ ++theActualIndex;
+ }
+ ::CheckMenuItem(m_Menu, theActualIndex, theFlags);
+}
+
+//=============================================================================
+/**
+ * Set all the menu items to being unchecked.
+ * This will uncheck any items that are currently checked.
+ */
+void CContextMenu::ClearChecked()
+{
+ for (long thePos = 0; thePos < (long)::GetMenuItemCount(m_Menu); ++thePos)
+ ::CheckMenuItem(m_Menu, thePos, MF_BYPOSITION | MF_UNCHECKED);
+}
+
+//=============================================================================
+/**
+ * Gets the string located at a certain index in the context menu list. Index
+ * is zero-based.
+ * @param inIndex zero-based index of the item whose string you want
+ * @return the string at the specified index
+ */
+Q3DStudio::CString CContextMenu::GetStringAt(long inIndex)
+{
+ Q3DStudio::CString theString;
+ if (inIndex >= 0 && inIndex < (long)m_Options.size())
+ theString = m_Options.at(inIndex);
+ return theString;
+}
+
+//=============================================================================
+/**
+ * @return the number of items in this context menu
+ */
+long CContextMenu::GetItemCount()
+{
+ return (long)m_Options.size();
+}
+
+//=============================================================================
+/**
+ * Override this to set enable state of options before the menu pops up
+ */
+void CContextMenu::Update()
+{
+}
+
+static inline long GetEnableMenuItemFlags(bool inEnabledState)
+{
+ return MF_BYPOSITION | (inEnabledState ? (MF_ENABLED) : (MF_GRAYED));
+}
+
+void CContextMenu::EnableOptionByIndex(long inIndex, bool inEnabledState /*= true*/)
+{
+ if (inIndex >= 0 && inIndex < (long)m_Options.size())
+ ::EnableMenuItem(m_Menu, inIndex, GetEnableMenuItemFlags(inEnabledState));
+}
+
+void CContextMenu::EnableOption(const Q3DStudio::CString &inOptionName, bool inEnabledState)
+{
+ long theCount = (long)m_Options.size();
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ if (m_Options[theIndex] == inOptionName) {
+ ::EnableMenuItem(m_Menu, theIndex, GetEnableMenuItemFlags(inEnabledState));
+ break;
+ }
+ }
+}
+
+void CContextMenu::EnableOption(unsigned int inOptionNameStringID, bool inEnabledState)
+{
+ Q3DStudio::CString theOptionName(::LoadResourceString(inOptionNameStringID));
+ EnableOption(theOptionName, inEnabledState);
+}
+
+//=============================================================================
+/**
+ * Find the menu performer associated with this index, recursing down any submenu if necessary.
+ * @param inIndex index of the menu performer
+ */
+CContextMenuPerformer *CContextMenu::GetSelectedMenuPerformer(long inIndex)
+{
+ if (inIndex - m_IndexBase < static_cast<long>(m_Performers.size())) {
+ m_SelectedOption =
+ inIndex - m_IndexBase; // update this for any references to the selected option
+ return m_Performers.at(m_SelectedOption);
+ }
+ // if not found, recurse down submenus
+ CContextMenuPerformer *theResult = nullptr;
+ TPerformerList::iterator theIter = m_Performers.begin();
+ for (; theIter != m_Performers.end() && !theResult; ++theIter) {
+ CContextMenu *theSubMenu = (*theIter)->GetSubMenu();
+ if (theSubMenu)
+ theResult = theSubMenu->GetSelectedMenuPerformer(inIndex);
+ }
+ return theResult;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/UI/ControlButton.cpp b/src/Authoring/Studio/_Win/UI/ControlButton.cpp
new file mode 100644
index 00000000..d99ce639
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ControlButton.cpp
@@ -0,0 +1,471 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "resource.h"
+#include "ControlButton.h"
+#include "MemoryDC.h"
+#include "WinUtils.h"
+
+//==============================================================================
+/*
+ * Constructor: Initializes the object.
+ */
+CControlButton::CControlButton()
+ : m_ImageUp(nullptr)
+ , m_ImageDown(nullptr)
+ , m_ImageDisabled(nullptr)
+ , m_ImageActive(nullptr)
+ , m_ImageMouseTrack(nullptr)
+ , m_ImageLimbo(nullptr)
+ , m_ActiveFlag(FALSE)
+ , m_HotTrackFlag(FALSE)
+ , m_MouseOverFlag(FALSE)
+ , m_ActiveDepressedFlag(FALSE)
+ , m_LimboEnabled(FALSE)
+ , m_LimboFlag(FALSE)
+ , m_SimpleFrame(TRUE)
+ , m_DrawFrame(TRUE)
+ , m_DepressFlag(FALSE)
+ , m_3DColorsFlag(FALSE)
+{
+ m_ImageOffset = CPoint(0, 0);
+ m_FillColor = GetSysColor(COLOR_WINDOW);
+ m_TransparentColor = RGB(255, 0, 255);
+ m_FillColor = GetSysColor(COLOR_3DFACE);
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+CControlButton::~CControlButton()
+{
+}
+
+BEGIN_MESSAGE_MAP(CControlButton, CButton)
+//{{AFX_MSG_MAP(CControlButton)
+ON_WM_MOUSEMOVE()
+ON_WM_ERASEBKGND()
+//}}AFX_MSG_MAP
+ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * Method to create the button
+ *
+ * @param inStyle Window style for the button
+ * @param inRect Bounding rectangle for the button
+ * @param inParentWnd Parent window for the button
+ * @param inID Child window ID for the button
+ * @param inDepressFlag
+ * @param inLimboEnabled Set to TRUE if this is a tri-state button (defaults to false).
+ *The third state is referred to as "limbo".
+ * Then use SetLimbo() to enter and leave the limbo state.
+ *
+ * @return Returns TRUE if the button was created
+ */
+BOOL CControlButton::Create(DWORD inStyle, const RECT &inRect, CWnd *inParentWnd, short inID,
+ BOOL inDepressFlag, BOOL inLimboEnabled)
+{
+ m_DepressFlag = inDepressFlag;
+ m_LimboEnabled = inLimboEnabled;
+
+ return CButton::Create(L"", inStyle /*| WS_BORDER*/ | BS_OWNERDRAW, inRect, inParentWnd, inID);
+}
+
+//==============================================================================
+/**
+ * Adds images to the button for the different button states.
+ * @param inTransparentColor RGB value of the transparency color used within the
+ *bitmaps. If there is no transparency color,
+ * set this to a value that does not appear in any of
+ *the images.
+ * @param inImageUp Image for the up state
+ * @param inImageDown Image for the down state
+ * @param inImageDisabled Image for the disabled state
+ * @param inImageActive Image for the active state
+ * @param inImageMouseTrack Image for hot mouse tracking
+ * @param inImageLimbo Image for "limbo" state (used for tri-state buttons)
+ */
+void CControlButton::AddImages(COLORREF inTransparentColor, short inImageUp, short inImageDown,
+ short inImageDisabled, short inImageActive, short inImageMouseTrack,
+ short inImageLimbo)
+{
+ if (inImageUp != 0)
+ m_ImageUp = (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageUp),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ // You always need to have at least one valid image for a button
+ ASSERT(m_ImageUp != nullptr);
+
+ if (inImageDown != 0)
+ m_ImageDown = (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageDown),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ if (inImageDisabled != 0)
+ m_ImageDisabled =
+ (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageDisabled),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ if (inImageActive != 0)
+ m_ImageActive = (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageActive),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ if (inImageMouseTrack != 0)
+ m_ImageMouseTrack =
+ (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageMouseTrack),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ if (inImageLimbo != 0)
+ m_ImageLimbo = (HBITMAP)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(inImageLimbo),
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+
+ m_TransparentColor = inTransparentColor;
+
+ if (IsWindow(m_hWnd))
+ this->Redraw();
+}
+
+//==============================================================================
+/**
+ * SetImageOffset
+ *
+ * Sets an image drawing offset
+ *
+ * @param inOffset Offset for image from (0,0)
+ */
+void CControlButton::SetImageOffset(CPoint inOffset)
+{
+ m_ImageOffset = inOffset;
+}
+
+//==============================================================================
+/**
+ * SetActiveDepressed
+ *
+ * Sets the active depressed flag. If TRUE and this control is set to active,
+ * then the image will be displayed in a depressed state (for toggle items).
+ *
+ * @param inActiveDepressedFlag TRUE if the active state should be depressed
+ */
+void CControlButton::SetActiveDepressed(BOOL inActiveDepressedFlag)
+{
+ m_ActiveDepressedFlag = inActiveDepressedFlag;
+}
+
+//==============================================================================
+/**
+ * SetActive
+ *
+ * Sets the active state for the button
+ *
+ * @param inActiveState Active state for the button
+ */
+void CControlButton::SetActive(BOOL inActiveFlag)
+{
+ m_ActiveFlag = inActiveFlag;
+
+ if (IsWindow(m_hWnd))
+ this->Redraw();
+}
+
+//==============================================================================
+/**
+ * GetActive
+ *
+ * Gets the active state for the button
+ *
+ * @param None
+ *
+ * @return Active state for the button
+ */
+BOOL CControlButton::GetActive()
+{
+ return m_ActiveFlag;
+}
+
+//==============================================================================
+/**
+ * DrawItem
+ *
+ * Handle the owner drawn button
+ *
+ * @param inDrawItemStruct DRAWITEMSTRUCT structure
+ */
+void CControlButton::DrawItem(LPDRAWITEMSTRUCT inDrawItemStruct)
+{
+ CDC *theDC;
+ CMemoryDC theMemDC;
+ BOOL theEnabledFlag;
+ BOOL theDownStateFlag;
+ HBITMAP theBitmap = m_ImageUp;
+ CRect theRect;
+
+ theDC = GetDC();
+
+ // Get the bounding client rectangle
+ GetClientRect(theRect);
+
+ theMemDC.Create(theDC, theRect);
+
+ theDownStateFlag = (inDrawItemStruct->itemState & ODS_SELECTED);
+
+ // Is this window enabled?
+ theEnabledFlag = IsWindowEnabled();
+
+ if (m_ActiveFlag)
+ theBitmap = m_ImageActive;
+
+ if (m_MouseOverFlag)
+ if (m_ImageMouseTrack != 0)
+ theBitmap = m_ImageMouseTrack;
+
+ if (!theEnabledFlag) {
+ // Disabled
+ if (m_ImageDisabled != 0)
+ theBitmap = m_ImageDisabled;
+ } else {
+ // Window is enabled
+ if (theDownStateFlag)
+ if (m_ImageDown != 0)
+ theBitmap = m_ImageDown;
+ }
+
+ // If the button has limbo enabled, and it in limbo state, it overrides any previous button
+ // settings
+ if (m_LimboEnabled && m_LimboFlag)
+ if (m_ImageLimbo != 0)
+ theBitmap = m_ImageLimbo;
+
+ // Fill the background color
+ theMemDC.FillSolidRect(theRect, m_FillColor);
+
+ // Draw the bitmap if it exists
+ if (theBitmap)
+ CWinUtils::DrawTransparentBitmap(
+ &theMemDC, m_ImageOffset.x + theDownStateFlag * m_DepressFlag,
+ m_ImageOffset.y + theDownStateFlag * m_DepressFlag, theBitmap, m_TransparentColor);
+
+ // Hot tracking the mouse over the button?
+ if (m_HotTrackFlag) {
+ if (m_DepressFlag) {
+ if (theDownStateFlag)
+ DrawButtonFrame((CDC *)&theMemDC, &theRect, TRUE);
+ else if (m_MouseOverFlag)
+ DrawButtonFrame((CDC *)&theMemDC, &theRect, FALSE);
+ } else {
+ if (m_MouseOverFlag)
+ DrawButtonFrame((CDC *)&theMemDC, &theRect, FALSE);
+ }
+ } else {
+ // Draw the frame around the button
+ if (!m_ActiveFlag)
+ DrawButtonFrame((CDC *)&theMemDC, &theRect, theDownStateFlag * m_DepressFlag);
+ }
+
+ theMemDC.Release();
+
+ ReleaseDC(theDC);
+}
+
+//==============================================================================
+/**
+ * DrawButtonFrame
+ *
+ * Draw the button frame
+ *
+ * @param inDC Device context for drawing
+ * @param inRect Bounding rectangle for the button frame
+ * @param inDownFlag If TRUE, the button is down
+ */
+void CControlButton::DrawButtonFrame(CDC *inDC, CRect *inRect, BOOL inDownFlag)
+{
+ if (m_DrawFrame) {
+ COLORREF theShadowColor = GetSysColor(COLOR_3DSHADOW);
+ COLORREF theHiliteColor = GetSysColor(COLOR_3DHIGHLIGHT);
+
+ if (inDownFlag) {
+ theShadowColor = GetSysColor(COLOR_3DHIGHLIGHT);
+ theHiliteColor = GetSysColor(COLOR_3DSHADOW);
+ }
+
+ CRect theFrameRect;
+
+ theFrameRect.CopyRect(inRect);
+
+ if (m_SimpleFrame) {
+ inDC->Draw3dRect(&theFrameRect, theHiliteColor, theShadowColor);
+ } else {
+ if (inDownFlag) {
+ theFrameRect.right--;
+ theFrameRect.bottom--;
+
+ inDC->Draw3dRect(&theFrameRect, theHiliteColor, theShadowColor);
+ } else {
+ // the button is up
+ theFrameRect.right--;
+ theFrameRect.bottom--;
+
+ inDC->Draw3dRect(&theFrameRect, theHiliteColor, theShadowColor);
+
+ inDC->MoveTo(theFrameRect.right, theFrameRect.top);
+ inDC->LineTo(theFrameRect.right, theFrameRect.bottom);
+ inDC->LineTo(theFrameRect.left, theFrameRect.bottom);
+ }
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * SetHotTrack
+ *
+ * Set the hot track (mouse over) flag
+ *
+ * @param inHotTrackFlag Hot tracking flag
+ */
+void CControlButton::SetHotTrack(BOOL inHotTrackFlag)
+{
+ m_HotTrackFlag = inHotTrackFlag;
+
+ if (IsWindow(m_hWnd))
+ this->Redraw();
+}
+
+//==============================================================================
+/**
+ * OnMouseMove
+ *
+ * Handle the WM_MOUSEMOVE message
+ *
+ * @param inFlags Flags for WM_MOUSEMOVE
+ * @param inPoint Cursor position in client coordinates
+ */
+void CControlButton::OnMouseMove(UINT inFlags, CPoint inPoint)
+{
+ CButton::OnMouseMove(inFlags, inPoint);
+
+ if (!m_MouseOverFlag) {
+ this->TrackMouse();
+ m_MouseOverFlag = TRUE;
+
+ this->Redraw();
+ }
+}
+
+//==============================================================================
+/**
+ * OnMouseMove
+ *
+ * Handle the WM_MOUSELEAVE message
+ *
+ * @param inwParam WPARAM for the WM_MOUSELEAVE message
+ * @param inlParam LPARAM for the WM_MOUSELEAVE message
+ *
+ * @param Return code - returns TRUE if this message is processed.
+ */
+LRESULT CControlButton::OnMouseLeave(WPARAM inwParam, LPARAM inlParam)
+{
+ Q_UNUSED(inwParam);
+ Q_UNUSED(inlParam);
+
+ m_MouseOverFlag = FALSE;
+
+ this->TrackMouse(FALSE);
+
+ this->Redraw();
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * TrackMouse
+ *
+ * Register for the WM_MOUSELEAVE message to determine when
+ * the mouse leaves this button.
+ *
+ * @param inTrackEnableFlag - TRUE/FALSE depending on whether we want to track the mouse
+ *event state.
+ */
+void CControlButton::TrackMouse(BOOL inTrackEnableFlag)
+{
+ TRACKMOUSEEVENT theTrackMouseEvent;
+
+ memset(&theTrackMouseEvent, 0, sizeof(theTrackMouseEvent));
+
+ theTrackMouseEvent.cbSize = sizeof(theTrackMouseEvent);
+ theTrackMouseEvent.dwFlags = TME_LEAVE;
+ if (!inTrackEnableFlag)
+ theTrackMouseEvent.dwFlags |= TME_CANCEL;
+ theTrackMouseEvent.hwndTrack = m_hWnd;
+
+ _TrackMouseEvent(&theTrackMouseEvent);
+}
+
+//==============================================================================
+/**
+ * Redraw
+ *
+ * Invalidate and update the button
+ *
+ * @param None
+ */
+void CControlButton::Redraw()
+{
+ // Invalidate and update the window
+ InvalidateRect(nullptr, TRUE);
+ UpdateWindow();
+}
+
+//==============================================================================
+/**
+ * Handles the WM_ERASEBKGND message. This function is overridden so that we
+ * can prevent the background from being erased. When we draw the button, the
+ * background is filled in with the appropriate color. Actually erasing the
+ * background before a draw was producing flickering.
+ *
+ * @param pDC Not used.
+ */
+BOOL CControlButton::OnEraseBkgnd(CDC *pDC)
+{
+ Q_UNUSED(pDC);
+ return 1;
+}
diff --git a/src/Authoring/Studio/_Win/UI/ControlButton.h b/src/Authoring/Studio/_Win/UI/ControlButton.h
new file mode 100644
index 00000000..6f168cb2
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ControlButton.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_CONTROLBUTTON_H__6B61A743_FB7E_11D4_A87A_005004D48D91__INCLUDED_)
+#define AFX_CONTROLBUTTON_H__6B61A743_FB7E_11D4_A87A_005004D48D91__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+/////////////////////////////////////////////////////////////////////////////
+// CControlButton window
+
+class CControlButton : public CButton
+{
+ // Construction
+public:
+ CControlButton();
+
+ // Attributes
+public:
+protected:
+ HBITMAP m_ImageUp, m_ImageDown, m_ImageDisabled, m_ImageActive, m_ImageMouseTrack,
+ m_ImageLimbo; // resource bitmap values for various button states
+ BOOL m_DepressFlag, m_3DColorsFlag; // drawing flags
+ BOOL m_ActiveFlag; // TRUE if the button is active (ie - selected)
+ BOOL m_ActiveDepressedFlag; // TRUE if the active state should be drawn depressed
+ CPoint m_ImageOffset; // Image offset from (0,0)
+ BOOL m_MouseOverFlag; // TRUE if the mouse is currently over this button
+ BOOL m_HotTrackFlag; // TRUE if the mouse should be hot-tracked
+ BOOL m_SimpleFrame; // TRUE if the button is a simple frame
+ BOOL m_DrawFrame; // TRUE if the frame around the button should be drawn
+ BOOL m_LimboEnabled; ///< TRUE if this button is capable of being in limbo (decided at creation
+ ///time)
+ BOOL m_LimboFlag; ///< TRUE if the button is currently in limbo state
+ COLORREF m_FillColor;
+ COLORREF m_TransparentColor;
+
+ // Operations
+public:
+ void AddImages(COLORREF inTransparentColor, short inImageUp, short inImageDown = 0,
+ short inImageDisabled = 0, short inImageActive = 0, short inImageMouseTrack = 0,
+ short inImageLimbo = 0);
+
+ BOOL Create(DWORD inStyle, const RECT &inRect, CWnd *inParentWnd, short inID,
+ BOOL inDepressFlag = TRUE, BOOL inTriState = FALSE);
+
+ BOOL GetActive();
+
+ void SetActive(BOOL inActiveFlag);
+ void SetActiveDepressed(BOOL inActiveDepressedFlag);
+ /// Sets the "limbo" state of the button. Usefully for making tri-state buttons. Limbo state
+ /// only works if the limbo flag was set during creation.
+ void SetLimbo(BOOL inLimboFlag) { m_LimboFlag = inLimboFlag; }
+ /// Returns TRUE if the button is in "limbo" state
+ BOOL GetLimbo() { return m_LimboFlag; }
+ void SetHotTrack(BOOL inHotTrackFlag);
+ void SetImageOffset(CPoint inOffset);
+ void SetDrawFrame(BOOL inDrawFrame) { m_DrawFrame = inDrawFrame; }
+ /// Use this function to manually set the fill color behind the button. Use this if you notice
+ /// that the wrong color is showing up.
+ void SetFillColor(COLORREF inColor) { m_FillColor = inColor; }
+protected:
+ void DrawButtonFrame(CDC *inDC, CRect *inRect, BOOL inDownFlag);
+ void Redraw();
+ void TrackMouse(BOOL inTrackEnableFlag = TRUE);
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CControlButton)
+public:
+ virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ virtual ~CControlButton();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CControlButton)
+ afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+ //}}AFX_MSG
+ afx_msg LRESULT OnMouseLeave(WPARAM inwParam, LPARAM inlParam);
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_CONTROLBUTTON_H__6B61A743_FB7E_11D4_A87A_005004D48D91__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/CrashDlg.cpp b/src/Authoring/Studio/_Win/UI/CrashDlg.cpp
new file mode 100644
index 00000000..13277385
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/CrashDlg.cpp
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "MainFrm.h"
+#include "CrashDlg.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CCrashDlg dialog
+
+CCrashDlg::CCrashDlg(CWnd *pParent /*=nullptr*/)
+ : CDialog(CCrashDlg::IDD, pParent)
+ , m_Filename(_T(""))
+{
+ //{{AFX_DATA_INIT(CCrashDlg)
+ m_Header = _T("");
+ //}}AFX_DATA_INIT
+
+ m_MediumFont2.CreatePointFont(88, CString(CStudioPreferences::GetFontFaceName()));
+
+ m_Color_Background = CStudioPreferences::GetDarkBaseColor();
+ m_Color_Text = CStudioPreferences::GetMasterColor();
+ m_Color_Gray = CStudioPreferences::GetNormalColor();
+ m_Color_Dark = CStudioPreferences::GetInactiveColor();
+}
+
+void CCrashDlg::DoDataExchange(CDataExchange *pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCrashDlg)
+ DDX_Control(pDX, IDC_CRASHICON, m_CrashIcon);
+ DDX_Text(pDX, IDC_HEADER, m_Header);
+ DDX_Text(pDX, IDC_HEADER2, m_FilenameSaved);
+ //}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CCrashDlg, CDialog)
+//{{AFX_MSG_MAP(CCrashDlg)
+ON_BN_CLICKED(IDEXIT, OnExit)
+//}}AFX_MSG_MAP
+// ON_STN_CLICKED(IDC_HEADER, &CCrashDlg::OnStnClickedHeader)
+ON_WM_ERASEBKGND()
+ON_WM_CTLCOLOR()
+ON_WM_DESTROY()
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * Set the description of the crash.
+ * @param inErrorMessage the error message.
+ */
+//==============================================================================
+void CCrashDlg::SetErrorMessage(CString inErrorMessage)
+{
+ m_ErrorMessage = inErrorMessage;
+}
+
+//==============================================================================
+/**
+ * Set the project filename saved
+ * @param inFilename the filename of the crashed project
+ */
+//==============================================================================
+void CCrashDlg::SetFilename(CString inFilename)
+{
+ m_Filename = inFilename;
+}
+
+//==============================================================================
+/**
+ * Set the stack trace for the crash.
+ * @param inStackTrace the stack trace.
+ */
+//==============================================================================
+void CCrashDlg::SetStackTrace(CString inStackTrace)
+{
+ m_StackTrace = inStackTrace;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CCrashDlg message handlers
+
+void CCrashDlg::OnExit()
+{
+ this->EndDialog(0);
+ _exit(EXIT_FAILURE);
+}
+
+void CCrashDlg::SplitLongPath(int controlId, CString &text)
+{
+
+ // Get the device context of your control.
+ CDC *dc = GetDlgItem(controlId)->GetDC();
+ CRect filenameTextRect;
+ GetDlgItem(controlId)->GetClientRect(filenameTextRect);
+
+ HGDIOBJ hOldFont = dc->SelectObject(&m_MediumFont2);
+
+ CRect tmpStringRect(0, 0, 0, 0);
+ CString workingString = text;
+
+ CString tmpString;
+ std::vector<int> insertPosition;
+ int previousOffset = 0;
+
+ for (int i = 0; i < workingString.GetLength(); ++i) {
+ tmpString = workingString.Mid(previousOffset, i - previousOffset);
+ dc->DrawText(tmpString, &tmpStringRect, DT_CALCRECT);
+ if (tmpStringRect.Width() + 5 > filenameTextRect.Width()) {
+ previousOffset = i;
+ insertPosition.push_back(i - 1);
+ }
+ }
+
+ // adds in all the \n
+ for (int i = (int)insertPosition.size() - 1; i >= 0; --i) {
+ text.Insert(insertPosition[i], '\n');
+ }
+
+ dc->SelectObject(hOldFont);
+}
+
+BOOL CCrashDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ m_Brush.CreateSolidBrush(m_Color_Background);
+ GetDlgItem(IDC_HEADER)->SetFont(&m_MediumFont2);
+ GetDlgItem(IDC_HEADER2)->SetFont(&m_MediumFont2);
+
+ // m_Header = ::LoadResourceString( IDS_ERROR_MSGPASSING ).GetMulti( );
+ m_Header = m_ErrorMessage;
+ m_FilenameSaved = m_Filename;
+
+ CString theTitle = ::LoadResourceString(IDS_ERROR_MSGTITLE);
+ this->SetWindowText(theTitle);
+
+ // Show the exclamation point icon
+ m_CrashIcon.SetIcon(::LoadIcon(nullptr, IDI_ERROR));
+
+ SplitLongPath(IDC_HEADER2, m_FilenameSaved);
+ SplitLongPath(IDC_HEADER, m_Header);
+
+ this->UpdateData(FALSE);
+
+ return TRUE;
+}
+
+BOOL CCrashDlg::OnEraseBkgnd(CDC *pDC)
+{
+ CRect theClientRect;
+ GetClientRect(&theClientRect);
+ pDC->FillSolidRect(theClientRect, m_Color_Background);
+
+ return TRUE;
+}
+
+HBRUSH CCrashDlg::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT)
+{
+ int theCtrlID = pWnd->GetDlgCtrlID();
+ if (theCtrlID == IDREPORT || theCtrlID == IDEXIT || theCtrlID == IDC_LINE_CRASHDLG) {
+ pDC->SetBkMode(TRANSPARENT); // for area just behind the text
+ pDC->SetTextColor(m_Color_Text);
+ } else if (theCtrlID == IDC_HEADER || theCtrlID == IDC_HEADER2) {
+ pDC->SetBkMode(TRANSPARENT); // for area just behind the text
+ pDC->SetTextColor(m_Color_Gray);
+ }
+
+ return m_Brush;
+}
+
+void CCrashDlg::OnDestroy()
+{
+ CDialog::OnDestroy();
+
+ m_Brush.DeleteObject();
+}
diff --git a/src/Authoring/Studio/_Win/UI/CrashDlg.h b/src/Authoring/Studio/_Win/UI/CrashDlg.h
new file mode 100644
index 00000000..75f5765c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/CrashDlg.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_CRASH_DIALOG_H
+#define INCLUDED_CRASH_DIALOG_H
+#include "afxwin.h"
+#include <string.h>
+
+#pragma once
+
+/////////////////////////////////////////////////////////////////////////////
+// CCrashDlg dialog
+
+class CCrashDlg : public CDialog
+{
+ // Construction
+public:
+ CCrashDlg(CWnd *pParent = nullptr); // standard constructor
+
+ // Dialog Data
+ //{{AFX_DATA(CCrashDlg)
+ enum { IDD = IDD_CRASHDLG };
+ CStatic m_CrashIcon;
+ CString m_Header;
+ //}}AFX_DATA
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCrashDlg)
+protected:
+ virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ void SetErrorMessage(CString inErrorMessage);
+ void SetFilename(CString inFilename);
+ void SetStackTrace(CString inStackTrace);
+ void SplitLongPath(int controlId, CString &text);
+
+protected:
+ CString m_ErrorMessage;
+ CString m_FilenameSaved;
+ CString m_StackTrace;
+
+ COLORREF m_Color_Background;
+ COLORREF m_Color_Text;
+ COLORREF m_Color_Gray;
+ COLORREF m_Color_Dark;
+ CBrush m_Brush;
+ CFont m_MediumFont2;
+
+ // Generated message map functions
+ //{{AFX_MSG(CCrashDlg)
+ afx_msg void OnSave();
+ afx_msg void OnExit();
+ afx_msg void OnDestroy();
+ virtual BOOL OnInitDialog();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+public:
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+ afx_msg HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor);
+ CString m_Filename;
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // INCLUDED_CRASH_DIALOG_H
diff --git a/src/Authoring/Studio/_Win/UI/EditCameraBar.cpp b/src/Authoring/Studio/_Win/UI/EditCameraBar.cpp
new file mode 100644
index 00000000..7827ee93
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/EditCameraBar.cpp
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "EditCameraBar.h"
+#include "MainFrm.h"
+#include "SceneView.h"
+#include "StringLoader.h"
+#include "StudioPreferences.h"
+#include "StudioApp.h"
+#include "IStudioRenderer.h"
+
+#include <QComboBox>
+#include <QLabel>
+
+//==============================================================================
+/**
+ * Constructor
+ */
+CEditCameraBar::CEditCameraBar(QWidget* parent)
+ : QToolBar(parent)
+ , m_SceneView(nullptr)
+ , m_ActiveCameraIndex(-1)
+{
+ OnCustomizeToolbar();
+ auto activated = static_cast<void(QComboBox::*)(int)>(&QComboBox::activated);
+ connect(m_CameraSelector, activated, this, &CEditCameraBar::OnCameraChanged);
+}
+
+//==============================================================================
+/**
+ * Destructor
+ */
+CEditCameraBar::~CEditCameraBar()
+{
+ delete m_CameraSelector;
+ delete m_CameraText;
+}
+
+//==============================================================================
+/**
+ * Setup the list of edit cameras into the camera combo box
+ * @param inCameras the container that holds the edit cameras
+ */
+void CEditCameraBar::SetupCameras()
+{
+ m_CameraSelector->clear();
+ Q3DStudio::IStudioRenderer &theRenderer = g_StudioApp.GetRenderer();
+ QStringList theCameraNames;
+ theRenderer.GetEditCameraList(theCameraNames);
+ int idx = 1;
+ for (const QString &str : qAsConst(theCameraNames)) {
+ m_CameraSelector->addItem(str, QVariant((int)idx));
+ idx++;
+ }
+
+ m_CameraSelector->addItem("--------------------------");
+ m_CameraSelector->setItemData(m_CameraSelector->count() - 1, -1); // set to an invalid pointer
+ m_CameraSelector->addItem(::LoadResourceString(IDS_SCENE_CAMERA_VIEW).toQString());
+ m_CameraSelector->setItemData(m_CameraSelector->count() - 1, 0);
+
+ long thePreferredView = CStudioPreferences::GetPreferredStartupView();
+ long theNumItems = m_CameraSelector->count();
+ if (thePreferredView == -1) // deployment view
+ {
+ m_CameraSelector->setCurrentIndex(theNumItems - 1);
+ HandleCameraChanged(theNumItems - 1); // set to the last one
+ } else {
+ int theIndex;
+ if (thePreferredView < theNumItems - 2)
+ theIndex = thePreferredView;
+ else // possibly from old content where cameras are removed
+ theIndex = 0;
+ m_CameraSelector->setCurrentIndex(theIndex);
+ HandleCameraChanged(theIndex);
+ }
+}
+
+//==============================================================================
+/**
+ * Callback method when the camera is changed from the camera selection combo box
+ */
+void CEditCameraBar::OnCameraChanged()
+{
+ HandleCameraChanged(m_CameraSelector->currentIndex());
+}
+
+//==============================================================================
+/**
+ * Handle the switching of the current edit camera
+ * @param inIndex the index of the to-be-activated camera in the combo box
+ */
+void CEditCameraBar::HandleCameraChanged(int inIndex)
+{
+ Q3DStudio::IStudioRenderer &theRenderer = g_StudioApp.GetRenderer();
+ QStringList theCameraNames;
+ theRenderer.GetEditCameraList(theCameraNames);
+ int theNumCameras = theCameraNames.size();
+ if (inIndex < theNumCameras) {
+ theRenderer.SetEditCamera(inIndex);
+ m_ActiveCameraIndex = inIndex;
+ if (m_SceneView)
+ m_SceneView->SetViewMode(CPlayerContainerWnd::VIEW_EDIT);
+ } else if (inIndex > theNumCameras) {
+ theRenderer.SetEditCamera(-1);
+ m_ActiveCameraIndex = inIndex;
+ if (m_SceneView)
+ m_SceneView->SetViewMode(CPlayerContainerWnd::VIEW_SCENE);
+ } else {
+ m_CameraSelector->setCurrentIndex(m_ActiveCameraIndex);
+ }
+ if (m_SceneView)
+ m_SceneView->OnEditCameraChanged();
+
+ CMainFrame *theMainFrame = g_StudioApp.m_pMainWnd;
+ ASSERT(theMainFrame != nullptr);
+
+ // if the current tool is camera rotate and has been switch to 2d camera
+ // set the tool to camera pan
+ if (theMainFrame != nullptr) {
+ long theToolMode = g_StudioApp.GetToolMode();
+ if (theRenderer.DoesEditCameraSupportRotation(theRenderer.GetEditCamera()) == false
+ && theToolMode == STUDIO_TOOLMODE_CAMERA_ROTATE) {
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ }
+
+ // Trigger for tool changed. Changing between deployment/edit camera can change the tool
+ theMainFrame->OnUpdateToolChange();
+ }
+}
+
+//==============================================================================
+/**
+ * Set the current scene view. This scene view is notified when there is a camera
+ * changed.
+ * @param inSceneView the scene view object
+ */
+void CEditCameraBar::SetSceneView(CSceneView *inSceneView)
+{
+ m_SceneView = inSceneView;
+}
+
+//==============================================================================
+/**
+ * Enable/Disable the edit camera selector combo box.
+ * @param inFlag true to enable the camera selector combo box, false to disable
+ */
+void CEditCameraBar::Enable(bool inFlag)
+{
+ m_CameraSelector->setEnabled(inFlag);
+}
+
+//==============================================================================
+/**
+ * When the active camera is changed, the display string needs to be changed. Hence
+ * find which entry is the one which is modified and update with the new string
+ * @param inCamera the camera that has been modified
+ */
+void CEditCameraBar::OnEditCameraChanged()
+{
+ using qt3ds::QT3DSI32;
+ QT3DSI32 cameraIndex = g_StudioApp.GetRenderer().GetEditCamera();
+ long theNumEntry = m_CameraSelector->count();
+ for (long theIndex = 0; theIndex < theNumEntry; ++theIndex) {
+ if (m_CameraSelector->itemData(theIndex).toInt() == cameraIndex) {
+ if (theIndex != m_CameraSelector->currentIndex()) {
+ m_CameraSelector->setCurrentIndex(theIndex);
+ HandleCameraChanged(theIndex);
+ }
+ break;
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * Callback while creating the toolbar in MainFrm. This allows the toolbar to add
+ * other controls to it. For this toolbar, we want to add a descriptor and a camera
+ * selector dropdown combobox.
+ */
+//==============================================================================
+void CEditCameraBar::OnCustomizeToolbar()
+{
+ // Create the combo box
+ addWidget(m_CameraSelector = new QComboBox);
+ // We need to specify accessibleName and objectName for the combobox, as it's in the toolbar,
+ // and we want to use a different style for it.
+ m_CameraSelector->setAccessibleName(QStringLiteral("cameraSelector"));
+ m_CameraSelector->setObjectName(QStringLiteral("cameraSelector"));
+ m_CameraSelector->setMinimumWidth(145);
+}
diff --git a/src/Authoring/Studio/_Win/UI/EditCameraBar.h b/src/Authoring/Studio/_Win/UI/EditCameraBar.h
new file mode 100644
index 00000000..bd8427f5
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/EditCameraBar.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_EDIT_CAMERA_BAR
+#define INCLUDED_EDIT_CAMERA_BAR 1
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DispatchListeners.h"
+#include <vector>
+
+#include <QToolBar>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QLabel;
+QT_END_NAMESPACE
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CSceneView;
+
+//==============================================================================
+/**
+ * @class CEditCameraBar
+ */
+class CEditCameraBar : public QToolBar, public CEditCameraChangeListener
+{
+ Q_OBJECT
+public:
+ CEditCameraBar(QWidget* parent = nullptr); // standard constructor
+ virtual ~CEditCameraBar();
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CEditCameraBar)
+ //}}AFX_VIRTUAL
+
+ // Implementation
+ // Generated message map functions
+ void OnCameraChanged();
+ void OnCustomizeToolbar();
+
+public:
+ void SetupCameras();
+ void SetSceneView(CSceneView *inSceneView);
+ void Enable(bool inFlag);
+ void HandleCameraChanged(int inIndex);
+
+ // CEditCameraChangeListener
+ void OnEditCameraChanged() override;
+ void OnEditCamerasTransformed() override {} // I am not interested in this
+ void OnAuthorZoomChanged() override {}
+
+protected:
+ CSceneView *m_SceneView; ///< The scene view object
+ long m_ActiveCameraIndex; ///< The index of the active camera in the list
+
+ // UI Controls definition
+ QLabel* m_CameraText; ///< Static text showing the word "Camera"
+ QComboBox* m_CameraSelector; ///< Combo box for selecting edit camera
+};
+
+#endif
diff --git a/src/Authoring/Studio/_Win/UI/EditorPane.cpp b/src/Authoring/Studio/_Win/UI/EditorPane.cpp
new file mode 100644
index 00000000..ef274392
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/EditorPane.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "EditorPane.h"
+
+//==============================================================================
+/**
+ * Constructor: Creates a CEditorPane
+ */
+CEditorPane::CEditorPane()
+ : m_EditObject(nullptr)
+{
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the CEditorPane
+ */
+CEditorPane::~CEditorPane()
+{
+}
+
+//==============================================================================
+/**
+ * Set the object that this window will edit.
+ * Recieve and keep the CAsset to be edited. You can override this method
+ * for each specific editor view class.
+ * @param inEditObject The CAsset to be represented and edited in this view.
+ */
+void CEditorPane::SetEditObject(CAsset *inEditObject)
+{
+ // Retain this object
+ m_EditObject = inEditObject;
+}
+
+//==============================================================================
+/**
+ * Get the object that this window is editing.
+ * return The CAsset that is associated with this window
+ */
+CAsset *CEditorPane::GetEditObject()
+{
+ // Return the asset
+ return m_EditObject;
+}
+
+//==============================================================================
+/**
+ * CloseEditor: Close the editor.
+ * Override to close each individual editor.
+ * @return false
+ */
+bool CEditorPane::CloseEditor()
+{
+ return false;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/UI/EditorPane.h b/src/Authoring/Studio/_Win/UI/EditorPane.h
new file mode 100644
index 00000000..9bc91694
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/EditorPane.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_EDITOR_PANE
+#define INCLUDED_EDITOR_PANE 1
+
+#pragma once
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CAsset;
+
+//==============================================================================
+/**
+ * @class CEditorPane
+ * @brief Abstract base class for various editor views.
+ *
+ * A base class for universally accessing information from editor views.
+ */
+class CEditorPane
+{
+ // Fields
+protected:
+ CAsset *m_EditObject; ///< object being edited by this window.
+
+ // Methods
+public:
+ CEditorPane();
+ virtual ~CEditorPane();
+ virtual void SetEditObject(CAsset *inEditObject);
+ CAsset *GetEditObject();
+ virtual bool CloseEditor();
+};
+
+#endif // INCLUDED_EDITOR_PANE \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/UI/FileDialogEX.cpp b/src/Authoring/Studio/_Win/UI/FileDialogEX.cpp
new file mode 100644
index 00000000..9183e536
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/FileDialogEX.cpp
@@ -0,0 +1,451 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "Strings.h"
+#include <afxpriv.h>
+#include "FileDialogEX.h"
+#include "resource.h"
+#include "windowsx.h"
+#include "Userenv.h"
+#include "Commdlg.h"
+#include "CdErr.h"
+#include "Preferences.h"
+
+static const long SFILELISTBUFFERSIZE =
+ 2048; // Size of the char buffer used to store selected filenames
+// static BOOL IsWin2000();
+
+/////////////////////////////////////////////////////////////////////////////
+// CFileDialogEx
+
+IMPLEMENT_DYNAMIC(CFileDialogEx, CFileDialog)
+
+//==============================================================================
+/**
+ * CFileDialogEx: constructor
+ */
+//==============================================================================
+CFileDialogEx::CFileDialogEx(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName,
+ DWORD dwFlags, LPCTSTR lpszFilter, CWnd *pParentWnd)
+ : CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd)
+ , m_IsSaving(FALSE)
+ , m_LastFilterIndex(1)
+ , m_FileListBuffer(nullptr)
+ , m_WasCreateNewDirectoryChecked(false)
+ , m_CreateDirectoryCheckboxEnabled(false)
+ , m_Preferences(CPreferences::GetUserPreferences())
+{
+ m_IsSaving = !bOpenFileDialog; // if not openfiledlg, is saving
+ m_FilterIndex = 0; // init to 0; index starts from 1
+ m_ofnEx.pvReserved = nullptr;
+ m_ofnEx.dwReserved = 0;
+ m_ofnEx.FlagsEx = 0;
+
+ if (dwFlags & OFN_ALLOWMULTISELECT) {
+ // Create buffer for multiple selection
+ m_FileListBuffer = new TCHAR[SFILELISTBUFFERSIZE];
+ m_FileListBuffer[0] = '\0';
+
+ GetOFN().lpstrFile = m_FileListBuffer;
+ GetOFN().nMaxFile = SFILELISTBUFFERSIZE;
+ }
+}
+
+CFileDialogEx::~CFileDialogEx()
+{
+ if (m_FileListBuffer) {
+ delete[] m_FileListBuffer;
+ }
+}
+
+BEGIN_MESSAGE_MAP(CFileDialogEx, CFileDialog)
+//{{AFX_MSG_MAP(CFileDialogEX)
+ON_COMMAND(IDC_CHECK1, OnCreateNewDirectory)
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * IsWin2000: function to determine if we are on win2k
+ *
+ * @return true if we are on win2k
+ */
+//==============================================================================
+// BOOL IsWin2000 ()
+//{
+// OSVERSIONINFOEX theOsvi;
+// BOOL OsVersionInfoEx;
+//
+// // Try calling GetVersionEx using the OSVERSIONINFOEX structure,
+// // which is supported on Windows 2000.
+// //
+// // If that fails, try using the OSVERSIONINFO structure.
+//
+// ZeroMemory( &theOsvi, sizeof( OSVERSIONINFOEX ) );
+// theOsvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFOEX );
+// OsVersionInfoEx = GetVersionEx ( ( OSVERSIONINFO* ) &theOsvi );
+//
+// if( !OsVersionInfoEx )
+// {
+// // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
+//
+// theOsvi.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO );
+// if ( !GetVersionEx( ( OSVERSIONINFO* ) &theOsvi) )
+// return FALSE;
+// }
+//
+// switch ( theOsvi.dwPlatformId )
+// {
+// case VER_PLATFORM_WIN32_NT:
+//
+// if ( theOsvi.dwMajorVersion >= 5 )
+// return TRUE;
+//
+// break;
+// }
+// return FALSE;
+//}
+
+void CFileDialogEx::EnableCreateDirectoryCheckbox()
+{
+ m_CreateDirectoryCheckboxEnabled = true;
+}
+
+// http://msdn.microsoft.com/en-us/magazine/cc300434.aspx
+struct RegKeyOverrider
+{
+ const char *getRegKeyName()
+ {
+ return "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\ComDlg32\\PlacesBar";
+ }
+ HKEY m_TempKey;
+
+ RegKeyOverrider()
+ {
+ memset(&m_TempKey, 0, sizeof(HKEY));
+ long newKeyResult =
+ RegCreateKeyEx(HKEY_CURRENT_USER, L"UIComposer_FileDialog", 0, nullptr,
+ REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &m_TempKey, nullptr);
+ if (newKeyResult == ERROR_SUCCESS) {
+ long overrideResult = RegOverridePredefKey(HKEY_CURRENT_USER, m_TempKey);
+ ASSERT(overrideResult == ERROR_SUCCESS);
+ if (overrideResult == ERROR_SUCCESS) {
+ // Customize the places bar now in or overridden key.
+ HKEY placesKey;
+ long placesResult =
+ RegCreateKeyEx(HKEY_CURRENT_USER, CString(getRegKeyName()), 0, nullptr,
+ REG_OPTION_VOLATILE, KEY_ALL_ACCESS, nullptr, &placesKey, nullptr);
+ if (placesResult == ERROR_SUCCESS) {
+ TCHAR szHomeDirBuf[MAX_PATH] = { 0 };
+
+ HANDLE hToken = 0;
+ OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
+
+ DWORD BufSize = MAX_PATH;
+ GetUserProfileDirectory(hToken, szHomeDirBuf, &BufSize);
+
+ CloseHandle(hToken);
+
+ wcscat(szHomeDirBuf, L"\\Links");
+
+ size_t len = wcslen(szHomeDirBuf);
+ RegSetValueEx(placesKey, L"Place0", 0, REG_SZ, (const BYTE *)szHomeDirBuf,
+ (DWORD)len);
+
+ TCHAR buffer[32] = { 0 };
+ DWORD folderIds[] = { 8, 0, 17, 18 };
+
+ for (int idx = 0; idx < 4; ++idx) {
+ swprintf_s(buffer, L"Place%d", idx + 1);
+ long setKeyResult =
+ RegSetValueEx(placesKey, buffer, 0, REG_DWORD,
+ (const BYTE *)(folderIds + idx), sizeof(DWORD));
+ ASSERT(setKeyResult == ERROR_SUCCESS);
+ }
+ }
+ RegCloseKey(placesKey);
+ }
+ }
+ }
+ ~RegKeyOverrider()
+ {
+ RegOverridePredefKey(HKEY_CURRENT_USER, nullptr);
+ RegDeleteKey(m_TempKey, nullptr);
+ RegCloseKey(m_TempKey);
+ }
+};
+struct SErrorMap
+{
+ DWORD m_Error;
+ const char *m_Name;
+};
+static SErrorMap g_ErrorMap[] = {
+#define MAKE_STRUCT(name) { name, #name },
+ MAKE_STRUCT(CDERR_DIALOGFAILURE) MAKE_STRUCT(CDERR_FINDRESFAILURE)
+ MAKE_STRUCT(CDERR_INITIALIZATION) MAKE_STRUCT(CDERR_LOADRESFAILURE)
+ MAKE_STRUCT(CDERR_LOADSTRFAILURE) MAKE_STRUCT(CDERR_LOCKRESFAILURE)
+ MAKE_STRUCT(CDERR_MEMALLOCFAILURE) MAKE_STRUCT(CDERR_MEMLOCKFAILURE)
+ MAKE_STRUCT(CDERR_NOHINSTANCE) MAKE_STRUCT(CDERR_NOHOOK)
+ MAKE_STRUCT(CDERR_NOTEMPLATE) MAKE_STRUCT(CDERR_STRUCTSIZE)
+ MAKE_STRUCT(FNERR_BUFFERTOOSMALL) MAKE_STRUCT(FNERR_INVALIDFILENAME)
+ MAKE_STRUCT(FNERR_SUBCLASSFAILURE)
+#undef MAKE_STRUCT
+ { 0, nullptr },
+};
+//==============================================================================
+/**
+ * IsWin2000: Overloaded CFileDialog Function
+ *
+ * adds extra parameter if the operating system is win2k
+ *
+ * @return true for file open... false for file save as
+ */
+//==============================================================================
+INT_PTR CFileDialogEx::DoModal()
+{
+ ASSERT_VALID(this);
+ ASSERT(m_ofn.Flags & OFN_ENABLEHOOK);
+ ASSERT(m_ofn.lpfnHook != nullptr); // can still be a user hook
+
+ // zero out the file buffer for consistent parsing later
+ ASSERT(AfxIsValidAddress(m_ofn.lpstrFile, m_ofn.nMaxFile));
+ DWORD nOffset = (DWORD)_tcslen(m_ofn.lpstrFile) + 1;
+ ASSERT(nOffset <= m_ofn.nMaxFile);
+ memset(m_ofn.lpstrFile + nOffset, 0, (m_ofn.nMaxFile - nOffset) * sizeof(TCHAR));
+
+ if (!m_InitialDir.IsEmpty())
+ m_ofn.lpstrInitialDir = m_InitialDir;
+
+ // If the title has been specified, change it from the OS default
+ if (!m_Title.IsEmpty())
+ m_ofn.lpstrTitle = m_Title;
+
+ // If filterIndex has been changed, use it instead of the default
+ if (m_FilterIndex != 0)
+ m_ofn.nFilterIndex = m_FilterIndex;
+
+ if (m_CreateDirectoryCheckboxEnabled) {
+ // Begin customization for the checkbox for new project dialog
+ m_ofn.hInstance = GetModuleHandle(nullptr);
+ m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_NEWPROJEXT);
+ m_ofn.Flags |= OFN_ENABLETEMPLATE;
+ }
+
+ // WINBUG: This is a special case for the file open/save dialog,
+ // which sometimes pumps while it is coming up but before it has
+ // disabled the main window.
+ HWND hWndFocus = ::GetFocus();
+ BOOL bEnableParent = FALSE;
+
+ // OVerride the places bar so that is always shows favorites.
+ RegKeyOverrider regOverrider;
+ m_ofn.hwndOwner = PreModal();
+ AfxUnhookWindowCreate();
+ if (m_ofn.hwndOwner != nullptr && ::IsWindowEnabled(m_ofn.hwndOwner)) {
+ bEnableParent = TRUE;
+ ::EnableWindow(m_ofn.hwndOwner, FALSE);
+ }
+
+ _AFX_THREAD_STATE *pThreadState = AfxGetThreadState();
+ ASSERT(pThreadState->m_pAlternateWndInit == nullptr);
+
+ if (m_ofn.Flags & OFN_EXPLORER)
+ pThreadState->m_pAlternateWndInit = this;
+ else
+ AfxHookWindowCreate(this);
+
+ // This is whre is differs for 2000
+ ::memset(&m_ofnEx, 0, sizeof(m_ofnEx));
+ ::memcpy(&m_ofnEx, &m_ofn, sizeof(m_ofn));
+ // if ( IsWin2000() ){
+ // m_ofnEx.lStructSize = sizeof( m_ofnEx );
+ //}
+
+ int nResult;
+ if (m_bOpenFileDialog)
+ nResult = ::GetOpenFileName(&m_ofnEx);
+ else
+ nResult = ::GetSaveFileName(&m_ofnEx);
+
+ if (nResult == 0) {
+ DWORD error = ::CommDlgExtendedError();
+ if (error) {
+ SErrorMap *theError = nullptr;
+ for (theError = g_ErrorMap; theError->m_Name; ++theError) {
+ if (theError->m_Error == error) {
+ OutputDebugStringA("Massive failure in FileDialogEX!!\n");
+ OutputDebugStringA(theError->m_Name);
+ ASSERT(false);
+ }
+ }
+ }
+ }
+
+ // Copy back m_ofnEx -> m_ofn
+ ::memcpy(&m_ofn, &m_ofnEx, sizeof(m_ofn));
+ m_ofn.lStructSize = sizeof(m_ofn);
+
+ if (nResult)
+ ASSERT(pThreadState->m_pAlternateWndInit == nullptr);
+ pThreadState->m_pAlternateWndInit = nullptr;
+
+ // WINBUG: Second part of special case for file open/save dialog.
+ if (bEnableParent)
+ ::EnableWindow(m_ofn.hwndOwner, TRUE);
+ if (::IsWindow(hWndFocus))
+ ::SetFocus(hWndFocus);
+
+ PostModal();
+
+ if (nResult) {
+ m_LastFilterIndex = GetOFN().nFilterIndex;
+ }
+
+ return nResult ? nResult : IDCANCEL;
+}
+
+BOOL CFileDialogEx::OnFileNameOK()
+{
+ CFileStatus theFileStatus;
+ CString theFilePath = Q3DStudio::CString(GetOFN().lpstrFile);
+ // CString theFilePath = this->GetPathName(); // Somehow, this does not return the filename
+ // with extension
+ BOOL theReturnValue = FALSE;
+
+ if (m_IsSaving && CFile::GetStatus(theFilePath, theFileStatus)) {
+ if (theFileStatus.m_attribute & CFile::readOnly) {
+ Q3DStudio::CString theMsgTitle;
+ Q3DStudio::CString theMsg;
+ Q3DStudio::CString theFormattedText;
+ Q3DStudio::CString theFileNameText = GetOFN().lpstrFileTitle;
+ // Q3DStudio::CString theFileNameText = GetFileName( );
+
+ theMsgTitle = ::LoadResourceString(IDS_PROJNAME);
+ theMsg = ::LoadResourceString(IDS_SAVE_READONLY_WARNING);
+ theFormattedText.Format(theMsg, static_cast<const wchar_t *>(theFileNameText));
+
+ ::MessageBox(this->GetSafeHwnd(), theFormattedText, theMsgTitle,
+ MB_OK | MB_ICONEXCLAMATION);
+ theReturnValue = TRUE;
+ }
+ }
+
+ return theReturnValue;
+}
+
+void CFileDialogEx::SetInitialDirectory(const Q3DStudio::CString &inDir)
+{
+ Q3DStudio::CFilePath thePath(inDir);
+ if (thePath.IsFile())
+ thePath = thePath.GetDirectory();
+ m_InitialDir = thePath;
+}
+
+void CFileDialogEx::OnCreateNewDirectory()
+{
+ m_WasCreateNewDirectoryChecked = !m_WasCreateNewDirectoryChecked;
+ m_Preferences.SetValue("CreateDirForProject", m_WasCreateNewDirectoryChecked);
+}
+
+//=============================================================================
+/**
+ * Allows the user to set the title of the dialog. By default, the title is
+ * determined by the first parameter of the constructor (bOpenFileDialog).
+ * TRUE specifies a file open dialog and FALSE specifies a Save As dialog.
+ * The title of the window is then generated by the OS. This function enables
+ * you to set the title to any string you want, overriding the OS value.
+ * @param inTitle new title for this dialog; if an empty string is specified,
+ * the OS generated title will be displayed.
+ */
+void CFileDialogEx::SetWindowTitle(const Q3DStudio::CString &inTitle)
+{
+ m_Title = inTitle;
+}
+
+//=============================================================================
+/**
+ * Sets the filter to be selected when the dialog is opened, if specified.
+ * The filter is indexed from 1 to n.
+ *
+ * @param inFilterIndex the index of the filter to be applied as the default
+ * extension to be used when displaying the dialog.
+ * @author AT May 29 2003
+ */
+void CFileDialogEx::SetFilterIndex(const LONG inFilterIndex)
+{
+ m_FilterIndex = inFilterIndex;
+}
+
+BOOL CFileDialogEx::OnInitDialog()
+{
+ BOOL retval = CFileDialog::OnInitDialog();
+ if (m_CreateDirectoryCheckboxEnabled) {
+ CWnd *theWnd = GetDlgItem(IDC_CHECK1);
+ if (theWnd) {
+ m_WasCreateNewDirectoryChecked =
+ m_Preferences.GetValue("CreateDirForProject", true) ? 1 : 0;
+ Button_SetCheck(theWnd->GetSafeHwnd(), m_WasCreateNewDirectoryChecked ? 1 : 0);
+ }
+ }
+ return retval;
+}
+
+//=============================================================================
+/**
+ * Gets the filter index when the dialog is opened.
+ * The filter is indexed from 1 to n.
+ * The member was set at the end of DoModal.
+ *
+ * @return the filter index, starting from 1 to n
+ */
+long CFileDialogEx::GetLastFilterIndex()
+{
+ return m_LastFilterIndex;
+}
+
+//=============================================================================
+/**
+ * Retrieves the list of files selected during multiple selection
+ * @param outFileList vector to hold filenames
+ */
+void CFileDialogEx::RetrieveFileList(TFILELIST &outFileList) const
+{
+ outFileList.clear();
+ POSITION theStartPosition = GetStartPosition();
+
+ Q3DStudio::CString theFileName;
+ while (nullptr != theStartPosition) {
+ theFileName = GetNextPathName(theStartPosition);
+ outFileList.push_back(CUICFile(theFileName));
+ }
+}
diff --git a/src/Authoring/Studio/_Win/UI/FileDialogEX.h b/src/Authoring/Studio/_Win/UI/FileDialogEX.h
new file mode 100644
index 00000000..ad82de14
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/FileDialogEX.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#if !defined(AFX_FILEDIALOGEX_H__4CF114A7_E3C1_44AA_8A73_92CF2480D010__INCLUDED_)
+#define AFX_FILEDIALOGEX_H__4CF114A7_E3C1_44AA_8A73_92CF2480D010__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+struct OPENFILENAMEEX : public OPENFILENAME
+{
+ void *pvReserved;
+ DWORD dwReserved;
+ DWORD FlagsEx;
+};
+
+//==============================================================================
+/**
+ * CFileDialog: Extends the regular CFileDialog for win2k dialog box
+ */
+//==============================================================================
+class CFileDialogEx : public CFileDialog
+{
+ DECLARE_DYNAMIC(CFileDialogEx)
+
+public:
+ typedef std::vector<CUICFile> TFILELIST;
+ OPENFILENAMEEX m_ofnEx; ///< Extra structure passed in for win 2k dialog
+
+ //==========================================================================
+ // Methods
+ //==========================================================================
+ CFileDialogEx(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
+ LPCTSTR lpszDefExt = nullptr, LPCTSTR lpszFileName = nullptr,
+ DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = nullptr,
+ CWnd *pParentWnd = nullptr);
+
+ virtual ~CFileDialogEx();
+
+ void EnableCreateDirectoryCheckbox();
+ virtual void SetInitialDirectory(const Q3DStudio::CString &inDir);
+ virtual void SetWindowTitle(const Q3DStudio::CString &inTitle);
+ virtual void SetFilterIndex(const LONG inFilterIndex);
+ virtual BOOL OnInitDialog();
+ long GetLastFilterIndex();
+ void RetrieveFileList(TFILELIST &outFileList) const;
+
+ virtual INT_PTR DoModal();
+ virtual BOOL OnFileNameOK();
+ void IsSaving() { m_IsSaving = TRUE; }
+ bool WasCreateNewDirectoryChecked() { return m_WasCreateNewDirectoryChecked; }
+
+protected:
+ BOOL m_IsSaving;
+ Q3DStudio::CString m_InitialDir;
+ Q3DStudio::CString m_Title;
+ LONG m_FilterIndex;
+ long m_LastFilterIndex; //< the last filter index for when a file was selected
+ wchar_t *m_FileListBuffer;
+ bool m_CreateDirectoryCheckboxEnabled;
+ bool m_WasCreateNewDirectoryChecked;
+ // We have to cache a CPreferences here because we reroute the registry during
+ // doModal in order to make the file dialogue behavior differently.
+ // see RegKeyOverrider
+ CPreferences m_Preferences;
+
+ //==========================================================================
+ // Members
+ //==========================================================================
+
+ // Generated message map functions
+ //{{AFX_MSG(CFileDialogEx)
+ afx_msg void OnCreateNewDirectory();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_FILEDIALOGEX_H__4CF114A7_E3C1_44AA_8A73_92CF2480D010__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/GLVersionDlg.cpp b/src/Authoring/Studio/_Win/UI/GLVersionDlg.cpp
new file mode 100644
index 00000000..cb02578b
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/GLVersionDlg.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "GLVersionDlg.h"
+
+CGLVersionDlg::CGLVersionDlg(CWnd *pParent /*=nullptr*/)
+ : CDialog(CGLVersionDlg::IDD, pParent)
+{
+ m_Title = _T("");
+ m_Message = _T("");
+ m_Icon = nullptr;
+ m_DontShowAgain = FALSE;
+}
+
+CGLVersionDlg::~CGLVersionDlg()
+{
+}
+
+void CGLVersionDlg::DoDataExchange(CDataExchange *pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_GL_VERSION_ICON, m_WarningIcon);
+ DDX_Text(pDX, IDC_GL_VERSION_MESSAGE, m_Message);
+ DDX_Check(pDX, IDC_CHECK1, m_DontShowAgain);
+}
+
+BEGIN_MESSAGE_MAP(CGLVersionDlg, CDialog)
+END_MESSAGE_MAP()
+
+BOOL CGLVersionDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Set the title
+ this->SetWindowText(m_Title);
+
+ // Set icon
+ m_WarningIcon.SetIcon(::AfxGetApp()->LoadStandardIcon(m_Icon));
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CGLVersionDlg::Initialize(const Q3DStudio::CString &inTitle,
+ const Q3DStudio::CString &inMessage, bool inErrorIcon)
+{
+ // Set title and message
+ m_Title = CString(inTitle);
+ m_Message = CString(inMessage);
+
+ // Set which icon to load
+ if (inErrorIcon)
+ m_Icon = IDI_ERROR;
+ else
+ m_Icon = IDI_WARNING;
+}
+
+BOOL CGLVersionDlg::GetDontShowAgain()
+{
+ return m_DontShowAgain;
+}
diff --git a/src/Authoring/Studio/_Win/UI/GLVersionDlg.h b/src/Authoring/Studio/_Win/UI/GLVersionDlg.h
new file mode 100644
index 00000000..908a8441
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/GLVersionDlg.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_GL_VERSION_DLG
+#define INCLUDED_GL_VERSION_DLG 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "resource.h"
+
+//==============================================================================
+/**
+ * CGLVersionDlg: Dialog class for showing Open GL Version Warning
+ */
+//==============================================================================
+class CGLVersionDlg : public CDialog
+{
+public:
+ CGLVersionDlg(CWnd *pParent = nullptr); // standard constructor
+ virtual ~CGLVersionDlg();
+
+ // Dialog Data
+ enum { IDD = IDD_GL_VERSION_DLG };
+
+protected:
+ virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
+
+ DECLARE_MESSAGE_MAP()
+ CStatic m_WarningIcon; // Warning icon
+ CString m_Title; // Title for the dialog
+ CString m_Message; // Warning message
+ BOOL m_DontShowAgain; // Set to true to "Don't show this dialog again"
+ LPCTSTR m_Icon; // Which icon to load
+
+public:
+ virtual BOOL OnInitDialog();
+ void Initialize(const Q3DStudio::CString &inTitle, const Q3DStudio::CString &inMessage,
+ bool inErrorIcon);
+ BOOL GetDontShowAgain();
+};
+
+#endif // INCLUDED_GL_VERSION_DLG
diff --git a/src/Authoring/Studio/_Win/UI/InterpolationDlg.cpp b/src/Authoring/Studio/_Win/UI/InterpolationDlg.cpp
new file mode 100644
index 00000000..57d6ae1d
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/InterpolationDlg.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix / Includes
+//==============================================================================
+
+#include "stdafx.h"
+#include "InterpolationDlg.h"
+#include "ui_InterpolationDlg.h"
+
+//==============================================================================
+/**
+ * Constructor: Creates a CInterpolationDlg.
+ *
+ * @param parent Pointer to the parent of this dialog (defaults to NULL)
+ */
+//==============================================================================
+CInterpolationDlg::CInterpolationDlg(QWidget *parent)
+ : QDialog(parent)
+ , m_ui(new Ui::InterpolationDlg)
+{
+ m_ui->setupUi(this);
+}
+
+CInterpolationDlg::~CInterpolationDlg()
+{
+ delete m_ui;
+ m_ui = nullptr;
+}
+
+void CInterpolationDlg::setEaseIn(uint value)
+{
+ m_ui->easeInSlider->setValue(value);
+}
+
+void CInterpolationDlg::setEaseOut(uint value)
+{
+ m_ui->easeOutSlider->setValue(value);
+}
+
+int CInterpolationDlg::easeIn() const
+{
+ return m_ui->easeInSlider->value();
+}
+
+int CInterpolationDlg::easeOut() const
+{
+ return m_ui->easeOutSlider->value();
+}
+
+
diff --git a/src/Authoring/Studio/_Win/UI/InterpolationDlg.h b/src/Authoring/Studio/_Win/UI/InterpolationDlg.h
new file mode 100644
index 00000000..5f371625
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/InterpolationDlg.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_INTERPOLATION_DLG
+#define INCLUDED_INTERPOLATION_DLG 1
+
+#pragma once
+
+#include <QDialog>
+
+namespace Ui {
+class InterpolationDlg;
+}
+
+//==============================================================================
+/**
+ * CInterpolationDlg: Dialog class for editing the ease-in/ease-out values of keyframes.
+ */
+//==============================================================================
+class CInterpolationDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit CInterpolationDlg(QWidget *parent = nullptr); // standard constructor
+ ~CInterpolationDlg();
+
+ void setEaseIn(uint value);
+ void setEaseOut(uint value);
+ int easeIn() const;
+ int easeOut() const;
+
+protected:
+ Ui::InterpolationDlg* m_ui = nullptr;
+};
+
+#endif // INCLUDED_INTERPOLATION_DLG
diff --git a/src/Authoring/Studio/_Win/UI/InterpolationDlg.ui b/src/Authoring/Studio/_Win/UI/InterpolationDlg.ui
new file mode 100644
index 00000000..f0a0a759
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/InterpolationDlg.ui
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>InterpolationDlg</class>
+ <widget class="QDialog" name="InterpolationDlg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>425</width>
+ <height>195</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Set Keyframe Interpolation</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="2" column="1" colspan="2">
+ <widget class="QSlider" name="easeOutSlider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBelow</enum>
+ </property>
+ <property name="tickInterval">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ease In:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Smooth</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <widget class="QSlider" name="easeInSlider">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksBelow</enum>
+ </property>
+ <property name="tickInterval">
+ <number>10</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Linear</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Linear</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Smooth</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ease Out:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QSpinBox" name="easeInSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QSpinBox" name="easeOutSpinBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>InterpolationDlg</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>224</x>
+ <y>272</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>InterpolationDlg</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>easeInSlider</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>easeInSpinBox</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>331</x>
+ <y>21</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>542</x>
+ <y>35</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>easeOutSlider</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>easeOutSpinBox</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>210</x>
+ <y>93</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>545</x>
+ <y>93</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>easeInSpinBox</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>easeInSlider</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>533</x>
+ <y>24</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>408</x>
+ <y>18</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>easeOutSpinBox</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>easeOutSlider</receiver>
+ <slot>setValue(int)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>546</x>
+ <y>98</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>338</x>
+ <y>84</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/Authoring/Studio/_Win/UI/MemoryDC.cpp b/src/Authoring/Studio/_Win/UI/MemoryDC.cpp
new file mode 100644
index 00000000..a253e89a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MemoryDC.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "resource.h"
+#include "MemoryDC.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CMemoryDC
+
+//==============================================================================
+/*
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CMemoryDC::CMemoryDC()
+ : m_ValidFlag(FALSE)
+ , m_SrcDC(nullptr)
+ , m_OldBmp(nullptr)
+{
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CMemoryDC::~CMemoryDC()
+{
+ // automatically release and draw on exit
+ Release();
+}
+
+//==============================================================================
+/**
+ * Create
+ *
+ * Creates the memory DC and image bitmap
+ *
+ * @param inDC Source device context
+ * @param inSrcRect CRect containing the source rectangle for drawing
+ */
+//==============================================================================
+void CMemoryDC::Create(CDC *inDC, CRect inSrcRect)
+{
+ ASSERT(inDC != nullptr);
+
+ if (!m_ValidFlag) {
+ // create the memory DC
+ CreateCompatibleDC(inDC);
+
+ // save the source DC
+ m_SrcDC = inDC;
+
+ // keep track of the destination rectangle
+ m_SrcRect.CopyRect(inSrcRect);
+
+ // create a bitmap for the memory bitmap image
+ m_MemBmp.CreateCompatibleBitmap(inDC, inSrcRect.Width(), inSrcRect.Height());
+
+ // select the memory image into the memory DC
+ m_OldBmp = SelectObject(&m_MemBmp);
+
+ m_ValidFlag = TRUE;
+ }
+}
+
+//==============================================================================
+/**
+ * Release
+ *
+ * Releases the memory DC and image bitmap and optionally copies the image
+ * to the source DC.
+ *
+ * @param inCopyToSourceFlag If TRUE, copies the memory image to the source DC
+ */
+//==============================================================================
+void CMemoryDC::Release(BOOL inCopyToSourceFlag)
+{
+ // copy the offscreen buffer to the sourceDC passed in Create()
+
+ if (m_ValidFlag) {
+ // blit to source DC to the m_SrcRect
+ if ((inCopyToSourceFlag) && (m_SrcDC != nullptr))
+ m_SrcDC->BitBlt(m_SrcRect.left, m_SrcRect.top, m_SrcRect.Width(), m_SrcRect.Height(),
+ this, 0, 0, SRCCOPY);
+
+ // de-select the memory image from the DC
+ SelectObject(m_OldBmp);
+
+ // delete the memory bitmap image
+ m_MemBmp.DeleteObject();
+
+ // delete the memory DC
+ DeleteDC();
+
+ m_ValidFlag = FALSE;
+ m_OldBmp = nullptr;
+ }
+}
+
+//==============================================================================
+/**
+ * CopySourceImage
+ *
+ * Copies the source image from the m_SrcRect into the memory DC/image.
+ *
+ * @param None
+ */
+//==============================================================================
+void CMemoryDC::CopySourceImage()
+{
+ if (m_ValidFlag) {
+ // copy the image from the source rectangle to the offscreen image
+ if (m_SrcDC != nullptr)
+ this->BitBlt(0, 0, m_SrcRect.Width(), m_SrcRect.Height(), m_SrcDC, m_SrcRect.left,
+ m_SrcRect.top, SRCCOPY);
+ }
+}
+
+//==============================================================================
+/**
+ * ConvertRect
+ *
+ * Converts a rectangle based on the source coordinates to one based on
+ * the memory image's coordinates.
+ *
+ * @param inDrawRect Source drawing CRect
+ *
+ * @return CRect Contains the converted rectangle
+ */
+//==============================================================================
+CRect CMemoryDC::ConvertRect(CRect inDrawRect)
+{
+ CRect theRect;
+
+ theRect.CopyRect(inDrawRect);
+ theRect.OffsetRect(-m_SrcRect.left, -m_SrcRect.top);
+
+ return theRect;
+}
+
+//==============================================================================
+/**
+ * ConvertPoint
+ *
+ * Converts a point based on the source coordinates to one based on
+ * the memory image's coordinates.
+ *
+ * @param inDrawRect Source drawing CPoint
+ *
+ * @return CPoint Contains the converted point
+ */
+//==============================================================================
+CPoint CMemoryDC::ConvertPoint(CPoint inDrawPoint)
+{
+ CPoint thePoint;
+
+ // convert point relative to this DC
+ thePoint = inDrawPoint;
+ thePoint.x -= m_SrcRect.left;
+ thePoint.y -= m_SrcRect.top;
+
+ return thePoint;
+}
+
+//==============================================================================
+/**
+ * ConvertXPos
+ *
+ * Converts an x-position based on the source coordinates to one based on
+ * the memory image's coordinates.
+ *
+ * @param inXValue Source drawing x-position
+ *
+ * @return long Contains the converted x-position
+ */
+//==============================================================================
+long CMemoryDC::ConvertXPos(long inXValue)
+{
+ long theXPos;
+
+ // convert x coordinate relative to this DC
+ theXPos = (long)inXValue - (long)m_SrcRect.left;
+
+ return theXPos;
+}
+
+//==============================================================================
+/**
+ * ConvertYPos
+ *
+ * Converts an y-position based on the source coordinates to one based on
+ * the memory image's coordinates.
+ *
+ * @param inYValue Source drawing y-position
+ *
+ * @return long Contains the converted y-position
+ */
+//==============================================================================
+long CMemoryDC::ConvertYPos(long inYValue)
+{
+ long theYPos;
+
+ // convert y coordinate relative to this DC
+ theYPos = (long)inYValue - (long)m_SrcRect.top;
+
+ return theYPos;
+}
+
+//==============================================================================
+/**
+ * SetRect
+ *
+ * Sets the drawing/output bounding rectangle.
+ *
+ * @param inRect Bounding rectangle for drawing
+ */
+//==============================================================================
+void CMemoryDC::SetRect(CRect inRect)
+{
+ m_SrcRect = inRect;
+}
diff --git a/src/Authoring/Studio/_Win/UI/MemoryDC.h b/src/Authoring/Studio/_Win/UI/MemoryDC.h
new file mode 100644
index 00000000..f72701e0
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MemoryDC.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_MEMORY_DC_H
+#define INCLUDED_MEMORY_DC_H
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+/**
+ * CMemoryDC: Subclassed CDC for drawing offscreen
+ *
+ * This is a subclassed CDC so we can control drawing into a DC with offscreen
+ * buffers to prevent flickering.
+ */
+//==============================================================================
+
+class CMemoryDC : public CDC
+{
+public:
+ //==========================================================================
+ // Fields
+ //==========================================================================
+
+protected:
+ BOOL m_ValidFlag; // TRUE if we can successfully release/copy the offscreen image to the screen
+ CRect m_SrcRect; // Bounding rectangle for drawing
+ CBitmap m_MemBmp; // Offscreen bitmap image
+ CBitmap *m_OldBmp; // Previous bitmap in the offscreen DC
+ CDC *m_SrcDC; // Source device context for final blit
+
+ //==========================================================================
+ // Methods
+ //==========================================================================
+
+public:
+ void SetRect(CRect inRect);
+
+ // Construction
+
+ CMemoryDC();
+ virtual ~CMemoryDC();
+
+ // Access
+
+ CPoint ConvertPoint(CPoint inDrawPoint);
+ CRect ConvertRect(CRect inDrawRect);
+ long ConvertXPos(long inXValue);
+ long ConvertYPos(long inYValue);
+ void CopySourceImage();
+ void Create(CDC *inDC, CRect inSrcRect);
+
+ void Release(BOOL inCopyToSourceFlag = TRUE);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // INCLUDED_MEMORY_DC_H
diff --git a/src/Authoring/Studio/_Win/UI/MenuEdit.cpp b/src/Authoring/Studio/_Win/UI/MenuEdit.cpp
new file mode 100644
index 00000000..39c82246
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MenuEdit.cpp
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "stdafx.h"
+#include "MenuEdit.h"
+#include "BCMenu.h"
+#include "StudioColors.h"
+
+//==============================================================================
+// Message Map
+//==============================================================================
+
+BEGIN_MESSAGE_MAP(CMenuEdit, CEdit)
+//{{AFX_MSG_MAP(CMenuEdit)
+ON_WM_SETFOCUS()
+ON_WM_CONTEXTMENU()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+// IMPLEMENTATION
+//==============================================================================
+
+//==============================================================================
+/**
+ * Constructor: Creates a CMenuEdit
+ */
+//==============================================================================
+CMenuEdit::CMenuEdit()
+{
+}
+
+//==============================================================================
+/**
+ * OnSetFocus: The message handler for accepting focus.
+ *
+ * @param pOldWnd The window that had focus last.
+ */
+//==============================================================================
+void CMenuEdit::OnSetFocus(CWnd *pOldWnd)
+{
+ // Allow the parent to handle the request.
+ CEdit::OnSetFocus(pOldWnd);
+
+ // Select the entire text.
+ this->SetSel(0, -1);
+}
+
+//******************************************************************************
+// DYNAMIC CREATION
+//******************************************************************************
+
+IMPLEMENT_DYNAMIC(CMenuEdit, CEdit)
+
+//==============================================================================
+/**
+ * OnContextMenu: WM_CONTEXTMENU handler for the CMenuEdit.
+ *
+ * @param inWnd This window.
+ * @param inPoint Mouse position when the right button was clicked.
+ */
+//==============================================================================
+void CMenuEdit::OnContextMenu(CWnd *inWnd, CPoint inPoint)
+{
+ Q_UNUSED(inWnd);
+
+ // Added just because Justin has an eye for detail. :)
+
+ BCMenu theMenu;
+ BCMenu *thePopupMenu = nullptr;
+ long theMenuCmd;
+
+ if (GetFocus() != this)
+ this->SetFocus();
+
+ // Load the context menu
+ theMenu.LoadMenu(IDR_CONTEXT_EDIT);
+#ifdef _USECONTENTMENUIMAGES_
+ theMenu.SetBitmapBackground(MENU_IMAGELIST_BACKCOLOR);
+ theMenu.LoadToolbar(IDC_EDITCONTEXTTOOLBAR);
+#endif
+
+ thePopupMenu = (BCMenu *)theMenu.GetSubMenu(0);
+
+ // Enable/disable menu items
+ if (!this->CanUndo())
+ thePopupMenu->EnableMenuItem(IDC_UNDO, MF_BYCOMMAND | MF_GRAYED);
+
+ // Disable the Select All if there is no text in the control
+ if (this->GetWindowTextLength() == 0)
+ thePopupMenu->EnableMenuItem(IDC_SELECTALL, MF_BYCOMMAND | MF_GRAYED);
+
+ // Disable paste if there is no CF_TEXT on the clipboard
+ if (!IsClipboardFormatAvailable(CF_TEXT))
+ thePopupMenu->EnableMenuItem(IDC_PASTE, MF_BYCOMMAND | MF_GRAYED);
+
+ // Check if any text is selected
+ DWORD theSelection = this->GetSel();
+ if (LOWORD(theSelection) == HIWORD(theSelection)) {
+ // Disable cut, copy and delete commands if no text is selected
+ thePopupMenu->EnableMenuItem(IDC_CUT, MF_BYCOMMAND | MF_GRAYED);
+ thePopupMenu->EnableMenuItem(IDC_COPY, MF_BYCOMMAND | MF_GRAYED);
+ thePopupMenu->EnableMenuItem(IDC_DELETE, MF_BYCOMMAND | MF_GRAYED);
+ }
+
+ // Post the menu and handle the command here.
+ theMenuCmd = (long)thePopupMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD
+ | TPM_NONOTIFY,
+ inPoint.x, inPoint.y, this, nullptr);
+ switch (theMenuCmd) {
+ case IDC_UNDO:
+ this->Undo();
+ break;
+
+ case IDC_CUT:
+ this->Cut();
+ break;
+
+ case IDC_COPY:
+ this->Copy();
+ break;
+
+ case IDC_PASTE:
+ this->Paste();
+ break;
+
+ case IDC_DELETE:
+ // Delete the selected text
+ this->ReplaceSel(L"", FALSE);
+ break;
+
+ case IDC_SELECTALL:
+ // Select the entire text.
+ this->SetSel(0, -1);
+ break;
+ }
+
+ theMenu.DestroyMenu();
+}
diff --git a/src/Authoring/Studio/_Win/UI/MenuEdit.h b/src/Authoring/Studio/_Win/UI/MenuEdit.h
new file mode 100644
index 00000000..9aae97e2
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MenuEdit.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef _MENUEDIT_H_
+#define _MENUEDIT_H_
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+// Class
+//==============================================================================
+
+//==============================================================================
+/**
+ * @class CMenuEdit
+ * @brief This edit control has a BCMenu context menu.
+ */
+//==============================================================================
+class CMenuEdit : public CEdit
+{
+ // Fields
+
+public:
+ // Methods
+
+public:
+ DECLARE_DYNAMIC(CMenuEdit)
+ CMenuEdit();
+ virtual ~CMenuEdit() {}
+
+protected:
+ //{{AFX_MSG(CMenuEdit)
+ afx_msg void OnSetFocus(CWnd *pOldWnd);
+ afx_msg void OnContextMenu(CWnd *pWnd, CPoint point);
+ //}}AFX_MSG
+
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _MENUEDIT_H_
diff --git a/src/Authoring/Studio/_Win/UI/MenuItem.cpp b/src/Authoring/Studio/_Win/UI/MenuItem.cpp
new file mode 100644
index 00000000..beb77d98
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MenuItem.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "MenuItem.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+//==============================================================================
+/**
+ * CMenuItem: constructor
+ *
+ * @param inName name of the file
+ * @param inPath path to the file
+ * @param inID ID tag for this handler in the menu
+ */
+//==============================================================================
+CMenuItem::CMenuItem(CString inName, CString inPath, UINT inID)
+{
+ m_Name = inName;
+ m_ID = inID;
+ m_Path = inPath;
+}
+
+CMenuItem::~CMenuItem()
+{
+}
diff --git a/src/Authoring/Studio/_Win/UI/MenuItem.h b/src/Authoring/Studio/_Win/UI/MenuItem.h
new file mode 100644
index 00000000..f8033b81
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/MenuItem.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_MENUITEM_H__6383095D_C084_44EB_A6D0_04CC1DA1B74F__INCLUDED_)
+#define AFX_MENUITEM_H__6383095D_C084_44EB_A6D0_04CC1DA1B74F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//==============================================================================
+// CMenuItem Class
+//==============================================================================
+
+//==============================================================================
+/**
+ * CMenuItem: Object for creating dynamic menus
+ */
+//==============================================================================
+class CMenuItem : public CObject
+{
+public:
+ CMenuItem(CString inName, CString inPath, UINT inID);
+ virtual ~CMenuItem();
+ UINT GetID() { return m_ID; };
+ CString GetPath() { return m_Path; };
+ CString GetName() { return m_Name; };
+protected:
+ CString m_Path; ///< Path to the file
+ CString m_Name; ///< Name to use in the menu
+ UINT m_ID; ///< ID of this menu item
+};
+
+#endif // !defined(AFX_MENUITEM_H__6383095D_C084_44EB_A6D0_04CC1DA1B74F__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/NumericEdit.cpp b/src/Authoring/Studio/_Win/UI/NumericEdit.cpp
new file mode 100644
index 00000000..73ff7bc8
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/NumericEdit.cpp
@@ -0,0 +1,347 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "NumericEdit.h"
+
+#include <minmax.h>
+
+/////////////////////////////////////////////////////////////////////////////
+// CNumericEdit class
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CNumericEdit::CNumericEdit()
+{
+ m_RangeLow = -1;
+ m_RangeHigh = -1;
+
+ m_WrapFlag = FALSE;
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CNumericEdit::~CNumericEdit()
+{
+}
+
+//=============================================================================
+/**
+ * Message Map
+ */
+//=============================================================================
+BEGIN_MESSAGE_MAP(CNumericEdit, CEdit)
+//{{AFX_MSG_MAP(CNumericEdit)
+ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillFocus)
+ON_CONTROL_REFLECT(EN_UPDATE, OnUpdate)
+ON_WM_CHAR()
+ON_WM_MOUSEWHEEL()
+ON_WM_KEYDOWN()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//=============================================================================
+/**
+ * SetRange: Sets the value range for this control.
+ *
+ * @param inRangeLow Low range.
+ * @param inRangeHigh High range
+ */
+//=============================================================================
+void CNumericEdit::SetRange(long inRangeLow, long inRangeHigh)
+{
+ // Ensure they are in low/high order
+ m_RangeLow = min(inRangeLow, inRangeHigh);
+ m_RangeHigh = max(inRangeLow, inRangeHigh);
+}
+
+//=============================================================================
+/**
+ * SetValue: Sets the numeric value for this control.
+ *
+ * @param inValue The new value for this control.
+ */
+//=============================================================================
+void CNumericEdit::SetValue(long inValue)
+{
+ CString theStrValue;
+
+ theStrValue.Format(L"%lu", inValue);
+ this->SetWindowText(theStrValue);
+}
+
+//=============================================================================
+/**
+ * GetValue: Returns the numeric value for this control.
+ *
+ * @param None
+ *
+ * @return long The numeric value for this control.
+ */
+//=============================================================================
+long CNumericEdit::GetValue()
+{
+ CString theStrValue;
+
+ // Get the current control value.
+ this->GetWindowText(theStrValue);
+
+ // Convert to long and return.
+ return atol((char *)LPCTSTR(theStrValue));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CNumericEdit message handlers
+
+//=============================================================================
+/**
+ * OnKillFocus: Handle the EN_KILLFOCUS and validate data.
+ *
+ * @param None
+ */
+//=============================================================================
+void CNumericEdit::OnKillFocus()
+{
+ this->ValidateData();
+}
+
+//=============================================================================
+/**
+ * OnUpdate: Handle the EN_UPDATE and handle validation before the control is updated.
+ *
+ * @param None
+ */
+//=============================================================================
+void CNumericEdit::OnUpdate()
+{
+ // TODO: If this is a RICHEDIT control, the control will not
+ // send this notification unless you override the CEdit::OnInitDialog()
+ // function to send the EM_SETEVENTMASK message to the control
+ // with the ENM_UPDATE flag ORed into the lParam mask.
+
+ // TODO: Add your control notification handler code here
+
+ this->ValidateData();
+}
+
+//=============================================================================
+/**
+ * OnChar: Handle the WM_CHAR and validate the key that was pressed.
+ *
+ * @param inChar
+ * @param inRepCnt
+ * @param inFlags
+ */
+//=============================================================================
+void CNumericEdit::OnChar(UINT inChar, UINT inRepCnt, UINT inFlags)
+{
+ if (IsNumeric(inChar) || IsEditKey(inChar)) {
+ CEdit::OnChar(inChar, inRepCnt, inFlags);
+ }
+}
+
+//=============================================================================
+/**
+ * OnKeyDown: Handle the WM_KEYDOWN and validate the key that was pressed.
+ *
+ * @param inChar
+ * @param inRepCnt
+ * @param inFlags
+ */
+//=============================================================================
+void CNumericEdit::OnKeyDown(UINT inChar, UINT inRepCnt, UINT inFlags)
+{
+ if (inChar == VK_UP) {
+ this->IncrementValue(+1);
+ this->SetSel(0, -1);
+ } else if (inChar == VK_DOWN) {
+ this->IncrementValue(-1);
+ this->SetSel(0, -1);
+ } else {
+ CEdit::OnKeyDown(inChar, inRepCnt, inFlags);
+ }
+}
+
+//=============================================================================
+/**
+ * IsEditKey: Determine if the pressed key was an edit key.
+ *
+ * @param inChar Key character value.
+ *
+ * @return TRUE if an editing key
+ */
+//=============================================================================
+BOOL CNumericEdit::IsEditKey(UINT inChar)
+{
+ BOOL theEditKeyFlag = FALSE;
+
+ if (inChar == VK_DELETE || inChar == VK_BACK || inChar == VK_LEFT || inChar == VK_RIGHT
+ || inChar == VK_HOME || inChar == VK_END || inChar == VK_UP || inChar == VK_DOWN)
+ theEditKeyFlag = TRUE;
+
+ return theEditKeyFlag;
+}
+
+//=============================================================================
+/**
+ * IsNumeric: Determine if the pressed key was a numeric key.
+ *
+ * @param inChar Key character value.
+ *
+ * @return TRUE if a numeric key
+ */
+//=============================================================================
+BOOL CNumericEdit::IsNumeric(UINT inChar)
+{
+ CString theNumericCheck = L"0123456789";
+ BOOL theNumericFlag = FALSE;
+
+ // Determine if the character is a numeric value.
+ if (theNumericCheck.Find((TCHAR)inChar) != -1) {
+ theNumericFlag = TRUE;
+ }
+
+ return theNumericFlag;
+}
+
+//=============================================================================
+/**
+ * ValidateData: Validates the value in the control to keep within range.
+ *
+ * @param None
+ */
+//=============================================================================
+void CNumericEdit::ValidateData()
+{
+ CString theStrValue;
+ long theValue = 0;
+ BOOL theChangeFlag = FALSE;
+
+ // Get the current text in the control.
+ this->GetWindowText(theStrValue);
+ if (theStrValue.GetLength() == 0) {
+ theValue = 0;
+ theChangeFlag = TRUE;
+ } else
+ // Check to see that a range has been set
+ if (m_RangeLow != -1 && m_RangeHigh != -1) {
+ theValue = this->GetValue();
+
+ // Check against the low and high range
+ if (theValue < m_RangeLow) {
+ theValue = m_RangeLow;
+ theChangeFlag = TRUE;
+ } else if (theValue > m_RangeHigh) {
+ theValue = m_RangeHigh;
+ theChangeFlag = TRUE;
+ }
+ }
+
+ // Has the value changed?
+ if (theChangeFlag) {
+ // Update the control's value.
+ this->SetValue(theValue);
+ this->SetSel(0, -1);
+ }
+}
+
+//=============================================================================
+/**
+ * IncrementValue: Increments the current value by inAmount
+ *
+ * @param inAmount Amount to change the current control value.
+ */
+//=============================================================================
+void CNumericEdit::IncrementValue(long inAmount)
+{
+ long theValue;
+
+ // Get the current value
+ theValue = this->GetValue();
+
+ // Change theValue
+ theValue += inAmount;
+
+ // Has a range been set?
+ if (m_RangeLow != -1 && m_RangeHigh != -1) {
+ if (theValue < m_RangeLow) {
+ theValue = m_RangeLow;
+
+ if (m_WrapFlag)
+ theValue = m_RangeHigh;
+ } else if (theValue > m_RangeHigh) {
+ theValue = m_RangeHigh;
+
+ if (m_WrapFlag)
+ theValue = m_RangeLow;
+ }
+ }
+
+ this->SetValue(theValue);
+}
+
+//=============================================================================
+/**
+ * OnMouseWheel: Handle the WM_MOUSEWHEEL message.
+ *
+ * @param inFlags
+ * @param inzDelta
+ * @param inPoint
+ *
+ * @return TRUE if processed.
+ */
+//=============================================================================
+BOOL CNumericEdit::OnMouseWheel(UINT inFlags, short inzDelta, CPoint inPoint)
+{
+ long theWheelDelta = 0;
+
+ if (inzDelta != 0)
+ theWheelDelta = inzDelta / abs(inzDelta);
+
+ this->IncrementValue(theWheelDelta);
+
+ this->SetSel(0, -1);
+
+ return CEdit::OnMouseWheel(inFlags, inzDelta, inPoint);
+}
diff --git a/src/Authoring/Studio/_Win/UI/NumericEdit.h b/src/Authoring/Studio/_Win/UI/NumericEdit.h
new file mode 100644
index 00000000..61a4017a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/NumericEdit.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_NUMERICEDIT_H__F08CC602_8CD7_4A27_AC46_48A80A7D8FD0__INCLUDED_)
+#define AFX_NUMERICEDIT_H__F08CC602_8CD7_4A27_AC46_48A80A7D8FD0__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "MenuEdit.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CNumericEdit window
+
+class CNumericEdit : public CMenuEdit
+{
+ // Construction
+public:
+ CNumericEdit();
+
+ // Attributes
+public:
+ void SetAllowWrap(BOOL inWrapFlag) { m_WrapFlag = inWrapFlag; }
+
+protected:
+ long m_RangeLow, m_RangeHigh;
+ BOOL m_WrapFlag;
+
+ // Operations
+public:
+ long GetValue();
+
+ void SetRange(long inRangeLow, long inRangeHigh);
+ void SetValue(long inValue);
+
+protected:
+ void IncrementValue(long inAmount);
+ BOOL IsEditKey(UINT inChar);
+ BOOL IsNumeric(UINT inChar);
+
+ void ValidateData();
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CNumericEdit)
+ //}}AFX_VIRTUAL
+
+ // Implementation
+public:
+ virtual ~CNumericEdit();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CNumericEdit)
+ afx_msg void OnKillFocus();
+ afx_msg void OnUpdate();
+ afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
+ afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
+ afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
+ //}}AFX_MSG
+
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_NUMERICEDIT_H__F08CC602_8CD7_4A27_AC46_48A80A7D8FD0__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.cpp b/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.cpp
new file mode 100644
index 00000000..64d7e2f0
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.cpp
@@ -0,0 +1,510 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "UICOptions.h"
+#include "SceneView.h"
+#include "Doc.h"
+#include "StudioProjectSettings.h"
+#include "Dispatch.h"
+#include "HotKeys.h"
+#include "MasterP.h"
+#include "StudioApp.h"
+#include "IStudioRenderer.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "PlatformStrings.h"
+#include "PlayerContainerWnd.h"
+#include "UICDMStudioSystem.h"
+#include "Core.h"
+#include "MainFrm.h"
+#include "StudioUtils.h"
+
+#include <QtWidgets/qscrollbar.h>
+#include <QtGui/qevent.h>
+
+//==============================================================================
+// Class CPlayerContainerWnd
+//==============================================================================
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CPlayerContainerWnd::CPlayerContainerWnd(CSceneView *inSceneView)
+ : QAbstractScrollArea(inSceneView)
+ , m_SceneView(inSceneView)
+ , m_PlayerWnd(NULL)
+ , m_IsMouseDown(false)
+ , m_IsMiddleMouseDown(false)
+ , m_ViewMode(VIEW_SCENE)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CPlayerContainerWnd::~CPlayerContainerWnd()
+{
+}
+
+bool CPlayerContainerWnd::ShouldHideScrollBars()
+{
+ return m_ViewMode == VIEW_EDIT || g_StudioApp.IsAuthorZoom();
+}
+
+//==============================================================================
+/**
+ * SetPlayerWndPosition: Sets the position of the child player window
+ *
+ * Called when the view is scrolled to position the child player window
+ * @param outLeftPresentationEdge the left edge of the presentation, with the scroll position
+ *taken into consideration
+ * @param outTopPresentionEdge the top edge of the presentation, with the scroll
+ *position taken into consideration
+ *
+ */
+//==============================================================================
+void CPlayerContainerWnd::SetPlayerWndPosition(long &outLeftPresentationEdge,
+ long &outTopPresentionEdge)
+{
+ long theHScrollPosition, theVScrollPosition;
+ // Negate to adjust actual client positions
+ theHScrollPosition = -horizontalScrollBar()->value();
+ theVScrollPosition = -verticalScrollBar()->value();
+
+ QRect theClientRect = rect();
+
+ // Horizontal scrollbar does not exist
+ if (m_ClientRect.width() < theClientRect.width()) {
+ theHScrollPosition =
+ theClientRect.left() + (theClientRect.width() / 2) - (m_ClientRect.width() / 2);
+ outLeftPresentationEdge = theHScrollPosition;
+ } else // This stays a negated value
+ outLeftPresentationEdge = theHScrollPosition;
+
+ // Vertical scrollbar does not exist
+ if (m_ClientRect.height() < theClientRect.height()) {
+ theVScrollPosition =
+ theClientRect.top() + (theClientRect.height() / 2) - (m_ClientRect.height() / 2);
+ outTopPresentionEdge = theVScrollPosition;
+ } else // This stays a negated value
+ outTopPresentionEdge = theVScrollPosition;
+
+ // Move the child player window
+ m_PlayerWnd->setGeometry(QRect(QPoint(theHScrollPosition, theVScrollPosition), m_ClientRect.size()));
+}
+
+//==============================================================================
+/**
+ * SetScrollRanges: Sets the scroll ranges when the view is being resized
+ */
+//==============================================================================
+void CPlayerContainerWnd::SetScrollRanges()
+{
+
+ long theScrollWidth = 0;
+ long theScrollHeight = 0;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ if (ShouldHideScrollBars()) {
+ horizontalScrollBar()->setRange(0, 0);
+ verticalScrollBar()->setRange(0, 0);
+ horizontalScrollBar()->setValue(0);
+ verticalScrollBar()->setValue(0);
+ } else
+#endif
+ {
+ QSize theSize = GetEffectivePresentationSize();
+
+ theScrollWidth = theSize.width();
+ theScrollHeight = theSize.height();
+
+
+ // Set scrollbar ranges
+ horizontalScrollBar()->setRange(0, theScrollWidth - width());
+ verticalScrollBar()->setRange(0, theScrollHeight - height());
+ horizontalScrollBar()->setPageStep(width());
+ verticalScrollBar()->setPageStep(height());
+ horizontalScrollBar()->setVisible(true);
+ verticalScrollBar()->setVisible(true);
+ }
+}
+
+//==============================================================================
+/**
+ * OnRulerGuideToggled:
+ * Handle scrollbar position when ruler, guide has been toggled
+ *
+ */
+//==============================================================================
+void CPlayerContainerWnd::OnRulerGuideToggled()
+{
+ int scrollAmount = g_StudioApp.GetRenderer().AreGuidesEnabled() ? 16 : -16;
+ bool hasHorz = horizontalScrollBar()->isVisible();
+ bool hasVert = verticalScrollBar()->isVisible();
+ int hscrollPos = 0, vscrollPos = 0;
+ if (hasHorz) {
+ hscrollPos = qMax(horizontalScrollBar()->value() + scrollAmount, 0);
+ }
+ if (hasVert) {
+ vscrollPos = qMax(verticalScrollBar()->value() + scrollAmount, 0);
+ }
+ horizontalScrollBar()->setValue(hscrollPos);
+ verticalScrollBar()->setValue(vscrollPos);
+ m_PlayerWnd->update();
+}
+
+//==============================================================================
+/**
+ * RecenterClient: Recenters the Client rect in the View's client area.
+ */
+//==============================================================================
+void CPlayerContainerWnd::RecenterClient()
+{
+ QRect theViewClientRect = rect();
+ QSize theClientSize;
+ m_ClientRect = theViewClientRect;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ if (ShouldHideScrollBars()) {
+ } else
+#endif
+ {
+ theClientSize = GetEffectivePresentationSize();
+
+ // Only center if we need to scroll
+ if (theClientSize.width() > theViewClientRect.width()) {
+ // compute the size of the client rectangle to display
+ m_ClientRect.setLeft(
+ (theViewClientRect.width() / 2) - (theClientSize.width() / 2) - (theClientSize.width() % 2));
+ m_ClientRect.setRight((theViewClientRect.width() / 2) + (theClientSize.width() / 2));
+ }
+
+ if (theClientSize.height() > theViewClientRect.height()) {
+ m_ClientRect.setTop(
+ (theViewClientRect.height() / 2) - (theClientSize.height() / 2) - (theClientSize.height() % 2));
+ m_ClientRect.setBottom((theViewClientRect.height() / 2) + (theClientSize.height() / 2));
+ }
+ }
+
+ QRect glRect = m_ClientRect;
+ glRect.setWidth(int(devicePixelRatio() * m_ClientRect.width()));
+ glRect.setHeight(int(devicePixelRatio() * m_ClientRect.height()));
+ g_StudioApp.GetRenderer().SetViewRect(glRect);
+}
+
+//==============================================================================
+/**
+ * OnLButtonDown: Called whenever the user left clicks in the view.
+ * This processes the WM_LBUTTONDOWN message. This message is generated whenever
+ * the user left clicks in the view. Since this could involve selection of an item
+ * in the scene, it may (if the click is in the Client rect) call ProcessMouseClick()
+ * on the Document to perform the selection.
+ * @param inFlags the flags passed in from the message call
+ * @param inPoint the point where the event takes place
+ */
+void CPlayerContainerWnd::mousePressEvent(QMouseEvent *event)
+{
+ if ((event->button() == Qt::LeftButton) || (event->button() == Qt::RightButton)) {
+ long theToolMode = g_StudioApp.GetToolMode();
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDown(SceneDragSenderType::Matte, event->pos(),
+ theToolMode);
+ m_IsMouseDown = true;
+ } else if (event->button() == Qt::MiddleButton) {
+#ifdef INCLUDE_EDIT_CAMERA
+ const bool theCtrlKeyIsDown = event->modifiers() & Qt::ControlModifier;
+ const bool theAltKeyIsDown = event->modifiers() & Qt::AltModifier;
+
+ bool theToolChanged = false;
+ if (rect().contains(event->pos()) && !IsDeploymentView()) {
+ // If both the control key and the Alt key is not down
+ if (!theCtrlKeyIsDown && !theAltKeyIsDown) {
+ // press Scroll Wheel Click
+ // Do Camera Pan
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ theToolChanged = true;
+ } else if ((theAltKeyIsDown) && (!theCtrlKeyIsDown)) {
+ // press Alt-Scroll Wheel Click
+ // Do Camera Rotate if we are in 3D Camera
+ if (g_StudioApp.GetRenderer().DoesEditCameraSupportRotation(
+ g_StudioApp.GetRenderer().GetEditCamera())) {
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ theToolChanged = true;
+ }
+ }
+ }
+
+ if (theToolChanged) {
+ Q_EMIT toolChanged();
+ m_SceneView->SetViewCursor();
+
+ long theToolMode = g_StudioApp.GetToolMode();
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDown(SceneDragSenderType::Matte,
+ event->pos(), theToolMode);
+ m_IsMouseDown = true;
+ m_IsMiddleMouseDown = true;
+ }
+#else
+ Q_UNUSED(inFlags);
+ Q_UNUSED(inPoint);
+#endif
+ }
+}
+
+//==============================================================================
+/**
+ * OnLButtonUp: Called whenever the user releases the left mouse button.
+ *
+ * This processes the WM_LBUTTONUP message. This message is generated whenever
+ * the left mouse button. We release the mouse capture to stop dragging.
+ *
+ * @param inFlags The flags passed in from the message call
+ * @param inPoint The point where the event takes place
+ */
+//==============================================================================
+void CPlayerContainerWnd::mouseReleaseEvent(QMouseEvent *event)
+{
+ if ((event->button() == Qt::LeftButton) || (event->button() == Qt::RightButton)) {
+ // Need to commit the current command when we have a mouse up. :)
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseUp(SceneDragSenderType::Matte);
+ g_StudioApp.GetCore()->CommitCurrentCommand();
+ m_IsMouseDown = false;
+ } else if (event->button() == Qt::MiddleButton) {
+ #ifdef INCLUDE_EDIT_CAMERA
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseUp(SceneDragSenderType::Matte);
+ g_StudioApp.GetCore()->CommitCurrentCommand();
+ if (m_IsMiddleMouseDown) {
+ m_IsMouseDown = false;
+ m_IsMiddleMouseDown = false;
+
+ const bool theCtrlKeyIsDown = event->modifiers() & Qt::ControlModifier;
+ const bool theAltKeyIsDown = event->modifiers() & Qt::AltModifier;
+
+ if (!IsDeploymentView()) {
+ if (!theCtrlKeyIsDown && !theAltKeyIsDown) {
+ // none of the modifier key is pressed... reset to previous tool
+ m_SceneView->RestorePreviousTool();
+ } else if (theCtrlKeyIsDown && theAltKeyIsDown) {
+ // since both modifier is down... let the ctrl has priority
+ m_SceneView->SetToolOnCtrl();
+ }
+ m_SceneView->SetViewCursor();
+ Q_EMIT toolChanged();
+ }
+ }
+ #endif
+ }
+}
+
+//==============================================================================
+/**
+ * OnMouseMove: Called whenever the user moves the mouse in the window.
+ *
+ * This processes the WM_MOUSEMOVE message. This message is generated whenever
+ * the user moves the mouse in the view. This tells lets the document process it
+ * as well since the user could be dragging an object.
+ *
+ * @param inFlags The flags passed in from the message call
+ * @param inPoint The point where the event takes place
+ */
+//==============================================================================
+void CPlayerContainerWnd::mouseMoveEvent(QMouseEvent* event)
+{
+ if (m_IsMouseDown) {
+ UICPROFILE(OnMouseMove);
+
+ long theModifierKeys = 0;
+ if (event->buttons() & Qt::LeftButton)
+ theModifierKeys = CHotKeys::MOUSE_LBUTTON | CHotKeys::GetCurrentKeyModifiers();
+ else if (event->buttons() & Qt::RightButton)
+ theModifierKeys = CHotKeys::MOUSE_RBUTTON | CHotKeys::GetCurrentKeyModifiers();
+
+ long theToolMode = g_StudioApp.GetToolMode();
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDrag(
+ SceneDragSenderType::Matte, event->pos(), theToolMode, theModifierKeys);
+ }
+}
+
+//==============================================================================
+/**
+ * OnMouseWheel: Called whenever the mouse wheel.
+ *
+ * This processes the WM_MOUSEWHEEL message.
+ *
+ * @param inFlags the flags passed in from the message call
+ * @param inPoint the point where the event takes place
+ */
+//==============================================================================
+void CPlayerContainerWnd::wheelEvent(QWheelEvent* event)
+{
+#ifdef INCLUDE_EDIT_CAMERA
+ // Note : Mouse wheel is a special animal of the scene drag tool. We dont change the tool
+ // so as not to affect the toolbar button and the view cursor. This will just do the zoom
+ // and the cursor is not changed.
+
+ const bool theCtrlKeyIsDown = event->modifiers() & Qt::ControlModifier;
+ const bool theAltKeyIsDown = event->modifiers() & Qt::AltModifier;
+
+ // Keeping these codes here, till we finalized the behavior and confirm these not needed
+ // long theToolMode = g_StudioApp.GetToolMode( );
+ //// If both the control key and the Alt key is not down
+ // if ( !theCtrlKeyIsDown && !theAltKeyIsDown && !IsDeploymentView( ) )
+ //{
+ // if ( theToolMode != STUDIO_TOOLMODE_CAMERA_ZOOM )
+ // {
+ // g_StudioApp.SetToolMode( STUDIO_TOOLMODE_CAMERA_ZOOM );
+ // theToolMode = STUDIO_TOOLMODE_CAMERA_ZOOM;
+ // m_MouseWheeling = true;
+
+ // Q_EMIT toolChanged();
+ // SetViewCursor( );
+ // }
+ //}
+
+ // Mouse Wheel
+ // Do Camera Zoom
+ if (!theCtrlKeyIsDown && !theAltKeyIsDown && !IsDeploymentView())
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseWheel(
+ SceneDragSenderType::Matte, event->delta(), STUDIO_TOOLMODE_CAMERA_ZOOM);
+#else
+ Q_UNUSED(event);
+#endif
+}
+
+void CPlayerContainerWnd::scrollContentsBy(int, int)
+{
+ long x, y;
+ SetPlayerWndPosition(x, y);
+}
+
+//==============================================================================
+/**
+ * Set the view mode of the current scene view, whether we are in editing mode
+ * or deployment mode. For editing mode, we want to use the full scene area without
+ * any matte area.
+ * @param inViewMode the view mode of this scene
+ */
+void CPlayerContainerWnd::SetViewMode(EViewMode inViewMode)
+{
+ m_ViewMode = inViewMode;
+ m_SceneView->RecheckSizingMode();
+ if (m_ViewMode == VIEW_SCENE) {
+ // switching from edit mode to deployment mode, release the edit camera tool and set it to
+ // object move
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+ bool theIsCameraTool = (theCurrentToolSettings & STUDIO_CAMERATOOL_MASK ? true : false);
+ if (theIsCameraTool) {
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_MOVE);
+ m_SceneView->SetToolMode(STUDIO_TOOLMODE_MOVE);
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * return the view mode of the current scene view, whether we are in editing mode
+ * or deployment mode. For editing mode, we want to use the full scene area without
+ * any matte area.
+ * @return the current view mode
+ */
+CPlayerContainerWnd::EViewMode CPlayerContainerWnd::GetViewMode()
+{
+ return m_ViewMode;
+}
+
+//==============================================================================
+/**
+ * Checks whether we are in deployment view mode.
+ * @return true if is in deployment view mode, else false
+ */
+bool CPlayerContainerWnd::IsDeploymentView()
+{
+ return m_ViewMode == VIEW_SCENE ? true : false;
+}
+
+QSize CPlayerContainerWnd::GetEffectivePresentationSize() const
+{
+ CPt cSize = g_StudioApp.GetCore()->GetStudioProjectSettings()->GetPresentationSize();
+ QSize theSize(cSize.x, cSize.y);
+
+ // If we have guides, resize the window with enough space for the guides as well as the
+ // presentation
+ // This is a very dirty hack because we are of course hardcoding the size of the guides.
+ // If the size of the guides never changes, the bet paid off.
+ if (g_StudioApp.GetRenderer().AreGuidesEnabled()) {
+ theSize += QSize(32, 32);
+ }
+
+ return theSize / devicePixelRatio();
+}
+
+//==============================================================================
+
+void CPlayerContainerWnd::SetPlayerWnd(CPlayerWnd *inPlayerWnd)
+{
+ m_PlayerWnd = inPlayerWnd;
+ viewport()->setBackgroundRole(QPalette::Dark);
+ m_PlayerWnd->SetContainerWnd(this);
+ m_PlayerWnd->setParent(viewport());
+ m_PlayerWnd->setVisible(true);
+ m_PlayerWnd->resize(m_PlayerWnd->sizeHint());
+}
+
+//==============================================================================
+/**
+ * OnSize: Handles the WM_SIZE message
+ *
+ * Recenters the Client and recaluclates the matte when a resize message is
+ * generated.
+ *
+ * @param nType Specifies the type of resizing requested.
+ * @param cx Specifies the new width of the client area.
+ * @param cy Specifies the new height of the client area.
+ */
+//==============================================================================
+void CPlayerContainerWnd::resizeEvent(QResizeEvent* event)
+{
+ QAbstractScrollArea::resizeEvent(event);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ SetScrollRanges();
+#endif
+}
diff --git a/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.h b/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.h
new file mode 100644
index 00000000..d4ac3cf5
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PlayerContainerWnd.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PLAYER_CONTAINER_WND_H
+#define INCLUDED_PLAYER_CONTAINER_WND_H 1
+
+#pragma once
+
+#include <QScrollArea>
+
+class CSceneView;
+class CStudioApp;
+class CPlayerWnd;
+class CWGLRenderContext;
+
+//==============================================================================
+// Class CPlayerContainerWnd
+//==============================================================================
+
+class CPlayerContainerWnd : public QAbstractScrollArea
+{
+ Q_OBJECT
+ //==============================================================================
+ // Typedefs
+ //==============================================================================
+public:
+ typedef enum {
+ VIEW_EDIT = 0, ///< Edit View
+ VIEW_SCENE, ///< Scene View
+ } EViewMode;
+
+public:
+ CPlayerContainerWnd(CSceneView *inSceneView);
+ virtual ~CPlayerContainerWnd();
+
+ void SetPlayerWnd(CPlayerWnd *inPlayerWnd);
+ void SetPlayerWndPosition(long &outLeftPresentationEdge, long &outTopPresentionEdge);
+ void SetScrollRanges();
+ void RecenterClient();
+ void OnRulerGuideToggled();
+
+ void SetViewMode(EViewMode inViewMode);
+ EViewMode GetViewMode();
+ bool IsDeploymentView();
+
+ QRect GetDisplayedClientRect() const { return m_ClientRect; }
+ bool IsMouseDown() const { return m_IsMouseDown; }
+ bool IsMiddleMouseDown() const { return m_IsMiddleMouseDown; }
+
+ QSize GetEffectivePresentationSize() const;
+
+Q_SIGNALS:
+ void toolChanged();
+
+protected:
+ void resizeEvent(QResizeEvent *) override;
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void wheelEvent(QWheelEvent *) override;
+
+ void scrollContentsBy(int, int) override;
+
+private:
+ CPlayerContainerWnd() {}
+ bool ShouldHideScrollBars();
+
+protected:
+ CSceneView *m_SceneView; ///< Pointer to the SceneView for rulers manipulation
+ CPlayerWnd *m_PlayerWnd; ///< Pointer to the window control that contains client
+ QRect m_ClientRect; ///< The rect where the client is drawn
+ bool m_IsMouseDown; ///< true if the mouse (any button) is down
+ bool m_IsMiddleMouseDown; ///< true if the middle mouse ( or scroll wheel ) is down
+ EViewMode m_ViewMode;
+};
+
+#endif // INCLUDED_PLAYER_CONTAINER_WND_H
diff --git a/src/Authoring/Studio/_Win/UI/PlayerWnd.cpp b/src/Authoring/Studio/_Win/UI/PlayerWnd.cpp
new file mode 100644
index 00000000..ca9c7c51
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PlayerWnd.cpp
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "PlayerWnd.h"
+#include "MainFrm.h"
+#include "SceneView.h"
+#include "Dispatch.h"
+#include "MasterP.h"
+#include "HotKeys.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "Dispatch.h"
+#include "HotKeys.h"
+#include "MouseCursor.h"
+#include "ResourceCache.h"
+#include "SceneDropTarget.h"
+#include "Core.h"
+#include "IDragable.h"
+#include "WGLRenderContext.h"
+#include "IStudioRenderer.h"
+
+#include <QMouseEvent>
+
+//==============================================================================
+// Class CPlayerWnd
+//==============================================================================
+
+CPlayerWnd::CPlayerWnd(QWidget *parent)
+ : QOpenGLWidget(parent)
+ , m_ContainerWnd(nullptr)
+ , m_IsMouseDown(false)
+ , m_PreviousToolMode(0)
+ , m_FitClientToWindow(false)
+{
+ m_LastKnownMousePosition = QPoint(-1, -1);
+
+ setAcceptDrops(true);
+ RegiserForDnd(this);
+ AddMainFlavor(EUIC_FLAVOR_FILE);
+ AddMainFlavor(EUIC_FLAVOR_ASSET_UICFILE);
+ AddMainFlavor(EUIC_FLAVOR_ASSET_LIB);
+ AddMainFlavor(EUIC_FLAVOR_BASIC_OBJECTS);
+
+ setFormat(CWGLRenderContext::selectSurfaceFormat(this));
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CPlayerWnd::~CPlayerWnd()
+{
+}
+
+//==============================================================================
+/**
+ * OnMouseMove: Handle the WM_MOUSEMOVE message
+ *
+ * @param inFlags flags passed in with th mouse move message
+ * @param inPoint where the mouse is
+ */
+//==============================================================================
+void CPlayerWnd::mouseMoveEvent(QMouseEvent *event)
+{
+ if (event->buttons() & Qt::MiddleButton) {
+ // Middle button events are handled by the parent CPlayerContainerWnd
+ event->ignore();
+ } else {
+ if (m_IsMouseDown) {
+ long theModifierKeys = 0;
+ if (event->buttons() & Qt::LeftButton)
+ theModifierKeys = CHotKeys::MOUSE_LBUTTON | CHotKeys::GetCurrentKeyModifiers();
+ else if (event->buttons() & Qt::RightButton)
+ theModifierKeys = CHotKeys::MOUSE_RBUTTON | CHotKeys::GetCurrentKeyModifiers();
+
+ long theToolMode = g_StudioApp.GetToolMode();
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDrag(
+ SceneDragSenderType::SceneWindow, event->pos(), theToolMode, theModifierKeys);
+ } else {
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseMove(
+ SceneDragSenderType::SceneWindow, event->pos());
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * OnSize: Handle the WM_SIZE message
+ *
+ * @param nType Passed to the base class
+ * @param cx change in x window size
+ * @param cy change in y window size
+ */
+//==============================================================================
+void CPlayerWnd::resizeEvent(QResizeEvent *event)
+{
+ QOpenGLWidget::resizeEvent(event);
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CWnd *theChildWnd = this->GetWindow(GW_CHILD);
+
+ if (theChildWnd != nullptr) {
+ CRect theWndRect;
+
+ // Set the child window at the same position as the parent
+ this->GetWindowRect(&theWndRect);
+
+ theChildWnd->ScreenToClient(&theWndRect);
+ theChildWnd->MoveWindow(&theWndRect, FALSE);
+ }
+#endif
+ update();
+}
+
+//==============================================================================
+/**
+ * OnLButtonDown: Handle the WM_LBUTTONDOWN message
+ *
+ * @param inFlags Flags passed in from the message
+ * @param inPoint The point wher the button was clicked
+ */
+//==============================================================================
+void CPlayerWnd::mousePressEvent(QMouseEvent *event)
+{
+ const Qt::MouseButton btn = event->button();
+ if ((btn == Qt::LeftButton) || (btn == Qt::RightButton)) {
+ long theToolMode = g_StudioApp.GetToolMode();
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDown(SceneDragSenderType::SceneWindow,
+ event->pos(), theToolMode);
+ m_IsMouseDown = true;
+ } else if (btn == Qt::MiddleButton) {
+ event->ignore();
+ }
+}
+
+//==============================================================================
+/**
+ * OnLButtonUp: Called whenever the user releases the left mouse button.
+ *
+ * This processes the WM_LBUTTONUP message. This message is generated whenever
+ * the left mouse button. We release the mouse capture to stop dragging.
+ *
+ * @param inFlags the flags passed in from the message call
+ * @param the point where the lbutton up takes place
+ */
+//==============================================================================
+void CPlayerWnd::mouseReleaseEvent(QMouseEvent *event)
+{
+ const Qt::MouseButton btn = event->button();
+ if ((btn == Qt::LeftButton) || (btn == Qt::RightButton)) {
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseUp(SceneDragSenderType::SceneWindow);
+ g_StudioApp.GetCore()->CommitCurrentCommand();
+ m_IsMouseDown = false;
+ } else if (btn == Qt::MiddleButton) {
+ event->ignore();
+ }
+}
+
+void CPlayerWnd::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ g_StudioApp.GetCore()->GetDispatch()->FireSceneMouseDblClick(
+ SceneDragSenderType::SceneWindow, event->pos());
+}
+
+
+bool CPlayerWnd::OnDragWithin(CDropSource &inSource)
+{
+ CSceneViewDropTarget theTarget;
+ return theTarget.Accept(inSource);
+}
+bool CPlayerWnd::OnDragReceive(CDropSource &inSource)
+{
+ CSceneViewDropTarget theTarget;
+ Q_EMIT dropReceived();
+ return theTarget.Drop(inSource);
+}
+void CPlayerWnd::OnDragLeave()
+{
+}
+
+//==============================================================================
+/**
+ * SetContainerWnd: keep track of a pointer to the containing window
+ *
+ * @param inContainerWnd pointer to what the containing window will be set to
+ */
+//==============================================================================
+void CPlayerWnd::SetContainerWnd(CPlayerContainerWnd *inContainerWnd)
+{
+ m_ContainerWnd = inContainerWnd;
+ updateGeometry();
+}
+
+QSize CPlayerWnd::sizeHint() const
+{
+ if (m_ContainerWnd)
+ return m_ContainerWnd->GetEffectivePresentationSize();
+ else
+ return QOpenGLWidget::sizeHint();
+}
+
+void CPlayerWnd::initializeGL()
+{
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.GetRenderer());
+ if (theRenderer.IsInitialized() == false) {
+ try {
+ theRenderer.Initialize(this);
+ } catch (...) {
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+ wchar_t theBufferString[MAX_ENTRY_LENGTH];
+
+ _aswprintf(theBufferString, MAX_ENTRY_LENGTH - 1,
+ L"Unable to initialize OpenGL.\nThis may be because your graphic device is "
+ L"not sufficient, or simply because your driver is too old.\n\nPlease try "
+ L"upgrading your graphics driver and try again.");
+ ::wcsncat(theBufferString, L"\r\n\r\n", MAX_ENTRY_LENGTH - 1);
+
+ ::MessageBoxW(nullptr, theBufferString, L"Fatal Error",
+ MB_OK | MB_ICONSTOP | MB_DEFBUTTON1);
+#endif
+ exit(1);
+ }
+ }
+}
+
+void CPlayerWnd::paintGL()
+{
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.GetRenderer());
+ // don't use request render here, this has to be
+ // synchronous inside paintGL
+ theRenderer.RenderNow();
+}
+
+void CPlayerWnd::resizeGL(int width, int height)
+{
+ // this also passes the new FBO to the CWGLContext
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.GetRenderer());
+ theRenderer.SetViewRect(QRect(0, 0, width * devicePixelRatio(),
+ height * devicePixelRatio()));
+}
diff --git a/src/Authoring/Studio/_Win/UI/PlayerWnd.h b/src/Authoring/Studio/_Win/UI/PlayerWnd.h
new file mode 100644
index 00000000..25dfd949
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PlayerWnd.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PLAYERWND_H
+#define INCLUDED_PLAYERWND_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "DropContainer.h"
+#include "PlayerContainerWnd.h"
+
+#include <QOpenGLWidget>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CPlayerContainerWnd;
+class CStudioApp;
+class CMouseCursor;
+class CHotkeys;
+
+//==============================================================================
+// Class CPlayerWnd
+//==============================================================================
+
+class CPlayerWnd : public QOpenGLWidget, public CWinDropContainer
+{
+ Q_OBJECT
+public:
+ explicit CPlayerWnd(QWidget *parent = nullptr);
+ ~CPlayerWnd();
+
+ void SetContainerWnd(CPlayerContainerWnd *inSceneView);
+
+ QSize sizeHint() const override;
+
+ bool OnDragWithin(CDropSource &inSource) override;
+ bool OnDragReceive(CDropSource &inSource) override;
+ void OnDragLeave() override;
+ void OnReflectMouse(CPt &inPoint, Qt::KeyboardModifiers inFlags) override {}
+
+protected:
+
+ CPlayerContainerWnd *m_ContainerWnd;
+ bool m_IsMouseDown;
+ long m_PreviousToolMode; ///< The previous tool mode (used when toggling with hotkeys to switch
+ ///back to previous mode on release)
+ bool m_FitClientToWindow; ///< True if we are in Fit to Window Mode
+
+ QPoint m_LastKnownMousePosition;
+
+Q_SIGNALS:
+ void dropReceived();
+
+protected:
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseDoubleClickEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+
+ void initializeGL() override;
+ void paintGL() override;
+ void resizeGL(int width, int height) override;
+};
+
+#endif // INCLUDED_PLAYERWND_H
diff --git a/src/Authoring/Studio/_Win/UI/PopupWnd.cpp b/src/Authoring/Studio/_Win/UI/PopupWnd.cpp
new file mode 100644
index 00000000..f0b5d2cd
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PopupWnd.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PopupWnd.h"
+#include "AppFonts.h"
+
+#include "StudioPreferences.h"
+
+//==============================================================================
+/**
+ * Constructor: Initialize the object.
+ *
+ * Creates default fonts, colors, etc.
+ */
+CPopupWnd::CPopupWnd()
+ : m_CenterPt(0, 0)
+ , m_IsStationary(false)
+{
+ // get the proper font from appfonts
+ m_Font = CAppFonts::GetInstance()->GetNormalFont();
+
+ // Set the default colors (use default system tooltip colors)
+ m_TextColor = ::GetSysColor(COLOR_INFOTEXT);
+ m_BkColor = ::GetSysColor(COLOR_INFOBK);
+ ::CColor theBGColor(CStudioPreferences::GetTooltipBackgroundColor());
+ m_BkColor = theBGColor.GetRGBColor();
+ m_BkBrush = new CBrush();
+ m_BkBrush->CreateSolidBrush(m_BkColor);
+}
+
+//==============================================================================
+/**
+ * Destructor: Destroys the object and releases associated memory.
+ */
+CPopupWnd::~CPopupWnd()
+{
+ // Clean up GDI objects
+
+ // m_Font.DeleteObject();
+
+ if (m_BkBrush) {
+ DeleteObject(m_BkBrush);
+ delete m_BkBrush;
+ }
+}
+
+//==============================================================================
+// Message Map
+//==============================================================================
+BEGIN_MESSAGE_MAP(CPopupWnd, CWnd)
+//{{AFX_MSG_MAP(CPopupWnd)
+ON_WM_PAINT()
+ON_WM_ERASEBKGND()
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * OnPaint: Handles the WM_PAINT windows message.
+ *
+ * Calls GetWindowText() and draws the text to the screen.
+ */
+void CPopupWnd::OnPaint()
+{
+ CPaintDC theDC(this); // device context for painting
+ CString theText;
+
+ GetWindowText(theText);
+ if (theText.GetLength() > 0) {
+
+ // Change the font
+ CFont *theOldFont = theDC.SelectObject(m_Font);
+ theDC.SetTextColor(m_TextColor);
+ theDC.SetBkColor(m_BkColor);
+
+ // Paint the text
+ CRect theClientRect;
+ this->GetClientRect(theClientRect);
+ theDC.DrawText(theText, theClientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+
+ // Restore original font
+ theDC.SelectObject(theOldFont);
+ }
+}
+
+//==============================================================================
+/**
+ * SetWindowText: Sets the text for this window.
+ *
+ * Overridden from parent so that we can calculate how big we need to make the
+ * pop-up window so that the text fits. Automatically adjusts the window
+ * size/position to fit the new text string.
+ */
+void CPopupWnd::SetWindowText(LPCTSTR lpszString)
+{
+ CString theNewText;
+ theNewText.Format(L" %s ", lpszString);
+
+ // Set the window text
+ CWnd::SetWindowText(theNewText);
+
+ // Calculate the new window rect with the specified text
+ CDC *theDC = GetDC();
+ CRect theRect(0, 0, 1, 1);
+ CFont *theOldFont = theDC->SelectObject(m_Font);
+
+ theDC->DrawText(theNewText, theRect, DT_CALCRECT);
+ theDC->SelectObject(theOldFont);
+ ::AdjustWindowRect(theRect, this->GetStyle(), FALSE);
+ CPoint theTopLeft = theRect.TopLeft();
+ CPoint theBottomRight = theRect.BottomRight();
+ ::ClientToScreen(this->GetParent()->GetSafeHwnd(), &theTopLeft);
+ ::ClientToScreen(this->GetParent()->GetSafeHwnd(), &theBottomRight);
+ CRect theAdjustedRect(theTopLeft, theBottomRight);
+ int theX = abs((int)(theAdjustedRect.Width() / 2) - m_CenterPt.x);
+ int theY = abs((int)(theAdjustedRect.Height() / 2) - m_CenterPt.y);
+
+ ReleaseDC(theDC);
+
+ // Move the window to the new position, with the new size, still centered at m_CenterPt
+ this->SetWindowPos(nullptr, theX, theY, theAdjustedRect.Width(), theAdjustedRect.Height(),
+ SWP_NOZORDER | SWP_NOACTIVATE);
+}
+
+//==============================================================================
+/**
+ * SetCenterPoint: Changes the center of this window.
+ *
+ * Use this function to specify where you want the center of the pop-up window
+ * to appear. The window is automatically moved to the new center position.
+ */
+void CPopupWnd::SetCenterPoint(CPoint inPoint)
+{
+ if ((m_IsStationary && !IsWindowVisible()) || !m_IsStationary) {
+ // Store the center point
+ m_CenterPt = inPoint;
+
+ // Calculate the new window position so that it is centered at the new point
+ CRect theWindowRect;
+ this->GetWindowRect(theWindowRect);
+ CPoint theTopLeft = theWindowRect.TopLeft();
+ CPoint theBottomRight = theWindowRect.BottomRight();
+ ::ClientToScreen(this->GetParent()->GetSafeHwnd(), &theTopLeft);
+ ::ClientToScreen(this->GetParent()->GetSafeHwnd(), &theBottomRight);
+ CRect theAdjustedRect(theTopLeft, theBottomRight);
+ int theX = abs((int)(theAdjustedRect.Width() / 2) - m_CenterPt.x);
+ int theY = abs((int)(theAdjustedRect.Height() / 2) - m_CenterPt.y);
+
+ // Move the window
+ SetWindowPos(nullptr, theX, theY, theAdjustedRect.Width(), theAdjustedRect.Height(),
+ SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+}
+
+//==============================================================================
+/**
+ * SetBkColor: Changes the background color of this window.
+ *
+ * Defaults to COLOR_INFOBK (tooltip background color). Creates a brush used
+ * in OnEraseBkgnd() of the specified color.
+ *
+ * @param inColor The new background color.
+ */
+void CPopupWnd::SetBkColor(COLORREF inColor)
+{
+ m_BkColor = inColor;
+
+ // If we already have a background brush
+ if (m_BkBrush) {
+ // Kill it
+ DeleteObject(m_BkBrush);
+ delete m_BkBrush;
+ }
+
+ // Make a brush of the specified color
+ m_BkBrush = new CBrush();
+ m_BkBrush->CreateSolidBrush(m_BkColor);
+}
+
+//==============================================================================
+/**
+ * OnEraseBkgnd: Handles the WM_ERASEBKGND windows message.
+ * Redraws the client rect using the current background brush.
+ *
+ * @param pDC Pointer to a display context for drawing.
+ * @return TRUE.
+ */
+BOOL CPopupWnd::OnEraseBkgnd(CDC *pDC)
+{
+ // Erase the client rect using the background brush so it's the right color
+ CRect theClientRect;
+ this->GetClientRect(theClientRect);
+ pDC->FillRect(theClientRect, m_BkBrush);
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * UpdateToolTip: Allows you to change multiple properties on this window at once.
+ *
+ * @param inCenter New center point of the popup window
+ * @param inText New window text for the window
+ * @param inShowWindow true to show the window, false to hide the window
+ */
+void CPopupWnd::UpdateToolTip(CPoint inCenter, CString inText, bool inShowWindow)
+{
+ int theShowFlag = (inShowWindow) ? SW_SHOWNA : SW_HIDE;
+
+ this->SetCenterPoint(inCenter);
+ this->SetWindowText(inText);
+ this->ShowWindow(theShowFlag);
+ this->Invalidate(FALSE);
+ this->UpdateWindow();
+}
diff --git a/src/Authoring/Studio/_Win/UI/PopupWnd.h b/src/Authoring/Studio/_Win/UI/PopupWnd.h
new file mode 100644
index 00000000..da58c9b1
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PopupWnd.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_POP_UP_WND
+#define INCLUDED_POP_UP_WND 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//==============================================================================
+/**
+ * CPopupWnd: Class for making pop-up windows (a replacement for tooltips).
+ *
+ * To use, just create a CPopupWnd, set the text that you want displayed, then
+ * call ShowWindow() to toggle the window on and off. Default color scheme of
+ * this window mimics the system settings for tooltips.
+ */
+class CPopupWnd : public CWnd
+{
+public:
+ CPopupWnd();
+
+ virtual ~CPopupWnd();
+
+ /// Sets the text to display in this window
+ void SetWindowText(LPCTSTR lpszString);
+
+ /// Sets the center point of this window in screen coordinates
+ void SetCenterPoint(CPoint inPoint);
+
+ /// Sets the center point of this window from an x,y pair (screen coordinates)
+ void SetCenterPoint(int inX, int inY) { SetCenterPoint(CPoint(inX, inY)); }
+
+ /// Retrieves the center point of this window (screen coordinates)
+ CPoint GetCenterPoint() { return m_CenterPt; }
+
+ /// Sets the color of the text
+ void SetTextColor(COLORREF inColor) { m_TextColor = inColor; }
+
+ /// Sets the background color of the window
+ void SetBkColor(COLORREF inColor);
+
+ /// Returns a pointer to the CFont object used to draw the text. You can make changes to the
+ /// font using the pointer that's returned,
+ /// just don't delete it. The default font is set in the constructor as Arial 13.
+ CFont *GetFont() { return m_Font; }
+
+ /// Allows you to change multiple properties on this window at once.
+ void UpdateToolTip(CPoint inCenter, CString inText, bool inShowWindow);
+
+ ///
+ void SetStationary(bool inIsStationary) { m_IsStationary = inIsStationary; }
+
+protected:
+ CPoint m_CenterPt; ///< Center of this window in screen coordinates
+ CFont *m_Font; ///< Font for the window text
+ COLORREF m_TextColor; ///< Color of the text
+ COLORREF m_BkColor; ///< Background color of the window
+ CBrush *m_BkBrush; ///< Brush used for paiting the background of the window
+ bool m_IsStationary; ///< If the window is stationary, then it can only be moved while it is not
+ ///showing
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CPopupWnd)
+ afx_msg void OnPaint();
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // INCLUDED_POP_UP_WND
diff --git a/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.cpp b/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.cpp
new file mode 100644
index 00000000..649efa8c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.cpp
@@ -0,0 +1,366 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+#pragma warning(disable : 4127)
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "PreviewOutputDlg.h"
+#include "StudioClipboard.h"
+#include "UICFileTools.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "Mainfrm.h"
+#include "Dialogs.h"
+#include "Strings.h"
+#include "Resource.h"
+#include "Core.h"
+
+// CPreviewOutput dialog
+
+IMPLEMENT_DYNAMIC(CPreviewOutputDlg, CDialog)
+
+CPreviewOutputDlg::CPreviewOutputDlg(const Q3DStudio::CString &inCmd,
+ const Q3DStudio::CString &inExportedFile,
+ CPreviewHelper::EExecMode inViewMode)
+ : CDialog(CPreviewOutputDlg::IDD, nullptr)
+ , m_Cmd(inCmd)
+ , m_ExportedFile(inExportedFile)
+ , m_ViewMode(inViewMode)
+ , m_ReadThreadHandle(nullptr)
+ , m_PreviewProcessHandle(nullptr)
+{
+ m_OutputReadThreadParam.m_ReadHandle = nullptr;
+ m_OutputReadThreadParam.m_Context = nullptr;
+}
+
+CPreviewOutputDlg::~CPreviewOutputDlg()
+{
+ if (m_ReadThreadHandle) {
+ TerminateThread(m_ReadThreadHandle, 0);
+ m_ReadThreadHandle = nullptr;
+ }
+
+ if (m_OutputReadThreadParam.m_ReadHandle) {
+ CloseHandle(m_OutputReadThreadParam.m_ReadHandle);
+ m_OutputReadThreadParam.m_ReadHandle = nullptr;
+ }
+}
+
+void CPreviewOutputDlg::DoDataExchange(CDataExchange *pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_LIST_PREVIEW_STATUS, m_ListCtrl);
+}
+
+//=============================================================================
+/**
+ * Message Handler when dialog is initially opened.
+ */
+//=============================================================================
+BOOL CPreviewOutputDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Set the dialog header
+ Q3DStudio::CString theFile = g_StudioApp.GetCore()->GetDoc()->GetDocumentPath().GetName();
+ if (theFile == "")
+ theFile = ::LoadResourceString(IDS_UNTITLED_DOCUMENT_TITLE);
+ Q3DStudio::CString theDlgHeader;
+ Q3DStudio::CString theOperation;
+ switch (m_ViewMode) {
+ case CPreviewHelper::EXECMODE_PREVIEW:
+ theOperation = ::LoadResourceString(IDS_CONFIG_EXPORT_PREVIEW);
+ break;
+
+ case CPreviewHelper::EXECMODE_DEPLOY:
+ theOperation = ::LoadResourceString(IDS_CONFIG_EXPORT_DEPLOY);
+ break;
+ }
+ theDlgHeader.Format(::LoadResourceString(IDS_CONFIG_PREVIEW_TITLE),
+ static_cast<const wchar_t *>(theOperation),
+ static_cast<const wchar_t *>(theFile));
+ SetWindowText(theDlgHeader);
+
+ // Prepare the list control
+ COLORREF theColor = 0x00C0C0C0;
+ m_ListCtrl.SetBkColor(theColor);
+ m_ListCtrl.SetTextBkColor(theColor);
+ CRect theRect;
+ m_ListCtrl.GetClientRect(theRect);
+ m_ListCtrl.InsertColumn(0, L"", LVCFMT_LEFT,
+ theRect.Width() * 2); // Double the width to see more
+
+ // Start the preview
+ if (!ExecutePreview()) {
+ CUICFile thePreviewerFile(CUICFile::GetApplicationDirectory(), m_ExportedFile);
+ Q3DStudio::CString theMessage;
+ theMessage.Format(::LoadResourceString(IDS_CONFIG_PREVIEW_ERROR_MSG),
+ static_cast<const wchar_t *>(thePreviewerFile.GetAbsolutePosixPath()));
+ g_StudioApp.GetDialogs()->DisplayMessageBox(
+ ::LoadResourceString(IDS_CONFIG_PREVIEW_ERROR_TITLE), theMessage,
+ CUICMessageBox::ICON_ERROR, false);
+ }
+
+ return TRUE;
+}
+
+//=============================================================================
+/**
+ * Handler for copying
+ */
+//=============================================================================
+void CPreviewOutputDlg::OnCopyText()
+{
+ try {
+ Q3DStudio::CString theText = "";
+ int theNumEntry = m_ListCtrl.GetItemCount();
+ for (int theIndex = 0; theIndex < theNumEntry; ++theIndex) {
+ theText += m_ListCtrl.GetItemText(theIndex, 0);
+ theText += "\r\n";
+ }
+ CStudioClipboard::CopyTextToClipboard(theText);
+ } catch (...) {
+ }
+}
+
+//=============================================================================
+/**
+ * Handler for OK Button
+ */
+//=============================================================================
+void CPreviewOutputDlg::OnOK()
+{
+ TerminateThread(m_ReadThreadHandle, 0);
+ CDialog::OnOK();
+ m_ReadThreadHandle = nullptr;
+}
+
+BEGIN_MESSAGE_MAP(CPreviewOutputDlg, CDialog)
+ON_BN_CLICKED(IDC_BUTTON_COPY_TEXT, OnCopyText)
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * Spawn the process to execute the preview operation and redirect it's output
+ * to the list control
+ */
+//==============================================================================
+BOOL CPreviewOutputDlg::ExecutePreview()
+{
+ HANDLE theOutputReadHandleTmp;
+ HANDLE theOutputReadHandle;
+ HANDLE theOutputWriteHandle;
+
+ // Set up the security attributes struct.
+ SECURITY_ATTRIBUTES theSecurityAttr;
+ theSecurityAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ theSecurityAttr.lpSecurityDescriptor = nullptr;
+ theSecurityAttr.bInheritHandle = TRUE;
+
+ // Create the child output pipe.
+ if (!CreatePipe(&theOutputReadHandleTmp, &theOutputWriteHandle, &theSecurityAttr, 0))
+ return FALSE;
+
+ // Create new output read handle. Set the Properties to FALSE.
+ // Otherwise, the child inherits the properties and, as a result,
+ // non-closeable handles to the pipes are created.
+ if (!DuplicateHandle(GetCurrentProcess(), theOutputReadHandleTmp, GetCurrentProcess(),
+ &theOutputReadHandle, // Address of new handle.
+ 0, FALSE, // Make it uninheritable.
+ DUPLICATE_SAME_ACCESS))
+ return FALSE;
+
+ // Close inheritable copies of the handles you do not want to be inherited.
+ CloseHandle(theOutputReadHandleTmp);
+
+ LaunchPreviewProcess(theOutputWriteHandle, m_Cmd);
+
+ // Close pipe handles (do not continue to modify the parent).
+ // You need to make sure that no handles to the write end of the
+ // output pipe are maintained in this process or else the pipe will
+ // not close when the child process exits and the ReadFile will hang.
+ CloseHandle(theOutputWriteHandle);
+
+ // Spawn a thread to busy read the preivew process output.
+ DWORD theThreadId;
+ m_OutputReadThreadParam.m_Context = this;
+ m_OutputReadThreadParam.m_ReadHandle = theOutputReadHandle;
+ m_ReadThreadHandle = CreateThread(nullptr, 0, CPreviewOutputDlg::ReadOutputThread,
+ (LPVOID)&m_OutputReadThreadParam, 0, &theThreadId);
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * Callback when the preview operation has completed.
+ */
+//==============================================================================
+void CPreviewOutputDlg::PreviewProcessTerminated()
+{
+ DWORD theExitCode;
+ GetExitCodeProcess(m_PreviewProcessHandle, &theExitCode);
+ if (theExitCode == 0)
+ PostMessage(WM_CLOSE); // close the window
+ else {
+ Q3DStudio::CString theOperation;
+ switch (m_ViewMode) {
+ case CPreviewHelper::EXECMODE_PREVIEW:
+ theOperation = ::LoadResourceString(IDS_CONFIG_EXPORT_PREVIEW);
+ break;
+
+ case CPreviewHelper::EXECMODE_DEPLOY:
+ theOperation = ::LoadResourceString(IDS_CONFIG_EXPORT_DEPLOY);
+ break;
+ }
+ Q3DStudio::CString theMessage;
+ theMessage.Format(::LoadResourceString(IDS_CONFIG_PREVIEW_ERROR_TEXT),
+ static_cast<const wchar_t *>(theOperation));
+ DisplayOutput(theMessage);
+ }
+}
+
+//==============================================================================
+/**
+ * Write the string into the list control.
+ * @param inString the string to display
+ */
+//==============================================================================
+void CPreviewOutputDlg::DisplayOutput(CString inStr)
+{
+ const wchar_t *DELIMITERS = L"\r\t\n";
+
+ wchar_t theBuffer[512] = { 0 };
+ ::wcscpy(theBuffer, inStr);
+
+ wchar_t *theToken = ::wcstok(theBuffer, DELIMITERS);
+ while (theToken) {
+ m_ListCtrl.InsertItem(m_ListCtrl.GetItemCount(), theToken);
+ theToken = ::wcstok(nullptr, DELIMITERS);
+ }
+
+ m_ListCtrl.EnsureVisible(m_ListCtrl.GetItemCount() - 1, FALSE);
+}
+
+//==============================================================================
+/**
+ * Spawn the process to execute the preview operation
+ * @param inStdOutput the handle to write the output to
+ * @param inCmd the command to execute
+ * @return true is spawned successfully, else false
+ */
+//==============================================================================
+BOOL CPreviewOutputDlg::LaunchPreviewProcess(HANDLE inStdOutput, Q3DStudio::CString &inCmd)
+{
+ // Further usability hax, you love them.
+ DisplayOutput(L"Checking for .NET Framework install:");
+ HKEY theHKey;
+ if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP", 0,
+ KEY_QUERY_VALUE, &theHKey)) {
+ DisplayOutput(L" - OK: .NET Framework present.");
+ } else {
+ DisplayOutput(L" - WARNING: .NET Framework Missing?");
+ }
+
+ // Launch the process with the working directory set to the parent directory of current document
+ CUICFile theDocument = g_StudioApp.GetCore()->GetDoc()->GetDocumentPath();
+
+ Q3DStudio::CString theWorkingPath;
+ if (theDocument.Exists())
+ theWorkingPath = theDocument.GetAbsolutePath();
+ else
+ theWorkingPath = m_ExportedFile;
+
+ CUICFile thePreviewerFile(CUICFile::GetApplicationDirectory(), theWorkingPath);
+ Q3DStudio::CFilePath theWorkingDirectory(thePreviewerFile.GetAbsolutePosixPath());
+
+ PROCESS_INFORMATION theProcessInfo;
+ STARTUPINFO theStartupInfo;
+
+ // Set up the start up info struct.
+ ZeroMemory(&theStartupInfo, sizeof(STARTUPINFO));
+ theStartupInfo.cb = sizeof(STARTUPINFO);
+ theStartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ theStartupInfo.hStdOutput = inStdOutput;
+ theStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE) /*hChildStdIn*/;
+ theStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ theStartupInfo.wShowWindow = SW_HIDE;
+
+ if (!CreateProcess(nullptr, const_cast<wchar_t *>(inCmd.c_str()), nullptr, nullptr, TRUE, 0, nullptr,
+ theWorkingDirectory.GetDirectory(), &theStartupInfo, &theProcessInfo))
+ return FALSE;
+
+ // Close any unnecessary handles.
+ CloseHandle(theProcessInfo.hThread);
+ m_PreviewProcessHandle = theProcessInfo.hProcess;
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * Spawn the thread to busy read the output and write to the list control
+ * @param inThreadParam the parameter used by this thread
+ * @return 1
+ */
+//==============================================================================
+DWORD WINAPI CPreviewOutputDlg::ReadOutputThread(LPVOID inThreadParam)
+{
+ char theBuffer[512];
+ DWORD theBytesRead;
+ SThreadParam *theParam = (SThreadParam *)inThreadParam;
+ HANDLE thePipeRead = theParam->m_ReadHandle;
+ CPreviewOutputDlg *theDlg = (CPreviewOutputDlg *)(theParam->m_Context);
+
+ while (TRUE) {
+ if (!ReadFile(thePipeRead, theBuffer, sizeof(theBuffer) - sizeof(char), &theBytesRead, nullptr)
+ || !theBytesRead) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ theDlg->PreviewProcessTerminated();
+ CloseHandle(thePipeRead);
+ theParam->m_ReadHandle = nullptr;
+ break; // pipe done - normal exit path.
+ } else
+ theDlg->DisplayOutput(::LoadResourceString(
+ IDS_CONFIG_PREVIEW_REDIRECT_ERROR)); // Something bad happened.
+ } else {
+ if (theBytesRead) {
+ theBuffer[theBytesRead] = '\0'; // Follow input with a nullptr.
+ theDlg->DisplayOutput(CString(theBuffer));
+ }
+ }
+ }
+ return 1;
+}
diff --git a/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.h b/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.h
new file mode 100644
index 00000000..19361d3d
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/PreviewOutputDlg.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_PREVIEW_OUTPUT_DIALOG_H
+#define INCLUDED_PREVIEW_OUTPUT_DIALOG_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "resource.h"
+#include "PreviewHelper.h"
+
+//==============================================================================
+// Forwards
+//==============================================================================
+namespace Q3DStudio {
+class CFilePath;
+}
+
+class CPreviewOutputDlg : public CDialog
+{
+ DECLARE_DYNAMIC(CPreviewOutputDlg)
+
+protected:
+ struct SThreadParam
+ {
+ void *m_Context;
+ HANDLE m_ReadHandle;
+ };
+
+public:
+ CPreviewOutputDlg(const Q3DStudio::CString &inCmd, const Q3DStudio::CString &inExportedFile,
+ CPreviewHelper::EExecMode inViewMode);
+ virtual ~CPreviewOutputDlg();
+
+ // Dialog Data
+ enum { IDD = IDD_DIALOG_PREVIEW_STATUS };
+ CListCtrl m_ListCtrl;
+
+protected:
+ virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
+ virtual BOOL OnInitDialog();
+ virtual void OnCopyText();
+ virtual void OnOK();
+ DECLARE_MESSAGE_MAP()
+
+protected:
+ BOOL ExecutePreview();
+ BOOL LaunchPreviewProcess(HANDLE inStdOutput, Q3DStudio::CString &inCmd);
+ static DWORD WINAPI ReadOutputThread(LPVOID lpvThreadParam);
+ void DisplayOutput(CString inStr);
+ void PreviewProcessTerminated();
+
+protected:
+ Q3DStudio::CString m_Cmd;
+ Q3DStudio::CString m_ExportedFile;
+ SThreadParam m_OutputReadThreadParam;
+ HANDLE m_PreviewProcessHandle;
+ HANDLE m_ReadThreadHandle;
+ CPreviewHelper::EExecMode m_ViewMode;
+};
+
+#endif // INCLUDED_PREVIEW_OUTPUT_DIALOG_H
diff --git a/src/Authoring/Studio/_Win/UI/RecentItems.cpp b/src/Authoring/Studio/_Win/UI/RecentItems.cpp
new file mode 100644
index 00000000..b229ff29
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/RecentItems.cpp
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "stdafx.h"
+
+#include "RecentItems.h"
+#include "Preferences.h"
+
+#include <QMenu>
+
+// using namespace Q3DStudio; <-- Do not do this here because it will conflict with CList and make
+// the template generator go blah
+
+const Q3DStudio::CString CRecentItems::RECENTITEM_KEY = "RecentItem";
+const Q3DStudio::CString CRecentItems::RECENTIMPORT_KEY = "RecentImport";
+const Q3DStudio::CString CRecentItems::RECENTITEM_VALID = "RecentValid";
+
+CRecentItems::CRecentItems(QMenu *inMenuID, long inCommandID, Q3DStudio::CString inPreferenceKey)
+{
+ Q_UNUSED(inCommandID)
+
+ m_Menu = inMenuID;
+ m_ValidItems = 10;
+ m_PreferenceKey = inPreferenceKey;
+
+ ReconstructList();
+}
+
+CRecentItems::~CRecentItems()
+{
+}
+
+void CRecentItems::AddRecentItem(const CUICFile &inItem)
+{
+ RemoveRecentItem(inItem);
+
+ m_RecentItems.insert(m_RecentItems.begin(), inItem);
+
+ while (m_RecentItems.size() > 10)
+ m_RecentItems.pop_back();
+
+ RebuildList();
+}
+
+void CRecentItems::RemoveRecentItem(const CUICFile &inItem)
+{
+ TFileList::iterator thePos = m_RecentItems.begin();
+ for (; thePos != m_RecentItems.end(); ++thePos) {
+ if ((*thePos) == inItem) {
+ m_RecentItems.erase(thePos);
+ break;
+ }
+ }
+
+ RebuildList();
+}
+
+void CRecentItems::ReconstructList()
+{
+ ClearMenu();
+ m_RecentItems.clear();
+
+ CPreferences thePrefs = CPreferences::GetUserPreferences();
+
+ m_ValidItems = thePrefs.GetLongValue(RECENTITEM_VALID, m_ValidItems);
+
+ for (long theIndex = 0; theIndex < (m_ValidItems > 10 ? 10 : m_ValidItems); ++theIndex) {
+ Q3DStudio::CString theKey;
+ theKey.Format(_UIC("%ls%d"), static_cast<const wchar_t *>(m_PreferenceKey), theIndex);
+
+ Q3DStudio::CString theFilename = thePrefs.GetStringValue(theKey, "");
+ if (theFilename != "") {
+ CUICFile theFile(theFilename);
+
+ QAction *act = m_Menu->addAction(theFile.GetName().toQString(),
+ this, &CRecentItems::onTriggerRecent);
+ act->setData(static_cast<int>(m_RecentItems.size()));
+ m_RecentItems.push_back(theFile);
+ }
+ }
+}
+
+void CRecentItems::RebuildList()
+{
+ ClearMenu();
+
+ CPreferences thePrefs = CPreferences::GetUserPreferences();
+ thePrefs.SetLongValue(RECENTITEM_VALID, GetItemCount());
+ TFileList::iterator thePos = m_RecentItems.begin();
+ for (long theIndex = 0; thePos != m_RecentItems.end(); ++thePos, ++theIndex) {
+ Q3DStudio::CString theFilename = (*thePos).GetName();
+
+ QAction *act = m_Menu->addAction(theFilename.toQString(),
+ this, &CRecentItems::onTriggerRecent);
+ act->setData(static_cast<int>(theIndex));
+
+ Q3DStudio::CString theKey;
+ theKey.Format(_UIC("%ls%d"), static_cast<const wchar_t *>(m_PreferenceKey), theIndex);
+
+ thePrefs.SetStringValue(theKey, (*thePos).GetAbsolutePath());
+ }
+}
+
+void CRecentItems::ClearMenu()
+{
+ m_Menu->clear();
+}
+
+CUICFile CRecentItems::GetItem(long inIndex)
+{
+ return m_RecentItems.at(inIndex);
+}
+
+void CRecentItems::onTriggerRecent()
+{
+ const int index = qobject_cast<QAction *>(sender())->data().toInt();
+ Q_EMIT openRecent(index);
+}
+
diff --git a/src/Authoring/Studio/_Win/UI/RecentItems.h b/src/Authoring/Studio/_Win/UI/RecentItems.h
new file mode 100644
index 00000000..bc295079
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/RecentItems.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_RECENT_ITEMS_H
+#define INCLUDED_RECENT_ITEMS_H 1
+
+#pragma once
+
+#include <QObject>
+
+#include "UICString.h"
+#include "UICFile.h"
+
+#include <vector>
+
+class CUICFile;
+
+QT_BEGIN_NAMESPACE
+class QMenu;
+QT_END_NAMESPACE
+
+class CRecentItems : public QObject
+{
+ Q_OBJECT
+
+ typedef std::vector<CUICFile> TFileList;
+
+public:
+ static const Q3DStudio::CString RECENTITEM_KEY;
+ static const Q3DStudio::CString RECENTIMPORT_KEY;
+ static const Q3DStudio::CString RECENTITEM_VALID;
+
+Q_SIGNALS:
+ void openRecent(int index);
+public:
+ CRecentItems(QMenu *inMenu, long inCommandID,
+ Q3DStudio::CString inPreferenceKey = RECENTITEM_KEY);
+ virtual ~CRecentItems();
+
+ void AddRecentItem(const CUICFile &inItem);
+ void RemoveRecentItem(const CUICFile &inItem);
+
+ CUICFile GetItem(long inIndex);
+ long GetItemCount() const { return (long)m_RecentItems.size(); }
+
+protected:
+ void ClearMenu();
+ void ReconstructList();
+ void RebuildList();
+ void SaveRecentList();
+
+ TFileList m_RecentItems;
+
+ long m_CommandID;
+ long m_ValidItems;
+ QMenu *m_Menu;
+ Q3DStudio::CString m_PreferenceKey;
+
+private Q_SLOTS:
+ void onTriggerRecent();
+};
+#endif // INCLUDED_RECENT_ITEMS_H
diff --git a/src/Authoring/Studio/_Win/UI/ReportDlg.cpp b/src/Authoring/Studio/_Win/UI/ReportDlg.cpp
new file mode 100644
index 00000000..2563a4c1
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ReportDlg.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// ReportDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "Strings.h"
+
+#include "ReportDlg.h"
+#include "CrashInfo.h"
+
+#include "CompConfig.h"
+
+#include "StudioInstance.h"
+#include <strstream>
+
+extern char g_DumpPath[MAX_PATH];
+/////////////////////////////////////////////////////////////////////////////
+// CReportDlg dialog
+
+CReportDlg::CReportDlg(CWnd *pParent /*=nullptr*/)
+ : CDialog(CReportDlg::IDD, pParent)
+ , m_SysInfoText(_T(""))
+{
+ //{{AFX_DATA_INIT(CReportDlg)
+ m_Info = _T("");
+ m_DescHeader = _T("");
+ m_EmailAddress = _T("");
+ m_EmailHeader = _T("");
+ //}}AFX_DATA_INIT
+
+ m_MediumFont2.CreatePointFont(88, CString(CStudioPreferences::GetFontFaceName()));
+
+ m_Color_Background = CStudioPreferences::GetDarkBaseColor();
+ m_Color_Text = CStudioPreferences::GetMasterColor();
+ m_Color_Gray = CStudioPreferences::GetNormalColor();
+ m_Color_Dark = CStudioPreferences::GetInactiveColor();
+}
+
+void CReportDlg::DoDataExchange(CDataExchange *pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CReportDlg)
+ DDX_Control(pDX, IDC_REPORTDLG_SYSINFO, m_SendSysInfo);
+ DDX_Control(pDX, IDC_REPORTDLG_DESC, m_Description);
+ DDX_Text(pDX, IDC_REPORTDLG_INFO, m_Info);
+ DDX_Text(pDX, IDC_REPORTDLG_DESCHEADER, m_DescHeader);
+ DDX_Text(pDX, IDC_REPORTDLG_EMAILADDR, m_EmailAddress);
+ DDX_Text(pDX, IDC_REPORTDLG_EMAILHEADER, m_EmailHeader);
+ //}}AFX_DATA_MAP
+ DDX_Text(pDX, IDC_CRASHDLG_SYSINFO_TEXT, m_SysInfoText);
+}
+
+BEGIN_MESSAGE_MAP(CReportDlg, CDialog)
+//{{AFX_MSG_MAP(CReportDlg)
+ON_BN_CLICKED(IDSUBMIT, OnSubmit)
+//}}AFX_MSG_MAP
+ON_WM_CTLCOLOR()
+ON_WM_ERASEBKGND()
+// ON_STN_CLICKED(IDC_REPORTDLG__SYSINFO_TEXT, &CReportDlg::OnStnClickedReportdlg)
+ON_STN_CLICKED(IDC_REPORTDLG__SYSINFO_TEXT, &CReportDlg::OnStnClickedReportdlg)
+END_MESSAGE_MAP()
+
+//==============================================================================
+/**
+ * Set the description of the crash.
+ * @param inErrorMessage the error message.
+ */
+//==============================================================================
+void CReportDlg::SetErrorMessage(CString inErrorMessage)
+{
+ m_ErrorMessage = inErrorMessage;
+}
+
+//==============================================================================
+/**
+ * Set the stack trace for the crash.
+ * @param inStackTrace the stack trace.
+ */
+//==============================================================================
+void CReportDlg::SetStackTrace(CString inStackTrace)
+{
+ m_StackTrace = inStackTrace;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CReportDlg message handlers
+
+void CReportDlg::OnSubmit()
+{
+ CWaitCursor theWaitCursor;
+
+ this->UpdateData(TRUE);
+
+ // Actual reporting functionality missing
+
+ this->EndDialog(TRUE);
+ exit(1);
+}
+
+void CReportDlg::OnCancel()
+{
+ this->EndDialog(FALSE);
+}
+
+BOOL CReportDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ m_Info = ::LoadResourceString(IDS_REPORTDLG_INFO).GetMulti();
+ m_DescHeader = ::LoadResourceString(IDS_REPORTDLG_DESCHEADER).GetMulti();
+ m_EmailHeader = ::LoadResourceString(IDS_REPORTDLG_EMAILHEADER).GetMulti();
+
+ CString theSysInfoText;
+ theSysInfoText = ""; //::LoadResourceString( IDS_REPORTDLG_SYSINFOHEADER ).GetMulti( );
+ // this is done this way, as i had problem changing the color of the text of the checkbox.
+ // it is now a seperate checkbox and a static text
+ // Made a workaround where the static text has notify flag on and would toggle the checkbox
+ // control on and off
+ // when clicked
+ m_SysInfoText = ::LoadResourceString(IDS_REPORTDLG_SYSINFOHEADER).GetMulti();
+
+ m_SendSysInfo.SetWindowText(theSysInfoText);
+ m_SendSysInfo.SetFont(&m_MediumFont2);
+ m_SendSysInfo.SetCheck(BST_CHECKED);
+
+ m_Brush.CreateSolidBrush(m_Color_Background);
+ GetDlgItem(IDC_REPORTDLG_INFO)->SetFont(&m_MediumFont2);
+ GetDlgItem(IDC_REPORTDLG_DESCHEADER)->SetFont(&m_MediumFont2);
+ GetDlgItem(IDC_REPORTDLG_EMAILADDR)->SetFont(&m_MediumFont2);
+ GetDlgItem(IDC_REPORTDLG_EMAILHEADER)->SetFont(&m_MediumFont2);
+ GetDlgItem(IDC_REPORTDLG__SYSINFO_TEXT)->SetFont(&m_MediumFont2);
+
+ this->UpdateData(FALSE);
+
+ return TRUE;
+}
+
+HBRUSH CReportDlg::OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor)
+{
+ Q_UNUSED(nCtlColor);
+
+ int theCtrlID = pWnd->GetDlgCtrlID();
+ if (theCtrlID == IDSUBMIT || theCtrlID == IDCANCEL || theCtrlID == IDC_REPORTDLG_SEPERATOR
+ || theCtrlID == IDC_REPORTDLG_SYSINFO || theCtrlID == IDC_REPORTDLG__SYSINFO_TEXT
+ || theCtrlID == IDC_REPORTDLG_INFO || theCtrlID == IDC_REPORTDLG_DESCHEADER
+ || theCtrlID == IDC_REPORTDLG_EMAILHEADER) {
+ pDC->SetBkMode(TRANSPARENT); // for area just behind the text
+ pDC->SetTextColor(m_Color_Gray);
+ } else if (theCtrlID == IDC_REPORTDLG_DESC || theCtrlID == IDC_REPORTDLG_EMAILADDR) {
+ pDC->SetBkColor(m_Color_Background);
+ pDC->SetBkMode(OPAQUE); // for area just behind the text
+ pDC->SetTextColor(m_Color_Text);
+ }
+
+ return m_Brush;
+}
+
+BOOL CReportDlg::OnEraseBkgnd(CDC *pDC)
+{
+ CRect theClientRect;
+ GetClientRect(&theClientRect);
+ pDC->FillSolidRect(theClientRect, m_Color_Background);
+
+ return TRUE;
+}
+
+void CReportDlg::OnStnClickedReportdlg()
+{
+ m_SendSysInfo.SetCheck(!m_SendSysInfo.GetCheck());
+}
diff --git a/src/Authoring/Studio/_Win/UI/ReportDlg.h b/src/Authoring/Studio/_Win/UI/ReportDlg.h
new file mode 100644
index 00000000..c54743da
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ReportDlg.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_REPORTDLG_H__B8564DEF_2599_4419_9E15_4AF38BE7066C__INCLUDED_)
+#define AFX_REPORTDLG_H__B8564DEF_2599_4419_9E15_4AF38BE7066C__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// ReportDlg.h : header file
+//
+
+/////////////////////////////////////////////////////////////////////////////
+// CReportDlg dialog
+
+class CReportDlg : public CDialog
+{
+ // Construction
+public:
+ CReportDlg(CWnd *pParent = nullptr); // standard constructor
+
+ COLORREF m_Color_Background;
+ COLORREF m_Color_Text;
+ COLORREF m_Color_Gray;
+ COLORREF m_Color_Dark;
+ CBrush m_Brush;
+ CFont m_MediumFont2;
+
+ // Dialog Data
+ //{{AFX_DATA(CReportDlg)
+ enum { IDD = IDD_REPORTDLG };
+ CButton m_SendSysInfo;
+ CEdit m_Description;
+ CString m_Info;
+ CString m_DescHeader;
+ CString m_EmailAddress;
+ CString m_EmailHeader;
+ //}}AFX_DATA
+
+ void SetErrorMessage(CString inErrorMessage);
+ void SetStackTrace(CString inStackTrace);
+
+ // Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CReportDlg)
+protected:
+ virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+ // Implementation
+protected:
+ // Generated message map functions
+ //{{AFX_MSG(CReportDlg)
+ afx_msg void OnSubmit();
+ virtual void OnCancel();
+ virtual BOOL OnInitDialog();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+ CString m_ErrorMessage;
+ CString m_StackTrace;
+
+public:
+ afx_msg HBRUSH OnCtlColor(CDC *pDC, CWnd *pWnd, UINT nCtlColor);
+ afx_msg BOOL OnEraseBkgnd(CDC *pDC);
+ CString m_SysInfoText;
+ // afx_msg void OnStnClickedReportdlg();
+ afx_msg void OnStnClickedReportdlg();
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_REPORTDLG_H__B8564DEF_2599_4419_9E15_4AF38BE7066C__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.cpp b/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.cpp
new file mode 100644
index 00000000..2eaa293a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "stdafx.h"
+#include "ResetKeyframeValuesDlg.h"
+#include ".\resetkeyframevaluesdlg.h"
+
+IMPLEMENT_DYNAMIC(CResetKeyframeValuesDlg, CDialog)
+CResetKeyframeValuesDlg::CResetKeyframeValuesDlg(CWnd *pParent /*=nullptr*/)
+ : CDialog(CResetKeyframeValuesDlg::IDD, pParent)
+{
+}
+
+CResetKeyframeValuesDlg::~CResetKeyframeValuesDlg()
+{
+}
+
+void CResetKeyframeValuesDlg::DoDataExchange(CDataExchange *pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Control(pDX, IDC_RESETKEYFRAMEVALUES_DLG_ICON, m_WarningIcon);
+ DDX_Control(pDX, IDCANCEL, m_CancelButton);
+}
+
+BEGIN_MESSAGE_MAP(CResetKeyframeValuesDlg, CDialog)
+END_MESSAGE_MAP()
+
+BOOL CResetKeyframeValuesDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ m_WarningIcon.SetIcon(::AfxGetApp()->LoadStandardIcon(IDI_WARNING));
+ m_CancelButton.SetButtonStyle(BS_DEFPUSHBUTTON);
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
diff --git a/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.h b/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.h
new file mode 100644
index 00000000..d0a883d1
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/ResetKeyframeValuesDlg.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#pragma once
+#include "Resource.h"
+#include "afxwin.h"
+
+class CResetKeyframeValuesDlg : public CDialog
+{
+ DECLARE_DYNAMIC(CResetKeyframeValuesDlg)
+
+public:
+ CResetKeyframeValuesDlg(CWnd *pParent = nullptr); // standard constructor
+ virtual ~CResetKeyframeValuesDlg();
+
+ // Dialog Data
+ enum { IDD = IDD_RESETKEYFRAMEVALUES_DLG };
+
+protected:
+ virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
+
+ DECLARE_MESSAGE_MAP()
+ CStatic m_WarningIcon;
+
+public:
+ virtual BOOL OnInitDialog();
+ CButton m_CancelButton;
+};
diff --git a/src/Authoring/Studio/_Win/UI/SceneView.cpp b/src/Authoring/Studio/_Win/UI/SceneView.cpp
new file mode 100644
index 00000000..6fcbc6d0
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/SceneView.cpp
@@ -0,0 +1,861 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#ifdef _WIN32
+#pragma warning(disable : 4100) // unreferenced formal parameter
+#endif
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICOptions.h"
+#include "SceneView.h"
+//#include "InterpolationDlg.h"
+#include "MainFrm.h"
+#include "StudioProjectSettings.h"
+#include "StudioInstance.h"
+#include "StudioPreferences.h"
+#include "HotKeys.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "Dispatch.h"
+#include "MouseCursor.h"
+#include "ResourceCache.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "WndControl.h"
+#endif
+#include "Core.h"
+#include "UICDMStudioSystem.h"
+#include "IStudioRenderer.h"
+#include "ClientDataModelBridge.h"
+
+#include <QKeyEvent>
+#include <QScrollBar>
+#include <QSettings>
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+
+CSceneView::CSceneView(CStudioApp *inStudioApp, QWidget *parent)
+ : QWidget(parent)
+{
+ Q_UNUSED(inStudioApp)
+ m_PreviousToolMode = g_StudioApp.GetToolMode();
+ m_PreviousSelectMode = g_StudioApp.GetSelectMode();
+
+ m_PlayerContainerWnd = new CPlayerContainerWnd(this);
+ connect(m_PlayerContainerWnd, &CPlayerContainerWnd::toolChanged, this, &CSceneView::toolChanged);
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Create the player container window
+ CPt theSize = CStudioPreferences::GetDefaultClientSize();
+
+ // Note that creating with WS_CLIPCHILDREN prevents flickering when SceneView is being
+ // resized.
+ m_PlayerContainerWnd->CreateEx(0, AfxRegisterWndClass(0, LoadCursor(NULL, IDC_ARROW),
+ (HBRUSH)GetStockObject(BLACK_BRUSH)),
+ L"player_container_wnd",
+ WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ CRect(0, 0, theSize.x, theSize.y), this, 1000);
+#endif
+
+ m_PlayerWnd = new CPlayerWnd(m_PlayerContainerWnd);
+ connect(m_PlayerWnd, &CPlayerWnd::dropReceived, this, &CSceneView::onDropReceived);
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+ // Create the player frame child window
+ m_PlayerWnd.Create(AfxRegisterWndClass(0, LoadCursor(NULL, IDC_ARROW),
+ (HBRUSH)GetStockObject(WHITE_BRUSH)),
+ L"player_wnd", WS_CHILD | WS_VISIBLE, CRect(0, 0, theSize.x, theSize.y),
+ m_PlayerContainerWnd, 1001);
+#endif
+ m_PlayerContainerWnd->SetPlayerWnd(m_PlayerWnd);
+
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+CSceneView::CSceneView()
+{
+ m_PreviousToolMode = g_StudioApp.GetToolMode();
+ m_PreviousSelectMode = g_StudioApp.GetSelectMode();
+}
+
+void CSceneView::onDropReceived()
+{
+ setFocus();
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+CSceneView::~CSceneView()
+{
+ CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch();
+ // Stop listening for selection change events
+ theDispatch->RemoveSelectedNodePropChangeListener(this);
+ theDispatch->RemoveClientPlayChangeListener(this);
+#ifdef INCLUDE_EDIT_CAMERA
+ theDispatch->RemoveEditCameraChangeListener(this);
+#endif
+}
+
+QSize CSceneView::sizeHint() const
+{
+ CPt theSize = CStudioPreferences::GetDefaultClientSize();
+ return QSize(theSize.x, theSize.y);
+}
+
+//====================================================m==========================
+/**
+ * Called by the framework after the view is first attached
+ * to the document, but before the view is initially displayed.
+ * Notifies the Main Frame that the palettes need to be shown, and destroys the
+ * splash screen.
+ */
+void CSceneView::showEvent(QShowEvent *event)
+{
+ QWidget::showEvent(event);
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+ // Modify the style for WS_CLIPCHILDREN
+ ModifyStyle(0, WS_CLIPCHILDREN);
+#endif
+
+ m_PlayerContainerWnd->RecenterClient();
+
+ // Set the scroll information.
+ m_PlayerContainerWnd->SetScrollRanges();
+
+ // Create the cursors
+ m_ArrowCursor = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_ARROW);
+ m_CursorGroupMove = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_GROUP_MOVE);
+ m_CursorGroupRotate =
+ CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_GROUP_ROTATE);
+ m_CursorGroupScale = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_GROUP_SCALE);
+ m_CursorItemMove = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_ITEM_MOVE);
+ m_CursorItemRotate = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_ITEM_ROTATE);
+ m_CursorItemScale = CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_ITEM_SCALE);
+ m_CursorEditCameraPan =
+ CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_EDIT_CAMERA_PAN);
+ m_CursorEditCameraRotate =
+ CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_EDIT_CAMERA_ROTATE);
+ m_CursorEditCameraZoom =
+ CResourceCache::GetInstance()->GetCursor(CMouseCursor::CURSOR_EDIT_CAMERA_ZOOM);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ g_StudioApp.GetCore()->GetDispatch()->AddEditCameraChangeListener(this);
+#endif
+
+ // Set the default cursor
+ OnSetCursor();
+}
+
+void CSceneView::keyPressEvent(QKeyEvent *event)
+{
+ /*switch (event->key())
+ {
+ case Qt::Key_Control:
+ case Qt::Key_Alt:
+ case Qt::Key_Shift:
+ case Qt::Key_Meta:
+ HandleModifierDown(event->key(), event->isAutoRepeat() ? 2 : 1, event->modifiers());
+ break;
+ default:
+ break;
+ }*/
+}
+
+//==============================================================================
+/**
+ * OnToolGroupSelection: Called when the Group Selection button is pressed.
+ * Sets the current tool mode and changes the cursor.
+ */
+void CSceneView::OnToolGroupSelection()
+{
+ m_PreviousSelectMode = g_StudioApp.GetSelectMode();
+ g_StudioApp.SetSelectMode(STUDIO_SELECTMODE_GROUP);
+ OnSetCursor();
+ Q_EMIT toolChanged();
+}
+
+//==============================================================================
+/**
+ * OnToolItemSelection: Called when the Item Selection button is pressed.
+ * Sets the current tool mode and changes the cursor.
+ */
+void CSceneView::OnToolItemSelection()
+{
+ m_PreviousSelectMode = g_StudioApp.GetSelectMode();
+ g_StudioApp.SetSelectMode(STUDIO_SELECTMODE_ENTITY);
+ OnSetCursor();
+ Q_EMIT toolChanged();
+}
+
+
+//==============================================================================
+/**
+ * SetViewCursor: Sets the cursor for the view according to the current Client Tool.
+ *
+ * Changes the cursor depending on the current tool mode. Each time the Tool mode
+ * changes, you should call this function. If you add extra tool modes, you
+ * will need to adjust this function.
+ */
+void CSceneView::SetViewCursor()
+{
+ long theCurrentToolSettings = g_StudioApp.GetToolMode();
+ long theCurrentSelectSettings = g_StudioApp.GetSelectMode();
+
+ // See what tool mode we are in
+ switch (theCurrentToolSettings) {
+ case STUDIO_TOOLMODE_MOVE:
+ switch (theCurrentSelectSettings) {
+ case STUDIO_SELECTMODE_ENTITY:
+ m_PlayerWnd->setCursor(m_CursorItemMove);
+ break;
+ case STUDIO_SELECTMODE_GROUP:
+ m_PlayerWnd->setCursor(m_CursorGroupMove);
+ break;
+ // Default - shouldn't happen
+ default:
+ m_PlayerWnd->setCursor(m_CursorItemMove);
+ break;
+ }
+ break;
+
+ case STUDIO_TOOLMODE_ROTATE:
+ switch (theCurrentSelectSettings) {
+ case STUDIO_SELECTMODE_ENTITY:
+ m_PlayerWnd->setCursor(m_CursorItemRotate);
+ break;
+ case STUDIO_SELECTMODE_GROUP:
+ m_PlayerWnd->setCursor(m_CursorGroupRotate);
+ break;
+ // Default - shouldn't happen
+ default:
+ m_PlayerWnd->setCursor(m_CursorItemRotate);
+ break;
+ }
+ break;
+
+ case STUDIO_TOOLMODE_SCALE:
+ switch (theCurrentSelectSettings) {
+ case STUDIO_SELECTMODE_ENTITY:
+ m_PlayerWnd->setCursor(m_CursorItemScale);
+ break;
+ case STUDIO_SELECTMODE_GROUP:
+ m_PlayerWnd->setCursor(m_CursorGroupScale);
+ break;
+ // Default - shouldn't happen
+ default:
+ m_PlayerWnd->setCursor(m_CursorItemScale);
+ break;
+ }
+ break;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ case STUDIO_TOOLMODE_CAMERA_PAN:
+ m_PlayerWnd->setCursor(m_CursorEditCameraPan);
+ break;
+
+ case STUDIO_TOOLMODE_CAMERA_ZOOM:
+ m_PlayerWnd->setCursor(m_CursorEditCameraZoom);
+ break;
+
+ case STUDIO_TOOLMODE_CAMERA_ROTATE:
+ m_PlayerWnd->setCursor(m_CursorEditCameraRotate);
+ break;
+#endif
+ // Default - shouldn't happen
+ default:
+ m_PlayerWnd->setCursor(m_CursorItemMove);
+ break;
+ }
+}
+
+//==============================================================================
+/**
+ * RecalcMatte: Recalculates the matte around the Client based on the settings.
+ *
+ * This will recalculate the matting around the Client based on the Client's
+ * current size. If the Client is a "Fit To Window" mode, then the matte region
+ * is cleared.
+ */
+//==============================================================================
+void CSceneView::RecalcMatte()
+{
+ long theXOffset = 0;
+ long theYOffset = 0;
+ QRect theClientRect = rect();
+
+ // Adjust the client area based if rulers are visible
+ if (m_PlayerContainerWnd) {
+ m_PlayerContainerWnd->setGeometry(theXOffset, theYOffset,
+ theClientRect.width() - theXOffset,
+ theClientRect.height() - theYOffset);
+
+ // Recenter the Client rect
+ m_PlayerContainerWnd->RecenterClient();
+ }
+}
+
+//==============================================================================
+/**
+ * PtInClientRect: Returns true if the point lies in the client rect, false otherwise.
+ *
+ * @param inPoint The point to check.
+ */
+//==============================================================================
+bool CSceneView::PtInClientRect(const QPoint& inPoint)
+{
+ return m_PlayerContainerWnd && m_PlayerContainerWnd->geometry().contains(inPoint);
+}
+
+//==============================================================================
+/**
+ * HandleModifierDown: Called when a modifier key (ctrl or alt) is pressed and held.
+ *
+ * Changes tool modes and saves the previous mode.
+ *
+ * @param inChar Contains the character code value of the key.
+ * @param inRepCnt Contains the repeat count, the number of times the keystroke
+ *is repeated when user holds down the key.
+ * @param inFlags Contains the scan code, key-transition code, previous
+ *key state, and context code.
+ */
+//==============================================================================
+bool CSceneView::HandleModifierDown(int inChar, int inRepCnt, Qt::KeyboardModifiers modifiers)
+{
+ bool theHandledFlag = false;
+
+ UNREFERENCED_PARAMETER(inRepCnt);
+
+ // Get the position of the mouse and the rectangle containing the scene view
+ QPoint theCursorPosition = mapFromGlobal(QCursor::pos());
+ QRect theWindowRect = rect();
+
+ // If we are currently dragging an object or the cursor is over the scene
+ if (((theWindowRect.contains(theCursorPosition))
+ || (m_PlayerContainerWnd && m_PlayerContainerWnd->IsMouseDown()))) {
+ bool theCtrlKeyIsDown = modifiers & Qt::ControlModifier;
+ bool theAltKeyIsDown = modifiers & Qt::AltModifier;
+
+ // If the control key is being pressed and the Alt key is not down
+ if ((inChar == Qt::Key_Control) && (!theAltKeyIsDown)) {
+ // If this is the first press, toggle tool modes
+ m_RegisteredKeyDown = true;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ if (m_PlayerContainerWnd->IsMiddleMouseDown() && !IsDeploymentView()) {
+ // Do nothing, do not let it switch to other tool when middle mouse is down
+ } else
+#endif
+ {
+ // See what tool mode we are in and change modes accordingly
+ SetToolOnCtrl();
+ theHandledFlag = true;
+ }
+ }
+ // If the alt key is being pressed and the control key is not down
+ else if ((inChar == Qt::Key_Alt) && (!theCtrlKeyIsDown)) {
+ m_RegisteredKeyDown = true;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ if (m_PlayerContainerWnd->IsMiddleMouseDown() && !IsDeploymentView()) {
+ // press Alt-Scroll Wheel Click
+ // Do Camera Rotate if we are in 3D Camera
+ if (g_StudioApp.GetRenderer().DoesEditCameraSupportRotation(
+ g_StudioApp.GetRenderer().GetEditCamera())) {
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ theHandledFlag = true;
+ }
+ } else
+#endif
+ {
+ // See what tool mode we are in and change modes accordingly
+ SetToolOnAlt();
+ theHandledFlag = true;
+ }
+ }
+ OnSetCursor();
+ Q_EMIT toolChanged();
+ }
+
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * HandleModifierUp: Handles the release of a modifier key (ctrl or alt).
+ *
+ * Changes tool modes back to the original tool mode.
+ *
+ * @param inChar Contains the character code value of the key.
+ * @param inRepCnt Contains the repeat count, the number of times the keystroke is
+ *repeated when user holds down the key.
+ * @param inFlags Contains the scan code, key-transition code, previous key
+ *state, and context code.
+ */
+//==============================================================================
+bool CSceneView::HandleModifierUp(int inChar, int inRepCnt, Qt::KeyboardModifiers modifiers)
+{
+ UNREFERENCED_PARAMETER(inRepCnt);
+
+ bool theHandledFlag = false;
+
+ // Get the position of the mouse and the rectangle containing the scene view
+ QPoint theCursorPosition = mapFromGlobal(QCursor::pos());
+ QRect theWindowRect = rect();
+
+ // If we are currently dragging an object or the cursor is over the scene
+ if (((theWindowRect.contains(theCursorPosition))
+ || (m_PlayerContainerWnd && m_PlayerContainerWnd->IsMouseDown()))) {
+ bool theCtrlKeyIsDown = modifiers & Qt::ControlModifier;
+ bool theAltKeyIsDown = modifiers & Qt::AltModifier;
+
+ // If the control key or alt key is released (and the opposite key is not down) revert back
+ // to the original tool mode
+ if (((inChar == Qt::Key_Control) && (!theAltKeyIsDown))
+ || ((inChar == Qt::Key_Alt) && (!theCtrlKeyIsDown))) {
+#ifdef INCLUDE_EDIT_CAMERA
+ if (m_PlayerContainerWnd->IsMiddleMouseDown())
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ else
+#endif
+ {
+ RestorePreviousTool();
+ }
+
+ OnSetCursor();
+ Q_EMIT toolChanged();
+
+ theHandledFlag = true;
+ }
+ // m_RegisteredKeyDown = false;
+ }
+
+ return theHandledFlag;
+}
+
+//==============================================================================
+/**
+ * resizeEvent: Handles the WM_SIZE message
+ *
+ * Recenters the Client and recaluclates the matte when a resize message is
+ * generated.
+ *
+ * @param nType Specifies the type of resizing requested.
+ * @param cx Specifies the new width of the client area.
+ * @param cy Specifies the new height of the client area.
+ */
+//==============================================================================
+void CSceneView::resizeEvent(QResizeEvent* event)
+{
+ QWidget::resizeEvent(event);
+ if (m_PlayerContainerWnd) {
+ RecalcMatte();
+ SetPlayerWndPosition();
+ g_StudioApp.GetCore()->GetDoc( )->GetSceneGraph()->RequestRender();
+ }
+}
+
+//==============================================================================
+/**
+ * SetPlayerWndPosition: Sets the position of the child player window
+ *
+ * Called when the view is scrolled to position the child player window
+ *
+ */
+//==============================================================================
+void CSceneView::SetPlayerWndPosition()
+{
+ // Move the child player window to coincide with the scrollbars
+ if (m_PlayerContainerWnd) {
+ long theLeft, theTop;
+ // Retrieve the left and top edge of the presentation currently in view
+ m_PlayerContainerWnd->SetPlayerWndPosition(theLeft, theTop);
+
+ m_PlayerContainerWnd->update();
+ }
+}
+
+//==============================================================================
+/**
+ * GetPlayerHwnd: Gets the window handle for the player object.
+ *
+ * @return <HWND> Window handle for the player window
+ */
+//==============================================================================
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+
+HWND CSceneView::GetPlayerHwnd()
+{
+ HWND thePlayerWnd;
+
+ thePlayerWnd = this->GetSafeHwnd();
+
+ if (IsWindow(m_PlayerWnd.GetSafeHwnd()))
+ thePlayerWnd = m_PlayerWnd.GetSafeHwnd();
+
+ return thePlayerWnd;
+}
+#endif
+//==============================================================================
+/**
+ * SetRegisteredKey: Sets the staus to true or false
+ *
+ * If we've already processesed an alt or tab, then set the flag
+ */
+//==============================================================================
+void CSceneView::SetRegisteredKey(bool inStatus)
+{
+ m_RegisteredKeyDown = inStatus;
+}
+
+//==============================================================================
+/**
+ * GetKeyStatus: get the staus
+ *
+ * check to see if the flag is true or false
+ */
+//==============================================================================
+bool CSceneView::GetKeyStatus()
+{
+ return m_RegisteredKeyDown;
+}
+
+//=============================================================================
+/**
+ * Register all the events for hotkeys that are active for the entire application.
+ * Hotkeys for the entire application are ones that are not view specific in
+ * scope.
+ *
+ * @param inShortcutHandler the global shortcut handler.
+ */
+//=============================================================================
+void CSceneView::RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler)
+{
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::OnToolGroupSelection), 0, Qt::Key_A);
+ inShortcutHandler->RegisterKeyEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::OnToolItemSelection), 0, Qt::Key_V);
+ inShortcutHandler->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::HandleModifierDown),
+ Qt::ControlModifier, Qt::Key_Control);
+ inShortcutHandler->RegisterKeyDownEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::HandleModifierDown),
+ Qt::AltModifier, Qt::Key_Alt);
+ inShortcutHandler->RegisterKeyUpEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::HandleModifierUp), 0,
+ Qt::Key_Control);
+ inShortcutHandler->RegisterKeyUpEvent(
+ new CDynHotKeyConsumer<CSceneView>(this, &CSceneView::HandleModifierUp), 0,
+ Qt::Key_Alt);
+}
+
+//==============================================================================
+/**
+ * OnSetCursor: Handles the WM_SETCURSOR Windows message.
+ *
+ * Changes the cursor depending on the current tool mode.
+ *
+ * @param inWnd Specifies a pointer to the window that contains the cursor.
+ * @param inHitTest Specifies the hit-test area code. The hit test determines the cursor�s
+ *location.
+ * @param inMessage Specifies the mouse message number.
+ *
+ * @return true if the cursor was set
+ */
+void CSceneView::OnSetCursor()
+{
+ SetViewCursor();
+}
+
+//==============================================================================
+/**
+ * Called when the tool mode changes, scene view maintains its own mode so it can
+ * return to that mode when modifiers are pressed
+ * @param inMode the mode to which to change
+ */
+void CSceneView::SetToolMode(long inMode)
+{
+ m_PreviousToolMode = inMode;
+ SetViewCursor();
+}
+
+//==============================================================================
+/**
+ * Resets its scroll ranges and recenters client in the window. This is called when
+ * an outside source needs to tell the scene view that size ranges have changed such
+ * as the preferences telling the sceneview that the size changed.
+ */
+void CSceneView::RecheckSizingMode()
+{
+ if (m_PlayerContainerWnd) {
+ m_PlayerContainerWnd->SetScrollRanges();
+ }
+}
+
+//=============================================================================
+/**
+ * Called a selected node property has changed.
+ * @param inPropertyName name of the property changed
+ */
+void CSceneView::OnPropValueChanged(const Q3DStudio::CString &inPropertyName)
+{
+}
+
+//=============================================================================
+/**
+ * Called a selected node property has its property changed via a mouse drag
+ * @param inConstrainXAxis true if dragging is constrained along the X-Axis
+ * @param inConstrainYAxis true if dragging is constrained along the Y-Axis
+ */
+void CSceneView::OnMouseDragged(bool inConstrainXAxis, bool inConstrainYAxis)
+{
+ // If not constraining to Y-Axis, snap horizontally
+ // If not constraining to X-Axis, snap vertically
+ // true to update tranform, as this is called from a mouse drag and the global transform may be
+ // outdated.
+ // false to not update the scene, since this is a result of a mouse drag which is already doing
+ // the updating
+}
+
+//==========================================================================
+/**
+ * Called when the presentation time changes.
+ * When animating an object, the markers must be updated with the selected object
+ */
+void CSceneView::OnTimeChanged(long inTime)
+{
+ UNREFERENCED_PARAMETER(inTime);
+}
+
+//==============================================================================
+/**
+ * Restore to the previous tool mode. This is called when the modifier is released
+ */
+//==============================================================================
+void CSceneView::RestorePreviousTool()
+{
+ // What was the original tool mode?
+ switch (m_PreviousToolMode) {
+ case STUDIO_TOOLMODE_MOVE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_MOVE);
+ break;
+
+ case STUDIO_TOOLMODE_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_ROTATE);
+ break;
+
+ case STUDIO_TOOLMODE_SCALE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_SCALE);
+ break;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ case STUDIO_TOOLMODE_CAMERA_PAN:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ break;
+
+ case STUDIO_TOOLMODE_CAMERA_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ break;
+
+ case STUDIO_TOOLMODE_CAMERA_ZOOM:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ZOOM);
+ break;
+#endif
+ }
+}
+
+//==============================================================================
+/**
+ * Change the tool when the Ctrl Key is pressed
+ */
+//==============================================================================
+void CSceneView::SetToolOnCtrl()
+{
+ switch (m_PreviousToolMode) {
+ // If we are in move mode, switch to rotate
+ case STUDIO_TOOLMODE_MOVE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_ROTATE);
+ break;
+
+ // If we are in rotate mode, switch to move
+ case STUDIO_TOOLMODE_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_MOVE);
+ break;
+
+ // If we are in scale mode, switch to move
+ case STUDIO_TOOLMODE_SCALE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_MOVE);
+ break;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ // If we are in camera pan mode, switch to camera orbit
+ case STUDIO_TOOLMODE_CAMERA_PAN:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ break;
+
+ // If we are in camera orbit mode, switch to camera pan
+ case STUDIO_TOOLMODE_CAMERA_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ break;
+
+ // If we are in camera zoom mode, switch to camera pan
+ case STUDIO_TOOLMODE_CAMERA_ZOOM:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_PAN);
+ break;
+#endif
+ }
+}
+
+//==============================================================================
+/**
+ * Change the tool when the Alt Key is pressed
+ */
+//==============================================================================
+void CSceneView::SetToolOnAlt()
+{
+ switch (m_PreviousToolMode) {
+ // If we are in move mode, switch to rotate
+ case STUDIO_TOOLMODE_MOVE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_SCALE);
+ break;
+
+ // If we are in rotate mode, switch to move
+ case STUDIO_TOOLMODE_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_SCALE);
+ break;
+
+ // If we are in scale mode, switch to move
+ case STUDIO_TOOLMODE_SCALE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_ROTATE);
+ break;
+
+#ifdef INCLUDE_EDIT_CAMERA
+ // If we are in camera pan mode, switch to camera zoom
+ case STUDIO_TOOLMODE_CAMERA_PAN:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ZOOM);
+ break;
+
+ // If we are in camera orbit mode, switch to camera zoom
+ case STUDIO_TOOLMODE_CAMERA_ROTATE:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ZOOM);
+ break;
+
+ // If we are in camera zoom mode, switch to camera orbit
+ case STUDIO_TOOLMODE_CAMERA_ZOOM:
+ g_StudioApp.SetToolMode(STUDIO_TOOLMODE_CAMERA_ROTATE);
+ break;
+#endif
+ }
+}
+
+//==============================================================================
+/**
+ * Redirect to PlayerContainerWnd to check whether we are in deployment view mode.
+ * @return true if is in deployment view mode, else false
+ */
+bool CSceneView::IsDeploymentView()
+{
+ // default mode is SCENE_VIEW so if playercontainerwnd does not exist ( should only happen on
+ // startup ),
+ // it is deployment view
+ bool theStatus = true;
+ if (m_PlayerContainerWnd)
+ theStatus = m_PlayerContainerWnd->IsDeploymentView();
+
+ return theStatus;
+}
+
+//==============================================================================
+/**
+ * Redirect to PlayerContainerWnd to set the view mode of the current scene view,
+ * whether we are in editing mode or deployment mode. For editing mode, we want to
+ * use the full scene area without any matte area.
+ * @param inViewMode the view mode of this scene
+ */
+void CSceneView::SetViewMode(CPlayerContainerWnd::EViewMode inViewMode)
+{
+ if (m_PlayerContainerWnd) {
+ m_PlayerContainerWnd->SetViewMode(inViewMode);
+ }
+}
+
+//==============================================================================
+/**
+ * When the active camera is changed, the display string needs to be changed. Hence
+ * find which entry is the one which is modified and update with the new string
+ * @param inCamera the camera that has been modified
+ */
+void CSceneView::OnEditCameraChanged()
+{
+ // reset any scrolling and recalculate the window position.
+ if (m_PlayerContainerWnd) {
+ m_PlayerContainerWnd->SetScrollRanges();
+ RecalcMatte();
+ SetPlayerWndPosition();
+ }
+
+ // update the view mode accordingly
+ SetViewMode(g_StudioApp.GetRenderer().GetEditCamera() >= 0 ? CPlayerContainerWnd::VIEW_EDIT
+ : CPlayerContainerWnd::VIEW_SCENE);
+ // redraw the scene so that markers can be calculated correctly
+ // g_StudioApp.GetCore()->GetDoc( )->UpdateClientScene( false );
+ m_PlayerWnd->update();
+}
+
+//==============================================================================
+/**
+ * When the active camera is changed, the display string needs to be changed. Hence
+ * find which entry is the one which is modified and update with the new string
+ * @param inCamera the camera that has been modified
+ */
+void CSceneView::OnEditCamerasTransformed()
+{
+}
+
+void CSceneView::OnAuthorZoomChanged()
+{
+ OnEditCameraChanged();
+}
+
+void CSceneView::OnRulerGuideToggled()
+{
+ m_PlayerContainerWnd->OnRulerGuideToggled();
+}
diff --git a/src/Authoring/Studio/_Win/UI/SceneView.h b/src/Authoring/Studio/_Win/UI/SceneView.h
new file mode 100644
index 00000000..12a58c6a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/SceneView.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_SCENE_VIEW_H
+#define INCLUDED_SCENE_VIEW_H 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include <QWidget>
+#include <QCursor>
+
+#include "EditorPane.h"
+#include "PlayerWnd.h"
+#include "PlayerContainerWnd.h"
+#include "DispatchListeners.h"
+
+class CStudioApp;
+class CHotKeys;
+class CMouseCursor;
+class CWndControl;
+
+//==============================================================================
+// Forwards
+//==============================================================================
+
+//==============================================================================
+// Class CSceneView
+//==============================================================================
+
+//==============================================================================
+/**
+ * CSceneView: Scene View.
+ *
+ * Provides the view of the scene and displays the 3D Client window.
+ */
+//==============================================================================
+class CSceneView : public QWidget,
+ public CEditorPane,
+ public CSelectedNodePropChangeListener,
+ public CClientPlayChangeListener,
+ public CEditCameraChangeListener
+{
+ Q_OBJECT
+ //==========================================================================
+ // Protected Properties
+ //==========================================================================
+protected:
+ bool m_FitClientToWindow = false; ///< True if user selected fit to window in prefrences
+ bool m_RegisteredKeyDown = false; ///< True if a key is down
+ CPlayerContainerWnd *m_PlayerContainerWnd = nullptr; ///< first-level child
+ CPlayerWnd *m_PlayerWnd = nullptr; ///< second-level child (grandchild)
+ QCursor m_ArrowCursor; ///< A pointer to the current cursor (changes according to mode)
+ QCursor m_CursorGroupMove; ///< The move group cursor
+ QCursor m_CursorGroupRotate; ///< The rotate group cursor
+ QCursor m_CursorGroupScale; ///< The scale group cursor
+ QCursor m_CursorItemMove; ///< The move item cursor
+ QCursor m_CursorItemRotate; ///< The rotate item cursor
+ QCursor m_CursorItemScale; ///< The scale item cursor
+ QCursor m_CursorEditCameraPan; ///< The edit camera pan cursor
+ QCursor m_CursorEditCameraRotate; ///< The edit camera rotate cursor
+ QCursor m_CursorEditCameraZoom; ///< The edit camera zoom cursor
+
+ long m_PreviousToolMode; ///< The previous tool mode (used when toggling with hotkeys to switch
+ ///back to previous mode on release)
+ long m_PreviousSelectMode; ///< The previous select mode
+
+ //==========================================================================
+ // Protected Methods
+ //==========================================================================
+protected: // create from serialization only
+
+ //==========================================================================
+ // Public Methods
+ //==========================================================================
+public:
+ CSceneView(CStudioApp *inStudioApp, QWidget *parent = nullptr);
+ CSceneView(); // used for serialization only!
+ virtual ~CSceneView();
+
+ bool HandleModifierUp(int inChar, int inRepCnt, Qt::KeyboardModifiers modifiers);
+ bool HandleModifierDown(int inChar, int inRepCnt, Qt::KeyboardModifiers modifiers);
+ bool PtInClientRect(const QPoint& inPoint);
+ void SetViewCursor();
+ void SetToolMode(long inMode);
+ void RecheckSizingMode();
+
+ void SetRegisteredKey(bool inStatus);
+ void RegisterGlobalKeyboardShortcuts(CHotKeys *inShortcutHandler);
+ bool GetKeyStatus();
+
+ void SetPlayerWndPosition();
+
+ // redirect to/from PlayerContainerWnd
+ bool IsDeploymentView();
+ void SetViewMode(CPlayerContainerWnd::EViewMode inViewMode);
+
+ // Tool helper methods
+ void RestorePreviousTool();
+ void SetToolOnCtrl();
+ void SetToolOnAlt();
+
+ void OnRulerGuideToggled();
+
+ // CSelectedNodePropChangeListener
+ void OnPropValueChanged(const Q3DStudio::CString &inPropertyName) override;
+ void OnMouseDragged(bool inConstrainXAxis, bool inConstrainYAxis) override;
+
+ // CClientPlayChangeListener
+ void OnTimeChanged(long inTime) override;
+
+ // CEditCameraChangeListener
+
+ void OnEditCameraChanged() override;
+ void OnEditCamerasTransformed() override;
+ void OnAuthorZoomChanged() override;
+
+ QSize sizeHint() const override;
+
+ void OnToolGroupSelection();
+ void OnToolItemSelection();
+public:
+
+ void resizeEvent(QResizeEvent *event) override;
+
+Q_SIGNALS:
+ void toolChanged();
+
+protected:
+ void RecalcMatte();
+
+ // Generated message map functions
+protected:
+ void OnSetCursor();
+ void showEvent(QShowEvent *event) override;
+ void keyPressEvent(QKeyEvent *event) override;
+private:
+ void onDropReceived();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif // INCLUDED_SCENE_VIEW_H
diff --git a/src/Authoring/Studio/_Win/UI/StartupDlg.cpp b/src/Authoring/Studio/_Win/UI/StartupDlg.cpp
new file mode 100644
index 00000000..286d94e3
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StartupDlg.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "StudioDefs.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "StartupDlg.h"
+
+#include "StudioPreferences.h"
+#include "ui_StartupDlg.h"
+
+#include <QtCore/qfileinfo.h>
+#include <QtGui/qpalette.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdir.h>
+
+// CStartupDlg dialog
+
+CStartupDlg::CStartupDlg(QWidget *pParent)
+ : QDialog(pParent, Qt::MSWindowsFixedSizeDialogHint | Qt::FramelessWindowHint)
+ , m_Choice(EStartupChoice_Invalid)
+ , m_RecentDocSelected("")
+ , m_ui(new Ui::StartupDlg)
+ , m_palette(nullptr)
+{
+ m_ui->setupUi(this);
+}
+
+CStartupDlg::~CStartupDlg()
+{
+ delete m_palette;
+}
+
+static QString GetFileTimeReadable(const CUICFile &inFile)
+{
+ QFileInfo finfo(inFile.GetAbsolutePath().toQString());
+ if (!finfo.exists())
+ return {};
+
+ return finfo.lastModified().toString("MM/dd/yyyy");
+}
+
+void CStartupDlg::showEvent(QShowEvent *ev)
+{
+ OnInitDialog();
+ QDialog::showEvent(ev);
+}
+
+void CStartupDlg::OnInitDialog()
+{
+ connect(m_ui->newDocument, &QPushButton::clicked, this, &CStartupDlg::OnNewDocClicked);
+ connect(m_ui->openDocument, &QPushButton::clicked, this, &CStartupDlg::OnOpenDocClicked);
+
+ // Load the product version
+ m_ProductVersionStr.Format(
+ ::LoadResourceString(IDS_UIC_STUDIO_VERSION),
+ static_cast<const wchar_t *>(CStudioPreferences::GetVersionString()));
+ m_ui->versionStr->setText(m_ProductVersionStr.toQString());
+
+ // Populate the recent document list
+ for (uint theIndex = 0; theIndex < RECENT_COUNT; ++theIndex) {
+ ClickableLabel *recent
+ = findChild<ClickableLabel *>(QStringLiteral("recent%1").arg(theIndex));
+ connect(recent, &ClickableLabel::clicked, this, &CStartupDlg::OnStnClickedStartupRecent);
+
+ recent->setProperty("recentIndex", theIndex);
+
+ if (m_RecentDocs.size() > theIndex) {
+ // Set the name
+ recent->setText(m_RecentDocs[theIndex].GetName().toQString());
+ // Set path and date to tooltip
+ QFileInfo thePath(m_RecentDocs[theIndex].GetAbsolutePath().toQString());
+ QString toolTip = thePath.absoluteDir().path();
+ toolTip.append(QStringLiteral("\n"));
+ toolTip.append(GetFileTimeReadable(m_RecentDocs[theIndex]));
+ recent->setToolTip(toolTip);
+ } else {
+ recent->hide();
+ }
+ }
+}
+
+void CStartupDlg::AddRecentItem(const CUICFile &inRecentItem)
+{
+ m_RecentDocs.push_back(inRecentItem);
+}
+
+CStartupDlg::EStartupChoice CStartupDlg::GetChoice()
+{
+ return m_Choice;
+}
+
+CUICFile CStartupDlg::GetRecentDoc() const
+{
+ return m_RecentDocSelected;
+}
+
+void CStartupDlg::OnNewDocClicked()
+{
+ m_Choice = EStartupChoice_NewDoc;
+ QDialog::accept();
+}
+
+void CStartupDlg::OnOpenDocClicked()
+{
+ m_Choice = EStartupChoice_OpenDoc;
+ QDialog::accept();
+}
+
+void CStartupDlg::OnStnClickedStartupRecent()
+{
+ const int index = sender()->property("recentIndex").toInt();
+ OpenRecent(index);
+}
+
+void CStartupDlg::OpenRecent(size_t inIndex)
+{
+ if (inIndex < m_RecentDocs.size()) {
+ m_RecentDocSelected = m_RecentDocs[inIndex];
+ m_Choice = EStartupChoice_OpenRecent;
+ QDialog::accept();
+ }
+}
+
+void CStartupDlg::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event)
+ if (m_palette)
+ return;
+
+ delete m_palette;
+ m_palette = new QPalette;
+ QPixmap pic = QPixmap(":/startup/open_dialog.png");
+ pic.setDevicePixelRatio(devicePixelRatio());
+ m_palette->setBrush(QPalette::Window, pic);
+ setPalette(*m_palette);
+ resize(pic.size());
+ setFixedSize(size());
+}
diff --git a/src/Authoring/Studio/_Win/UI/StartupDlg.h b/src/Authoring/Studio/_Win/UI/StartupDlg.h
new file mode 100644
index 00000000..6fbd0a2a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StartupDlg.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2006 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#ifndef INCLUDED_STARTUP_DLG
+#define INCLUDED_STARTUP_DLG 1
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include <QDialog>
+
+#include "UICString.h"
+#include "UICFile.h"
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class StartupDlg;
+}
+QT_END_NAMESPACE
+
+//==============================================================================
+/**
+ * CGLVersionDlg: Dialog class for showing what user can do upon startup
+ */
+//==============================================================================
+class CStartupDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ enum EStartupChoice {
+ EStartupChoice_Invalid = -1,
+ EStartupChoice_NewDoc,
+ EStartupChoice_OpenDoc,
+ EStartupChoice_OpenRecent,
+ EStartupChoice_Exit
+ };
+
+public:
+ CStartupDlg(QWidget *pParent = nullptr); // standard constructor
+ virtual ~CStartupDlg();
+
+protected:
+ const static int RECENT_COUNT = 5;
+
+ void paintEvent(QPaintEvent *event) override;
+ void showEvent(QShowEvent *) override;
+
+protected Q_SLOTS:
+ void OnNewDocClicked();
+ void OnOpenDocClicked();
+ void OnStnClickedStartupRecent();
+ void OpenRecent(size_t inIndex);
+
+private:
+ // Dialog background
+ QPalette *m_palette;
+
+ // Product version string
+ Q3DStudio::CString m_ProductVersionStr;
+
+ // Choice
+ EStartupChoice m_Choice = EStartupChoice_Invalid;
+
+ // Recent Docs
+ std::vector<CUICFile> m_RecentDocs;
+ CUICFile m_RecentDocSelected;
+
+public:
+ void OnInitDialog();
+ void AddRecentItem(const CUICFile &inRecentItem);
+ EStartupChoice GetChoice();
+ CUICFile GetRecentDoc() const;
+
+private:
+ QScopedPointer<Ui::StartupDlg> m_ui;
+};
+
+#endif // INCLUDED_STARTUP_DLG
diff --git a/src/Authoring/Studio/_Win/UI/StartupDlg.ui b/src/Authoring/Studio/_Win/UI/StartupDlg.ui
new file mode 100644
index 00000000..619c857a
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StartupDlg.ui
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StartupDlg</class>
+ <widget class="QDialog" name="StartupDlg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>442</width>
+ <height>650</height>
+ </rect>
+ </property>
+ <widget class="QLabel" name="recentDocLabel">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>250</y>
+ <width>411</width>
+ <height>29</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>RECENT FILES</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QWidget" name="verticalLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>30</x>
+ <y>290</y>
+ <width>401</width>
+ <height>341</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="recentList">
+ <item>
+ <widget class="ClickableLabel" name="recent0">
+ <property name="text">
+ <string>Recent0.uip</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="docIndex" stdset="0">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ClickableLabel" name="recent1">
+ <property name="text">
+ <string>Recent1.uip</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="docIndex" stdset="0">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ClickableLabel" name="recent2">
+ <property name="text">
+ <string>Recent2.uip</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="docIndex" stdset="0">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ClickableLabel" name="recent3">
+ <property name="text">
+ <string>Recent3.uip</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="docIndex" stdset="0">
+ <number>3</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="ClickableLabel" name="recent4">
+ <property name="text">
+ <string>Recent3.uip</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="docIndex" stdset="0">
+ <number>3</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="verticalLayoutWidget_2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>130</y>
+ <width>161</width>
+ <height>101</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="buttons">
+ <item>
+ <widget class="QPushButton" name="newDocument">
+ <property name="text">
+ <string>Create Project...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../MainFrm.qrc">
+ <normaloff>:/res/Toolbar-00.png</normaloff>:/res/Toolbar-00.png</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="openDocument">
+ <property name="text">
+ <string>Open Project...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../MainFrm.qrc">
+ <normaloff>:/res/Toolbar-01.png</normaloff>:/res/Toolbar-01.png</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QLabel" name="versionStr">
+ <property name="geometry">
+ <rect>
+ <x>290</x>
+ <y>90</y>
+ <width>141</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Qt 3D Studio version string</string>
+ </property>
+ </widget>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ClickableLabel</class>
+ <extends>QLabel</extends>
+ <header>ClickableLabel.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../../MainFrm.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.cpp b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.cpp
new file mode 100644
index 00000000..bde792eb
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.cpp
@@ -0,0 +1,590 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+
+#include "ui_StudioAppPrefsPage.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "UICOptions.h"
+#include "Doc.h"
+#include "StudioAppPrefsPage.h"
+#include "StudioConst.h"
+#include "StudioProjectSettings.h"
+#include "StudioPreferences.h"
+#include "StudioApp.h"
+#include "StudioPreferences.h"
+#include "StringLoader.h"
+#include "CommonConstants.h"
+#include "Views.h"
+#include "UICDMStudioSystem.h"
+#include "ClientDataModelBridge.h"
+#include "Core.h"
+#include "IStudioRenderer.h"
+
+#include <QColorDialog>
+#include <QMessageBox>
+
+#define WM_PREVIEW_DYNAMIC_CONTROLS WM_USER + 1200
+const long MIN_PAGESIZE = 380;
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioAppPrefsPage property page
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CStudioAppPrefsPage::CStudioAppPrefsPage(QWidget *parent)
+ : CStudioPreferencesPropPage(parent)
+ , m_nudgeValue(0.0)
+ , m_TimebarShowTime(FALSE)
+ , m_InterpolationIsSmooth(FALSE)
+ , m_ui(new Ui::StudioAppPrefsPage)
+{
+ m_Font = QFont(CStudioPreferences::GetFontFaceName());
+ m_Font.setPixelSize(CStudioPreferences::fontSize());
+
+ // Create a bold font for the group box text
+ m_BoldFont = m_Font;
+ m_BoldFont.setBold(true);
+
+ OnInitDialog();
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CStudioAppPrefsPage::~CStudioAppPrefsPage()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioAppPrefsPage message handlers
+
+void CStudioAppPrefsPage::OnInitDialog()
+{
+ m_ui->setupUi(this);
+ m_ui->m_editNudgeAmount->setValidator(new QDoubleValidator(this));
+
+ // Add tool tips for controls
+ m_ui->m_editNudgeAmount->setToolTip(::LoadResourceString(IDS_PREFS_NUDGEAMOUNT).toQString());
+ m_ui->m_DefaultInterpolation->setToolTip(::LoadResourceString(IDS_PREFS_INTERPOLATIONDEFAULT).toQString());
+ m_ui->m_checkTimelineAbsoluteSnapping->setToolTip(
+ ::LoadResourceString(IDS_PREFS_TIMELINEGRIDSNAPPING).toQString());
+ m_ui->m_SnapRangeCombo->setToolTip(::LoadResourceString(IDS_PREFS_TIMELINEGRIDRESOLUTION).toQString());
+ m_ui->m_buttonRestoreDefaults->setToolTip(::LoadResourceString(IDS_PREFS_RESTOREDEFAULTS).toQString());
+ m_ui->m_EditViewBGColor->setAutoFillBackground(true);
+
+ // Set fonts for child windows.
+ for (auto w : findChildren<QWidget *>())
+ w->setFont(m_Font);
+
+ // Make the group text bold
+ for (auto w : findChildren<QGroupBox *>())
+ w->setFont(m_BoldFont);
+
+#ifndef INCLUDE_EDIT_CAMERA
+ ASSERT(0);
+ // This won't work as all the controls have to move accordingly. Don't think INCLUDE_EDIT_CAMERA
+ // will be undefined
+ // Hide controls related to Edit Camera
+ m_ui->m_groupBoxEditingView->setVisible(false);
+#endif
+
+ // Load the settings for the controls
+ LoadSettings();
+
+ auto activated = static_cast<void(QComboBox::*)(int)>(&QComboBox::activated);
+ connect(m_ui->m_buttonRestoreDefaults, &QPushButton::clicked, this, &CStudioAppPrefsPage::OnButtonRestoreDefaults);
+ connect(m_ui->m_DefaultInterpolation, activated, this, &CStudioAppPrefsPage::OnSelChangeInterpolationDefault);
+ connect(m_ui->m_SnapRangeCombo, activated, this, &CStudioAppPrefsPage::OnSelChangeSnapRange);
+ connect(m_ui->m_checkTimelineAbsoluteSnapping, &QCheckBox::clicked, this, &CStudioAppPrefsPage::OnCheckTimelineAbsoluteSnapping);
+ connect(m_ui->m_EditViewBGColor, &QPushButton::clicked, this, &CStudioAppPrefsPage::OnBgColorButtonClicked);
+ connect(m_ui->m_editNudgeAmount, &QLineEdit::textEdited, this, &CStudioAppPrefsPage::OnChangeEditNudgeAmount);
+ connect(m_ui->m_EditViewStartupView, activated, this, &CStudioAppPrefsPage::OnSelChangeStartupView);
+ connect(m_ui->m_PreviewSelector, activated, this, &CStudioAppPrefsPage::OnChangePreviewConfiguration);
+}
+
+//==============================================================================
+/**
+ * LoadSettings: Load the settings from the CDoc and set the control values.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::LoadSettings()
+{
+ m_nudgeValue = CStudioPreferences::GetNudgeAmount();
+
+ // Get the Interpolation Preference
+ Q3DStudio::CString theComboItem;
+ theComboItem = ::LoadResourceString(IDS_PREF_INTERPOLATION_1);
+ m_ui->m_DefaultInterpolation->addItem(theComboItem.toQString());
+ theComboItem = ::LoadResourceString(IDS_PREF_INTERPOLATION_2);
+ m_ui->m_DefaultInterpolation->addItem(theComboItem.toQString());
+
+ long theInterpolationPref = 0;
+ if (CStudioPreferences::GetInterpolation())
+ theInterpolationPref = 0;
+ else
+ theInterpolationPref = 1;
+ m_ui->m_DefaultInterpolation->setCurrentIndex(theInterpolationPref);
+
+ // Timeline snapping grid
+ m_ui->m_checkTimelineAbsoluteSnapping->setChecked(
+ CStudioPreferences::IsTimelineSnappingGridActive());
+
+ // Load the combo boxes with values from the string table so that they are localizable.
+ // The scale mode
+ (void)theComboItem;
+ theComboItem = ::LoadResourceString(IDS_PREF_SNAPRANGE_1);
+ m_ui->m_SnapRangeCombo->addItem(theComboItem.toQString());
+ theComboItem = ::LoadResourceString(IDS_PREF_SNAPRANGE_2);
+ m_ui->m_SnapRangeCombo->addItem(theComboItem.toQString());
+ theComboItem = ::LoadResourceString(IDS_PREF_SNAPRANGE_3);
+ m_ui->m_SnapRangeCombo->addItem(theComboItem.toQString());
+ long theResolution = (long)CStudioPreferences::GetTimelineSnappingGridResolution();
+ m_ui->m_SnapRangeCombo->setCurrentIndex(theResolution);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ // Edit View Background color
+ // TODO: Visualize selected color on the bg color change button
+ InitEditStartViewCombo();
+#endif
+
+ EnableOptions();
+
+ LoadPreviewSelections();
+
+ m_ui->m_editNudgeAmount->setText(QString::number(m_nudgeValue));
+ m_bgColor = CStudioPreferences::GetEditViewBackgroundColor();
+ updateColorButton();
+}
+
+void CStudioAppPrefsPage::updateColorButton()
+{
+ QPalette pal = m_ui->m_EditViewBGColor->palette();
+ pal.setColor(QPalette::Button, m_bgColor);
+ m_ui->m_EditViewBGColor->setPalette(pal);
+}
+
+//==============================================================================
+/**
+ * SaveSettings: Save the settings from the controls to the CDoc
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::SaveSettings()
+{
+ // Nudge amount
+ CStudioPreferences::SetNudgeAmount(m_nudgeValue);
+
+ // Default interpolation
+ g_StudioApp.GetCore()->GetDoc()->SetDefaultKeyframeInterpolation(
+ m_ui->m_DefaultInterpolation->currentIndex() == 0);
+
+ // Timeline snapping grid
+ CStudioPreferences::SetTimelineSnappingGridActive(
+ m_ui->m_checkTimelineAbsoluteSnapping->isChecked());
+ long theCurrentSelection = m_ui->m_SnapRangeCombo->currentIndex();
+ CStudioPreferences::SetTimelineSnappingGridResolution((ESnapGridResolution)theCurrentSelection);
+
+#ifdef INCLUDE_EDIT_CAMERA
+ // Edit View Background Color
+ CStudioPreferences::SetEditViewBackgroundColor(m_bgColor);
+ //
+ // g_StudioApp.GetCore()->GetDoc( )->SetEditViewBackgroundColor( theColor );
+
+ // Preferred Startup View
+ long theSel = m_ui->m_EditViewStartupView->currentIndex();
+ long theNumItems = m_ui->m_EditViewStartupView->count();
+ CStudioPreferences::SetPreferredStartupView(
+ (theSel == theNumItems - 1) ? -1 : theSel); // -1 for deployment view
+// g_StudioApp.GetCore()->GetDoc( )->UpdateClientScene( false );
+#endif
+
+ SavePreviewSettings();
+}
+
+//==============================================================================
+/**
+ * OnApply: Handler for the Apply button
+ *
+ * @param None
+ */
+//==============================================================================
+bool CStudioAppPrefsPage::OnApply()
+{
+ // Apply was clicked - save settings and disabled the Apply button
+ this->SaveSettings();
+
+ this->SetModified(FALSE);
+
+ // Request that the renderer refreshes as settings may have changed
+ g_StudioApp.GetRenderer().RequestRender();
+
+ return CStudioPreferencesPropPage::OnApply();
+}
+
+//==============================================================================
+/**
+ * OnOK: Handler for the OK button
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnOK()
+{
+ CStudioPreferencesPropPage::OnOK();
+}
+
+//==============================================================================
+/**
+ * OnCancel: Handler for the Cancel button
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnCancel()
+{
+#ifdef INCLUDE_EDIT_CAMERA
+ // if the edit view background color was changed, reset it back to the original
+ // value
+ CColor theOrigColor = CStudioPreferences::GetEditViewBackgroundColor();
+ if (m_bgColor != theOrigColor) {
+ // g_StudioApp.GetCore()->GetDoc( )->SetEditViewBackgroundColor( theOrigColor );
+ // g_StudioApp.GetCore()->GetDoc( )->UpdateClientScene( false );
+ }
+#endif
+ CStudioPreferencesPropPage::OnCancel();
+}
+
+//==============================================================================
+/**
+ * OnButtonRestoreDefaults: Restore the defaults and exit the preferences.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnButtonRestoreDefaults()
+{
+ Q3DStudio::CString theMessage;
+ Q3DStudio::CString theTitle;
+ int theChoice = 0;
+
+ // Load the text strings for the message box
+ theTitle = ::LoadResourceString(IDS_PREF_RESTOREDEFAULT_TITLE);
+ theMessage = ::LoadResourceString(IDS_PREF_RESTOREDEFAULT_TEXT);
+
+ // Ask the user if she really wants to do this
+ theChoice = QMessageBox::question(this, theTitle.toQString(), theMessage.toQString());
+
+ // If the "yes" button was selected
+ if (theChoice == QMessageBox::Yes) {
+ // Restore default preferences by passing PREFS_RESET_DEFAULTS back
+ // to the CStudioDocPreferences (that called this preferences sheet)
+ CStudioPreferencesPropPage::EndDialog(PREFS_RESET_DEFAULTS);
+ }
+}
+
+//==============================================================================
+/**
+ * OnSelChangeInterpolationDefault: CBN_SELCHANGE handler for the
+ * IDC_INTERPOLATION_DEFAULT combo
+ *box.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnSelChangeInterpolationDefault()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeEditNudgeAmount: EN_UPDATE handler for the IDC_EDIT_NUDGE
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnChangeEditNudgeAmount()
+{
+ QString editStr = m_ui->m_editNudgeAmount->text();
+ m_nudgeValue = editStr.toDouble();
+ this->SetModified(true);
+}
+
+//==============================================================================
+/**
+ * OnSelChangeSnapRange: CBN_SELCHANGE handler for the IDC_SNAPRANGE combo box.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnSelChangeSnapRange()
+{
+ this->SetModified(true);
+}
+
+//==============================================================================
+/**
+ * OnCheckTimelineAbsoluteSnapping: Handler for the IDC_CHECK_TIMELINEABSOLUTESNAPPING
+ *checkbox.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnCheckTimelineAbsoluteSnapping()
+{
+ this->SetModified(true);
+ this->EnableOptions();
+}
+
+//==============================================================================
+/**
+ * EnableOptions: Enable/disable options.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioAppPrefsPage::EnableOptions()
+{
+ m_ui->m_SnapRangeCombo->setEnabled(m_ui->m_checkTimelineAbsoluteSnapping->isChecked());
+}
+
+//==============================================================================
+/**
+ * OnSelChangeStartupView: CBN_SELCHANGE handler for the IDC_COMBO_EDIT_STARTUP_VIEW
+ * Combo box
+ */
+void CStudioAppPrefsPage::OnSelChangeStartupView()
+{
+ this->SetModified(true);
+}
+
+//==============================================================================
+/**
+ * Initialise the combo box that displays the preferred startup view.
+ * Set the initial selection to that saved to the preferences
+ */
+//==============================================================================
+void CStudioAppPrefsPage::InitEditStartViewCombo()
+{
+ Q3DStudio::IStudioRenderer &theRenderer = g_StudioApp.GetRenderer();
+ QStringList theCameraNames;
+ theRenderer.GetEditCameraList(theCameraNames);
+ for (size_t idx = 0, end = theCameraNames.size(); idx < end; ++idx) {
+ m_ui->m_EditViewStartupView->addItem(
+ theCameraNames.at(idx));
+ m_ui->m_EditViewStartupView->setItemData(m_ui->m_EditViewStartupView->count() - 1, QVariant((int)idx + 1));
+ }
+
+ m_ui->m_EditViewStartupView->addItem("--------------------------");
+ m_ui->m_EditViewStartupView->setItemData(m_ui->m_EditViewStartupView->count() - 1, -1); // set to an invalid pointer
+ // add the deployment view as the last selection
+ m_ui->m_EditViewStartupView->addItem(::LoadResourceString(IDS_SCENE_CAMERA_VIEW).toQString());
+ m_ui->m_EditViewStartupView->setItemData(m_ui->m_EditViewStartupView->count() - 1, 0);
+
+ long thePreferredView = CStudioPreferences::GetPreferredStartupView();
+ long theNumItems = m_ui->m_EditViewStartupView->count();
+ if (thePreferredView == -1) // deployment view
+ m_ui->m_EditViewStartupView->setCurrentIndex(theNumItems - 1); // set to the last one
+ else if (thePreferredView < theNumItems - 1)
+ m_ui->m_EditViewStartupView->setCurrentIndex(thePreferredView);
+ else // possibly from old content where cameras are removed
+ m_ui->m_EditViewStartupView->setCurrentIndex(0);
+}
+
+void CStudioAppPrefsPage::LoadPreviewSelections()
+{
+ // Load the configurations from all the .build files
+ Q3DStudio::CBuildConfigurations &theConfig = g_StudioApp.GetCore()->GetBuildConfigurations();
+ Q3DStudio::CBuildConfigurations::TBuildConfigurations theConfigurations =
+ theConfig.GetConfigurations();
+ Q3DStudio::CBuildConfigurations::TBuildConfigurations::iterator theIter;
+ for (theIter = theConfigurations.begin(); theIter != theConfigurations.end(); ++theIter) {
+ const Q3DStudio::CString &theConfig = theIter->first;
+ m_ui->m_PreviewSelector->addItem(theConfig.toQString());
+ m_ui->m_PreviewSelector->setItemData(m_ui->m_PreviewSelector->count() - 1,
+ QVariant::fromValue(theIter->second));
+ }
+
+ int thePreviewSelected = m_ui->m_PreviewSelector->findText(CStudioPreferences::GetPreviewConfig().toQString());
+ m_ui->m_PreviewSelector->setCurrentIndex(thePreviewSelected);
+ if (thePreviewSelected == -1) {
+ // select the first build configuration, or if no conriguration, the first application, i.e.
+ // AMPlayer
+ m_ui->m_PreviewSelector->setCurrentIndex(0);
+ long thePreviewCount = m_ui->m_PreviewSelector->count();
+ for (long theIndex = 0; theIndex < thePreviewCount; ++theIndex) {
+ if (m_ui->m_PreviewSelector->itemData(theIndex).value<Q3DStudio::CBuildConfiguration *>() != nullptr) {
+ m_ui->m_PreviewSelector->setCurrentIndex(theIndex);
+ break;
+ }
+ }
+ }
+
+ LoadBuildProperties();
+}
+
+//==============================================================================
+/**
+ * When the build configuration is changed, all the properties have to be updated.
+ */
+//==============================================================================
+void CStudioAppPrefsPage::OnChangePreviewConfiguration()
+{
+ LoadBuildProperties();
+}
+
+void CStudioAppPrefsPage::OnBgColorButtonClicked()
+{
+ QColorDialog dlg(this);
+ dlg.setCurrentColor(m_bgColor);
+ if (dlg.exec() == QDialog::Accepted) {
+ m_bgColor = dlg.selectedColor();
+ updateColorButton();
+ }
+
+ this->SetModified(true);
+}
+
+//==============================================================================
+/**
+ * Load the build properties for the current preview application selected
+ */
+//==============================================================================
+void CStudioAppPrefsPage::LoadBuildProperties()
+{
+ // Remove those dynamic controls
+ RemovePreviewPropertyControls();
+
+ if (m_ui->m_PreviewSelector->count() > 0) {
+ Q3DStudio::CBuildConfiguration *theConfig =
+ m_ui->m_PreviewSelector->itemData(m_ui->m_PreviewSelector->currentIndex()).value<Q3DStudio::CBuildConfiguration *>();
+ if (theConfig) {
+ // Only configuration read from .build files will have the ItemDataPtr set.
+
+ Q3DStudio::CBuildConfiguration::TConfigProperties &theProperties =
+ theConfig->GetBuildProperties();
+
+ auto layout = qobject_cast<QFormLayout *>(m_ui->groupBoxPreview->layout());
+ auto activated = static_cast<void(QComboBox::*)(int)>(&QComboBox::activated);
+
+ if (theProperties.empty() == false) {
+ Q3DStudio::CBuildConfiguration::TConfigProperties::iterator theIter;
+
+ for (theIter = theProperties.begin(); theIter != theProperties.end(); ++theIter) {
+ Q3DStudio::CBuildConfiguration::TConfigPropertyValues &theValues =
+ theIter->GetAcceptableValues();
+ // Only create the combo if there is more than 1 choices
+ if (theValues.size() > 1) {
+ Q3DStudio::CBuildConfiguration::TConfigPropertyValues::iterator
+ theValueIter;
+ long theMaxLength = 0;
+ for (theValueIter = theValues.begin(); theValueIter != theValues.end();
+ ++theValueIter) {
+ long theLabelLength = theValueIter->GetLabel().Length();
+ if (theLabelLength > theMaxLength)
+ theMaxLength = theLabelLength;
+ }
+
+ QLabel *theStaticText = new QLabel(theIter->GetLabel().toQString());
+ theStaticText->setFont(m_Font);
+ QComboBox *thePropertyDropdown = new QComboBox();
+ connect(thePropertyDropdown, activated, [&]() {SetModified(true);});
+ thePropertyDropdown->setFont(m_Font);
+ layout->addRow(theStaticText, thePropertyDropdown);
+
+ m_BuildProperties.push_back(std::make_pair(
+ &*theIter, std::make_pair(theStaticText, thePropertyDropdown)));
+
+ Q3DStudio::CString thePropertyValue =
+ CStudioPreferences::GetPreviewProperty(theIter->GetName());
+ for (theValueIter = theValues.begin(); theValueIter != theValues.end();
+ ++theValueIter) {
+ thePropertyDropdown->addItem(theValueIter->GetLabel().toQString());
+ thePropertyDropdown->setItemData(thePropertyDropdown->count() - 1, QVariant::fromValue(&*theValueIter));
+ if (theValueIter->GetName() == thePropertyValue)
+ thePropertyDropdown->setCurrentIndex(thePropertyDropdown->count() - 1);
+ }
+
+ // Select the first entry
+ if (thePropertyDropdown->currentIndex() == -1)
+ thePropertyDropdown->setCurrentIndex(0);
+ }
+ }
+ }
+ }
+ }
+}
+
+void CStudioAppPrefsPage::SavePreviewSettings()
+{
+ QString thePreviewApp = m_ui->m_PreviewSelector->currentText();
+ CStudioPreferences::SetPreviewConfig(Q3DStudio::CString::fromQString(thePreviewApp));
+
+ std::list<TBuildNameControlPair>::iterator theIter;
+ for (theIter = m_BuildProperties.begin(); theIter != m_BuildProperties.end(); ++theIter) {
+ QComboBox *theCombo = theIter->second.second;
+ Q3DStudio::CString theName = theIter->first->GetName();
+ Q3DStudio::CBuildConfiguration::SConfigPropertyValue *thePropertyValue =
+ theCombo->itemData(theCombo->currentIndex()).value<Q3DStudio::CBuildConfiguration::SConfigPropertyValue *>();
+ CStudioPreferences::SetPreviewProperty(theName, thePropertyValue->GetName());
+ }
+}
+
+//==============================================================================
+/**
+ * Remove all the dynamically added controls that was read in from the build file
+ */
+//==============================================================================
+void CStudioAppPrefsPage::RemovePreviewPropertyControls()
+{
+ // Remove the created control
+ std::list<TBuildNameControlPair>::iterator theIter;
+ for (theIter = m_BuildProperties.begin(); theIter != m_BuildProperties.end(); ++theIter) {
+ delete theIter->second.first;
+ delete theIter->second.second;
+ }
+ m_BuildProperties.clear();
+}
diff --git a/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.h b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.h
new file mode 100644
index 00000000..a3be36c2
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_STUDIOAPPPREFSPAGE_H__FA329789_EEDD_4439_B6F6_AAD0FED5285F__INCLUDED_)
+#define AFX_STUDIOAPPPREFSPAGE_H__FA329789_EEDD_4439_B6F6_AAD0FED5285F__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "BuildConfigParser.h"
+
+#include "StudioPreferencesPropSheet.h"
+
+class QComboBox;
+class QLabel;
+
+class CStudioApp;
+/////////////////////////////////////////////////////////////////////////////
+// CStudioAppPrefsPage dialog
+
+namespace Ui
+{
+ class StudioAppPrefsPage;
+}
+
+class CStudioAppPrefsPage : public CStudioPreferencesPropPage
+{
+ Q_OBJECT
+protected:
+ typedef std::pair<QLabel *, QComboBox *> TBuildLabelDropdownPair;
+ typedef std::pair<Q3DStudio::CBuildConfiguration::SConfigProperty *, TBuildLabelDropdownPair>
+ TBuildNameControlPair;
+
+ // Construction
+public:
+ explicit CStudioAppPrefsPage(QWidget *parent = nullptr);
+ ~CStudioAppPrefsPage();
+
+ // Dialog Data
+ QColor m_bgColor;
+
+ double m_nudgeValue;
+
+public:
+ bool OnApply() override;
+ void OnOK() override;
+ void OnCancel() override;
+
+ // Implementation
+protected:
+ BOOL m_TimebarShowTime; ///< TRUE if timebars are to display their time value
+ BOOL m_InterpolationIsSmooth; ///< TRUE if default interpolation is smooth
+ QFont m_Font; ///< Font for text
+ QFont m_BoldFont; ///< Bold font for drawing the group boxes
+ void EnableOptions();
+ void LoadSettings();
+ void SaveSettings();
+
+ // Generated message map functions
+ virtual void OnInitDialog();
+ void OnButtonRestoreDefaults();
+ void OnSelChangeInterpolationDefault();
+ void OnSelChangeSnapRange();
+ void OnCheckTimelineAbsoluteSnapping();
+ void OnChangeEditNudgeAmount();
+ void OnSelChangeStartupView();
+ void OnChangePreviewConfiguration();
+ void OnBgColorButtonClicked();
+
+protected: // helper functions
+ void InitEditStartViewCombo();
+
+protected:
+ std::list<TBuildNameControlPair>
+ m_BuildProperties; ///< List of build properties, either ComboBox or Static
+
+ void LoadPreviewSelections();
+ void LoadBuildProperties();
+ void SavePreviewSettings();
+ void RemovePreviewPropertyControls();
+
+ QScopedPointer<Ui::StudioAppPrefsPage> m_ui;
+
+private:
+ void updateColorButton();
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STUDIOAPPPREFSPAGE_H__FA329789_EEDD_4439_B6F6_AAD0FED5285F__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.ui b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.ui
new file mode 100644
index 00000000..5e1b88c5
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioAppPrefsPage.ui
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StudioAppPrefsPage</class>
+ <widget class="QWidget" name="StudioAppPrefsPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>347</width>
+ <height>387</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Nudge Increment</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_editNudgeAmount</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Default Interpolation</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_DefaultInterpolation</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="m_DefaultInterpolation"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="m_checkTimelineAbsoluteSnapping">
+ <property name="text">
+ <string>Timeline Snapping Grid</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="m_SnapRangeCombo"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="m_editNudgeAmount"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_groupBoxEditingView">
+ <property name="title">
+ <string>Editing View</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Background Color</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_EditViewBGColor</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" colspan="2">
+ <widget class="QPushButton" name="m_EditViewBGColor">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Preferred Startup View</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_EditViewStartupView</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" colspan="2">
+ <widget class="QComboBox" name="m_EditViewStartupView"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_buttonRestoreDefaults">
+ <property name="maximumSize">
+ <size>
+ <width>293</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Restore Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBoxPreview">
+ <property name="title">
+ <string>Preview</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Configuration</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_PreviewSelector</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="m_PreviewSelector"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/_Win/UI/StudioPaletteBar.cpp b/src/Authoring/Studio/_Win/UI/StudioPaletteBar.cpp
new file mode 100644
index 00000000..747ba442
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioPaletteBar.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioPaletteBar.h"
+#include "Preferences.h"
+#include "StudioConst.h"
+#include "PaletteState.h"
+#include "StudioApp.h"
+
+//==============================================================================
+// Message Maps
+//==============================================================================
+BEGIN_MESSAGE_MAP(CStudioPaletteBar, CViewBar)
+//{{AFX_MSG_MAP(CStudioPaletteBar)
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+// Constructors/Destructors
+//==============================================================================
+
+CStudioPaletteBar::CStudioPaletteBar()
+{
+}
+
+CStudioPaletteBar::~CStudioPaletteBar()
+{
+}
+
+void CStudioPaletteBar::SetHorz(CSize inSize)
+{
+ m_szHorz.cx = max(m_szMinHorz.cx, inSize.cx);
+ m_szHorz.cy = max(m_szMinHorz.cy, inSize.cy);
+}
+
+void CStudioPaletteBar::SetVert(CSize inSize)
+{
+ m_szVert.cx = max(m_szMinVert.cx, inSize.cx);
+ m_szVert.cy = max(m_szMinVert.cy, inSize.cy);
+}
+
+void CStudioPaletteBar::SetFloat(CSize inSize)
+{
+ m_szFloat.cx = max(m_szMinFloat.cx, inSize.cx);
+ m_szFloat.cy = max(m_szMinFloat.cy, inSize.cy);
+}
+
+//==============================================================================
+/**
+ * Create: A wrapper around the CViewBar::Create() method.
+ *
+ * Simplifies the create parameters for the palette bar.
+ *
+ * @return BOOL The result from the Create() call (FALSE if unsuccessful)
+ */
+BOOL CStudioPaletteBar::Create(CString inTitle, CCreateContext *inCreateContext, long inControlId,
+ CWnd *theParent /* = nullptr */)
+{
+ BOOL theReturnValue;
+
+ // Add the title of the bar to the registry location.
+ m_PaletteName = inTitle;
+
+ // Create the palette bar.
+ theReturnValue =
+ CViewBar::Create(theParent, inCreateContext, inTitle, WS_CHILD | CBRS_TOP, inControlId);
+ ASSERT(theReturnValue == TRUE);
+
+ SetBarStyle(GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
+ EnableDocking(CBRS_ALIGN_ANY);
+
+ return theReturnValue;
+}
+
+//==============================================================================
+/**
+ * Call this function when the palette's view needs to be updated for the first
+ * time on application launch.
+ */
+void CStudioPaletteBar::InitialUpdate()
+{
+ // Send the update message to the attached CView
+ SendMessageToDescendants(WM_STUDIO_INITIALIZE_PALETTES, reinterpret_cast<WPARAM>(&g_StudioApp));
+}
+
+//==============================================================================
+/**
+ * Shows or hides the palette and saves the visibility.
+ *
+ * This function should be called instead of ShowWindow(), so that the visibility
+ * will be saved in the registry.
+ */
+void CStudioPaletteBar::ShowPalette(bool inState /* = true */)
+{
+ ShowWindow(inState ? SW_SHOW : SW_HIDE);
+ m_pDockSite->ShowControlBar(this, inState, FALSE);
+}
+
+//==============================================================================
+/**
+ * Query the view associated with this palette
+ */
+CView *CStudioPaletteBar::GetView()
+{
+ // Looks like the first grandchild is the view
+ return (CView *)GetWindow(GW_CHILD)->GetWindow(GW_CHILD);
+}
+
+//==============================================================================
+// Message Maps
+//==============================================================================
+BEGIN_MESSAGE_MAP(CStudioDialog, CMiniFrameWnd)
+//{{AFX_MSG_MAP(CStudioDialog)
+//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//==============================================================================
+// Constructors/Destructors
+//==============================================================================
+
+CStudioDialog::CStudioDialog()
+{
+}
+
+CStudioDialog::~CStudioDialog()
+{
+}
+
+//==============================================================================
+/**
+ * Create: A wrapper around the CMiniFrameWnd::Create() method.
+ *
+ * Simplifies the create parameters for the palette bar.
+ *
+ * @return BOOL The result from the Create() call (FALSE if unsuccessful)
+ */
+/*
+BOOL CStudioDialog::Create( CString inTitle, CCreateContext* inCreateContext, long inControlId,
+CWnd* theParent )
+{
+ BOOL theReturnValue;
+
+ // Add the title of the bar to the registry location.
+ m_DialogName = inTitle;
+
+ // Create the palette bar.
+ theReturnValue = CMiniFrameWnd::Create( theParent, inCreateContext, _T( inTitle ), WS_CHILD
+| CBRS_TOP, inControlId );
+ ASSERT( theReturnValue == TRUE );
+
+ SetBarStyle( GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC );
+ EnableDocking(CBRS_ALIGN_ANY);
+
+ return theReturnValue;
+}*/
+
+//==============================================================================
+/**
+* Create: A wrapper around the CMiniFrameWnd::Create() method.
+*
+* Simplifies the create parameters for the palette bar.
+*
+* @return BOOL The result from the Create() call (FALSE if unsuccessful)
+*/
+//==============================================================================
+BOOL CStudioDialog::Create(CString inTitle, CCreateContext *inCreateContext,
+ CWnd *theParent /* = nullptr */)
+{
+ CString theWndClass = AfxRegisterWndClass(CS_DBLCLKS, ::LoadCursor(nullptr, IDC_ARROW));
+ BOOL theReturnValue;
+
+ // Add the title of the bar to the registry location.
+ m_DialogName = inTitle;
+
+ // Create the palette bar.
+ theReturnValue = CFrameWnd::Create(
+ theWndClass, inTitle, WS_SYSMENU | MFS_SYNCACTIVE | WS_POPUP | WS_CAPTION | MFS_THICKFRAME,
+ CRect(300, 300, 550, 800), theParent, nullptr, 0, inCreateContext);
+
+ // Get the view to initialize.
+ this->InitialUpdateFrame(inCreateContext->m_pCurrentDoc, FALSE);
+
+ // Get a handle to the system menu on the palette window
+ CMenu *theSystemMenu = GetSystemMenu(FALSE);
+
+ // Remove the unwanted system menu items
+ theSystemMenu->DeleteMenu(SC_MINIMIZE, MF_BYCOMMAND);
+ theSystemMenu->DeleteMenu(SC_MAXIMIZE, MF_BYCOMMAND);
+ theSystemMenu->DeleteMenu(SC_RESTORE, MF_BYCOMMAND);
+
+ return theReturnValue;
+}
+
+//==============================================================================
+/**
+ * Call this function when the palette's view needs to be updated for the first
+ * time on application launch.
+ */
+void CStudioDialog::InitialUpdate()
+{
+ // Send the update message to the attached CView
+ SendMessageToDescendants(WM_STUDIO_INITIALIZE_PALETTES, reinterpret_cast<WPARAM>(&g_StudioApp));
+}
+
+//==============================================================================
+/**
+ * Shows or hides the dialog and saves the visibility.
+ *
+ * This function should be called instead of ShowWindow(), so that the visibility
+ * will be saved in the registry.
+ */
+void CStudioDialog::ShowDialog(bool inState /* = true */)
+{
+ ShowWindow(inState ? SW_SHOW : SW_HIDE);
+ // m_pDockSite->ShowControlBar( this, inState, FALSE );
+}
+
+//==============================================================================
+/**
+ * Query the view associated with this palette
+ */
+CView *CStudioDialog::GetView()
+{
+ // Looks like the first grandchild is the view
+ return (CView *)GetWindow(GW_CHILD)->GetWindow(GW_CHILD);
+}
diff --git a/src/Authoring/Studio/_Win/UI/StudioPaletteBar.h b/src/Authoring/Studio/_Win/UI/StudioPaletteBar.h
new file mode 100644
index 00000000..eed3fd05
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioPaletteBar.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_STUDIO_PALETTE_BAR_H
+#define INCLUDED_STUDIO_PALETTE_BAR_H 1
+
+#pragma once
+
+#include "ViewBar.h"
+
+class CStudioApp;
+class CPaletteState;
+class CStudioApp;
+
+class CStudioPaletteBar : public CViewBar
+{
+public:
+ CStudioPaletteBar();
+ virtual ~CStudioPaletteBar();
+
+ virtual BOOL Create(CString inTitle, CCreateContext *inCreateContext, long inControlId,
+ CWnd *theParent = nullptr);
+
+ void ShowPalette(bool inState = true);
+ void InitialUpdate();
+ CView *GetView();
+ CSize GetHorz() { return m_szHorz; }
+ void SetHorz(CSize inSize);
+ CSize GetVert() { return m_szVert; }
+ void SetVert(CSize inSize);
+ CSize GetFloat() { return m_szFloat; }
+ void SetFloat(CSize inSize);
+
+protected:
+ Q3DStudio::CString m_PaletteName;
+
+ //{{AFX_MSG(CStudioPaletteBar)
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+class CStudioDialog : public CMiniFrameWnd
+{
+public:
+ CStudioDialog();
+ virtual ~CStudioDialog();
+
+ virtual BOOL Create(CString inTitle, CCreateContext *inCreateContext, CWnd *theParent = nullptr);
+
+ void ShowDialog(bool inState = true);
+ void InitialUpdate();
+ CView *GetView();
+
+protected:
+ Q3DStudio::CString m_DialogName;
+
+ //{{AFX_MSG(CStudioDialog)
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // INCLUDED_STUDIO_PALETTE_BAR_H
diff --git a/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.cpp b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.cpp
new file mode 100644
index 00000000..eb645d5e
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+
+#include "ui_StudioPreferencesPropSheet.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioPreferences.h"
+#include "StudioPreferencesPropSheet.h"
+#include "StudioProjectSettingsPage.h"
+#include "StringLoader.h"
+
+#include <QPushButton>
+
+CStudioPreferencesPropPage::CStudioPreferencesPropPage(QWidget *parent)
+ : QWidget(parent)
+{
+}
+
+void CStudioPreferencesPropPage::SetModified(bool modified)
+{
+ setProperty("modified", modified);
+
+ auto s = sheet();
+ if (s) {
+ auto buttons = s->findChild<QDialogButtonBox *>();
+ bool anyModified = false;
+ for (auto page : s->findChildren<CStudioPreferencesPropPage *>())
+ anyModified |= page->property("modified").toBool();
+ buttons->button(QDialogButtonBox::Apply)->setEnabled(anyModified);
+ }
+}
+
+CStudioPreferencesPropSheet* CStudioPreferencesPropPage::sheet()
+{
+ QWidget *parent = parentWidget();
+ while (parent != nullptr) {
+ if (auto sheet = qobject_cast<CStudioPreferencesPropSheet *>(parent))
+ return sheet;
+ parent = parent->parentWidget();
+ }
+ return nullptr;
+}
+
+
+void CStudioPreferencesPropPage::EndDialog(int returnCode)
+{
+ auto s = sheet();
+ if (s)
+ s->done(returnCode);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioPreferencesPropSheet
+
+CStudioPreferencesPropSheet::CStudioPreferencesPropSheet(int nIDCaption, QWidget *pParentWnd,
+ int iSelectPage)
+ : QDialog(pParentWnd)
+ , m_ui(new Ui::StudioPreferencesPropSheet)
+{
+ setWindowTitle(::LoadResourceString(nIDCaption).toQString());
+ OnInitDialog();
+ m_ui->m_TabCtrl->setCurrentIndex(iSelectPage);
+}
+
+CStudioPreferencesPropSheet::CStudioPreferencesPropSheet(const QString &pszCaption, QWidget *pParentWnd,
+ int iSelectPage)
+ : QDialog(pParentWnd)
+ , m_ui(new Ui::StudioPreferencesPropSheet)
+{
+ setWindowTitle(pszCaption);
+ OnInitDialog();
+ m_ui->m_TabCtrl->setCurrentIndex(iSelectPage);
+}
+
+CStudioPreferencesPropSheet::~CStudioPreferencesPropSheet()
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioPreferencesPropSheet message handlers
+
+//==============================================================================
+/**
+ * OnInitDialog: Handle the WM_INITDIALOG message to initialize the property sheet.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioPreferencesPropSheet::OnInitDialog()
+{
+ m_ui->setupUi(this);
+ m_ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
+
+ m_Font = QFont(CStudioPreferences::GetFontFaceName(), 8);
+ setFont(m_Font);
+
+ connect(m_ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked,
+ this, &CStudioPreferencesPropSheet::apply);
+}
+
+bool CStudioPreferencesPropSheet::apply()
+{
+ for (auto page : findChildren<CStudioPreferencesPropPage *>()) {
+ if (!page->OnApply())
+ return false;
+ }
+ return true;
+}
+
+void CStudioPreferencesPropSheet::accept()
+{
+ if (apply())
+ QDialog::accept();
+}
+
+void CStudioPreferencesPropSheet::reject()
+{
+ for (auto page : findChildren<CStudioPreferencesPropPage *>())
+ page->OnCancel();
+ QDialog::reject();
+}
diff --git a/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.h b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.h
new file mode 100644
index 00000000..7057ba0f
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_STUDIOPREFERENCESPROPSHEET_H__FADC69CE_5F0E_4F7E_A906_ED6052BBECF6__INCLUDED_)
+#define AFX_STUDIOPREFERENCESPROPSHEET_H__FADC69CE_5F0E_4F7E_A906_ED6052BBECF6__INCLUDED_
+
+#pragma once
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include <QDialog>
+
+class CStudioProjectSettingsPage;
+class CStudioPreferencesPropSheet;
+class CStudioApp;
+
+namespace Ui
+{
+ class StudioPreferencesPropSheet;
+}
+
+class CStudioPreferencesPropPage : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit CStudioPreferencesPropPage(QWidget *parent = nullptr);
+
+ virtual bool OnApply() { OnOK(); SetModified(false); return true; }
+ virtual void OnOK() {}
+ virtual void OnCancel() {}
+
+protected:
+ CStudioPreferencesPropSheet* sheet();
+
+ void SetModified(bool modified);
+ void EndDialog(int returnCode);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioPreferencesPropSheet
+
+class CStudioPreferencesPropSheet : public QDialog
+{
+ Q_OBJECT
+ // Construction
+public:
+ explicit CStudioPreferencesPropSheet(int nIDCaption, QWidget *pParentWnd = nullptr,
+ int iSelectPage = 0);
+ explicit CStudioPreferencesPropSheet(const QString &pszCaption, QWidget *pParentWnd = nullptr,
+ int iSelectPage = 0);
+
+protected:
+ QFont m_Font; // Font for text
+
+ // Implementation
+public:
+ virtual ~CStudioPreferencesPropSheet();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CStudioPreferencesPropSheet)
+ virtual void OnInitDialog();
+
+ bool apply();
+ void accept() override;
+ void reject() override;
+
+private:
+ QScopedPointer<Ui::StudioPreferencesPropSheet> m_ui;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STUDIOPREFERENCESPROPSHEET_H__FADC69CE_5F0E_4F7E_A906_ED6052BBECF6__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.ui b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.ui
new file mode 100644
index 00000000..813d6016
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioPreferencesPropSheet.ui
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StudioPreferencesPropSheet</class>
+ <widget class="QDialog" name="StudioPreferencesPropSheet">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>484</width>
+ <height>466</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Studio Preferences</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="m_TabCtrl">
+ <widget class="CStudioAppPrefsPage" name="m_AppPrefsPage">
+ <attribute name="icon">
+ <iconset resource="../../MainFrm.qrc">
+ <normaloff>:/res/prefstab-00.png</normaloff>:/res/prefstab-00.png</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Studio</string>
+ </attribute>
+ </widget>
+ <widget class="CStudioProjectSettingsPage" name="m_ProjectSettingsPage">
+ <attribute name="icon">
+ <iconset resource="../../MainFrm.qrc">
+ <normaloff>:/res/prefstab-01.png</normaloff>:/res/prefstab-01.png</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Presentation Settings</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>CStudioProjectSettingsPage</class>
+ <extends>QWidget</extends>
+ <header>StudioProjectSettingsPage.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>CStudioAppPrefsPage</class>
+ <extends>QWidget</extends>
+ <header>StudioAppPrefsPage.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../../MainFrm.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>StudioPreferencesPropSheet</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>StudioPreferencesPropSheet</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.cpp b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.cpp
new file mode 100644
index 00000000..c2a7e26c
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.cpp
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#include "stdafx.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+#include "ui_StudioProjectSettingsPage.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "StudioProjectSettingsPage.h"
+#include "StudioProjectSettings.h"
+#include "StudioApp.h"
+#include "Doc.h"
+#include "Views.h"
+#include "MainFrm.h"
+#include "CommonConstants.h"
+
+#include "StudioPreferences.h"
+#include "Core.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioProjectSettingsPage property page
+
+//==============================================================================
+/**
+ * Constructor: Initializes the object.
+ */
+//==============================================================================
+CStudioProjectSettingsPage::CStudioProjectSettingsPage(QWidget *parent)
+ : CStudioPreferencesPropPage(parent)
+ , m_AspectRatio(0.0f)
+ , m_ui(new Ui::StudioProjectSettingsPage)
+{
+ m_Font = QFont(CStudioPreferences::GetFontFaceName());
+ m_Font.setPixelSize(CStudioPreferences::fontSize());
+
+ // Create a bold font for the group box text
+ m_BoldFont = m_Font;
+ m_BoldFont.setBold(true);
+
+ OnInitDialog();
+}
+
+//==============================================================================
+/**
+ * Destructor: Releases the object.
+ */
+//==============================================================================
+CStudioProjectSettingsPage::~CStudioProjectSettingsPage()
+{
+}
+
+//==============================================================================
+// Message Map is defined by subclasses, since there is any specific handlers must be
+// defined within the BEGIN_MESSAGE_MAP macros.
+//==============================================================================
+
+/////////////////////////////////////////////////////////////////////////////
+// CStudioProjectSettingsPage message handlers
+
+//==============================================================================
+/**
+ * OnInitDialog: Handle the WM_INITDIALOG message.
+ *
+ * Initialize the dialog by setting the various control values.
+ *
+ * @param None
+ *
+ * @return Returns TRUE always.
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnInitDialog()
+{
+ m_ui->setupUi(this);
+
+ m_ui->m_ClientSizeWidth->setToolTip(::LoadResourceString(IDS_PREFS_PRESENTATIONWIDTH).toQString());
+ m_ui->m_ClientSizeHeight->setToolTip(::LoadResourceString(IDS_PREFS_PRESENTATIONHEIGHT).toQString());
+ m_ui->m_checkConstrainProportions->setToolTip(::LoadResourceString(IDS_PREFS_PRESENTATIONASPECTRATIO).toQString());
+ m_ui->m_Author->setToolTip(::LoadResourceString(IDS_PREFS_AUTHORNAME).toQString());
+ m_ui->m_Company->setToolTip(::LoadResourceString(IDS_PREFS_COMPANYNAME).toQString());
+
+ // Set fonts for child windows.
+ for (auto w : findChildren<QWidget *>())
+ w->setFont(m_Font);
+
+ // Make the group text bold
+ for (auto w : findChildren<QGroupBox *>())
+ w->setFont(m_BoldFont);
+
+ // Set the ranges of the client width and height
+ m_ui->m_ClientSizeWidth->setRange(1, 16384);
+ m_ui->m_ClientSizeHeight->setRange(1, 16384);
+
+ // Load the settings for the controls
+ this->LoadSettings();
+
+ auto valueChanged = static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged);
+ connect(m_ui->m_ClientSizeWidth, valueChanged, this, &CStudioProjectSettingsPage::OnChangeEditPresWidth);
+ connect(m_ui->m_ClientSizeHeight, valueChanged, this, &CStudioProjectSettingsPage::OnChangeEditPresHeight);
+ connect(m_ui->m_checkConstrainProportions, &QCheckBox::clicked, this, &CStudioProjectSettingsPage::OnCheckMaintainRatio);
+ connect(m_ui->m_Author, &QLineEdit::textEdited, this, &CStudioProjectSettingsPage::OnChangeAuthor);
+ connect(m_ui->m_Company, &QLineEdit::textEdited, this, &CStudioProjectSettingsPage::OnChangeCompany);
+}
+
+//==============================================================================
+/**
+ * LoadSettings: Load the settings from the project settings and set the control values.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::LoadSettings()
+{
+ // Get the Client size
+ CStudioProjectSettings *theProjectSettings = g_StudioApp.GetCore()->GetStudioProjectSettings();
+ CPt theClientSize = theProjectSettings->GetPresentationSize();
+
+ // Set client width & height
+ m_ui->m_ClientSizeWidth->setValue(theClientSize.x);
+ m_ui->m_ClientSizeHeight->setValue(theClientSize.y);
+
+ // Save the aspect ratio
+ m_AspectRatio = (double)theClientSize.x / (double)theClientSize.y;
+
+ // Maintain Aspect Ratio checkbox
+ m_ui->m_checkConstrainProportions->setChecked(theProjectSettings->GetMaintainAspect());
+
+ m_ui->m_checkPortraitFormat->setChecked(theProjectSettings->GetRotatePresentation());
+
+ // Author
+ m_ui->m_Author->setText(theProjectSettings->GetAuthor());
+
+ // Company
+ m_ui->m_Company->setText(theProjectSettings->GetCompany());
+}
+
+//==============================================================================
+/**
+ * SaveSettings: Save the settings from the controls to the project settings.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::SaveSettings()
+{
+ CPt theClientSize;
+ CStudioProjectSettings *theProjectSettings = g_StudioApp.GetCore()->GetStudioProjectSettings();
+
+ // Presentation width & height
+ theClientSize.x = m_ui->m_ClientSizeWidth->value();
+ theClientSize.y = m_ui->m_ClientSizeHeight->value();
+ theProjectSettings->SetPresentationSize(theClientSize);
+
+ // Author
+ QString theAuthor = m_ui->m_Author->text();
+ theProjectSettings->SetAuthor(theAuthor);
+
+ // Company
+ QString theCompany = m_ui->m_Company->text();
+ theProjectSettings->SetCompany(theCompany);
+
+ g_StudioApp.GetViews()->RecheckMainframeSizingMode();
+
+ // Maintain Aspect Ratio checkbox
+ theProjectSettings->SetMaintainAspect(m_ui->m_checkConstrainProportions->isChecked());
+
+ theProjectSettings->SetRotatePresentation(m_ui->m_checkPortraitFormat->isChecked());
+}
+
+//==============================================================================
+/**
+ * Generic function when settings are modified.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnSettingsModified()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnApply: Handler for the Apply button
+ *
+ * @param None
+ */
+//==============================================================================
+bool CStudioProjectSettingsPage::OnApply()
+{
+ // Apply was clicked - save settings and disabled the Apply button
+ this->SaveSettings();
+
+ this->SetModified(FALSE);
+
+ return CStudioPreferencesPropPage::OnApply();
+}
+
+//==============================================================================
+/**
+ * OnOK: Handler for the OK button
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnOK()
+{
+ CStudioPreferencesPropPage::OnOK();
+}
+
+//==============================================================================
+/**
+ * OnChangeEditPresWidth: EN_CHANGE handler for the IDC_EDIT_PRESWIDTH field
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeEditPresWidth()
+{
+ this->SetModified(TRUE);
+
+ // Should the aspect ratio be maintained?
+ if (m_ui->m_checkConstrainProportions->isChecked()) {
+ long thePresWidth, thePresHeight;
+
+ thePresWidth = m_ui->m_ClientSizeWidth->value();
+
+ // Change the height
+ thePresHeight = ((double)thePresWidth / m_AspectRatio);
+
+ QSignalBlocker sb(m_ui->m_ClientSizeHeight);
+ m_ui->m_ClientSizeHeight->setValue(thePresHeight);
+ }
+}
+
+//==============================================================================
+/**
+ * OnChangeEditPresHeight: EN_CHANGE handler for the IDC_EDIT_PRESHEIGHT field
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeEditPresHeight()
+{
+ this->SetModified(TRUE);
+
+ // Should the aspect ratio be maintained?
+ if (m_ui->m_checkConstrainProportions->isChecked()) {
+ long thePresWidth, thePresHeight;
+
+ thePresHeight = m_ui->m_ClientSizeHeight->value();
+
+ // Change the width
+ thePresWidth = ((double)thePresHeight * m_AspectRatio);
+
+ QSignalBlocker sb(m_ui->m_ClientSizeWidth);
+ m_ui->m_ClientSizeWidth->setValue(thePresWidth);
+ }
+}
+
+//==============================================================================
+/**
+ * OnCheckMaintainRatio: The aspect ratio checkbox has changed.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnCheckMaintainRatio()
+{
+ this->SetModified(TRUE);
+
+ long thePresWidth, thePresHeight;
+
+ // Get the width and height
+ thePresWidth = m_ui->m_ClientSizeWidth->value();
+ thePresHeight = m_ui->m_ClientSizeHeight->value();
+
+ // Save the Aspect Ratio
+ m_AspectRatio = (double)thePresWidth / (double)thePresHeight;
+}
+
+//==============================================================================
+/**
+ * OnChangeAuthor: EN_CHANGE handler for the IDC_AUTHOR field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeAuthor()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeCompany: EN_CHANGE handler for the IDC_COMPANY field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeCompany()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeSet1: EN_CHANGE handler for the IDC_SET1 field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeSet1()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeSet2: EN_CHANGE handler for the IDC_SET2 field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeSet2()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeSet3: EN_CHANGE handler for the IDC_SET3 field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeSet3()
+{
+ this->SetModified(TRUE);
+}
+
+//==============================================================================
+/**
+ * OnChangeSet5: EN_CHANGE handler for the IDC_SET5 field.
+ *
+ * @param None
+ */
+//==============================================================================
+void CStudioProjectSettingsPage::OnChangeSet5()
+{
+ this->SetModified(TRUE);
+}
diff --git a/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.h b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.h
new file mode 100644
index 00000000..64465e98
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if !defined(AFX_STUDIOPROJECTSETTINGSPAGE_H__E3317E44_810D_4478_A7DD_CF8570B7C17C__INCLUDED_)
+#define AFX_STUDIOPROJECTSETTINGSPAGE_H__E3317E44_810D_4478_A7DD_CF8570B7C17C__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+//#include "ParameterColor.h"
+
+#include "StudioPreferencesPropSheet.h"
+
+#ifdef _USENEWCOLORPICKER_
+#include "StudioColorPicker.h"
+#endif
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CStudioApp;
+
+namespace Ui {
+ class StudioProjectSettingsPage;
+}
+
+//==============================================================================
+// Base class for the settings for different modes
+//==============================================================================
+class CStudioProjectSettingsPage : public CStudioPreferencesPropPage
+{
+ Q_OBJECT
+ // Construction
+public:
+ explicit CStudioProjectSettingsPage(QWidget *parent = nullptr);
+ ~CStudioProjectSettingsPage();
+
+ // Overrides
+ // ClassWizard generate virtual function overrides
+public:
+ bool OnApply() override;
+ void OnOK() override;
+
+ // Implementation
+protected:
+ double m_AspectRatio; ///< Stores the presentation width divided by the presentation height
+ QFont m_Font; ///< Font for text
+ QFont m_BoldFont; ///< Bold font for drawing the group boxes
+
+ void LoadSettings();
+ void SaveSettings();
+
+protected:
+ // Generated message map functions
+ virtual void OnInitDialog();
+ void OnChangeEditPresWidth();
+ void OnChangeEditPresHeight();
+ void OnCheckMaintainRatio();
+ void OnChangeAuthor();
+ void OnChangeCompany();
+ void OnSettingsModified();
+ void OnSelChangePreviewApp();
+ void OnCustomPreviewMore();
+ void OnChangeSet1();
+ void OnChangeSet2();
+ void OnChangeSet3();
+ void OnChangeSet5();
+
+ QScopedPointer<Ui::StudioProjectSettingsPage> m_ui;
+};
+
+#endif // !defined(AFX_STUDIOPROJECTSETTINGSPAGE_H__E3317E44_810D_4478_A7DD_CF8570B7C17C__INCLUDED_)
diff --git a/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.ui b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.ui
new file mode 100644
index 00000000..07a4bf65
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/StudioProjectSettingsPage.ui
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StudioProjectSettingsPage</class>
+ <widget class="QWidget" name="StudioProjectSettingsPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>259</width>
+ <height>269</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Presentation</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Width x Height</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_ClientSizeWidth</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QSpinBox" name="m_ClientSizeWidth">
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_ClientSizeHeight">
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QCheckBox" name="m_checkConstrainProportions">
+ <property name="text">
+ <string>Constrain Proportions</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QCheckBox" name="m_checkPortraitFormat">
+ <property name="text">
+ <string>Portrait Format</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Project Info</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Author</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_Author</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="m_Author"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Company</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_Company</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="m_Company"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>m_ClientSizeWidth</tabstop>
+ <tabstop>m_ClientSizeHeight</tabstop>
+ <tabstop>m_checkConstrainProportions</tabstop>
+ <tabstop>m_checkPortraitFormat</tabstop>
+ <tabstop>m_Author</tabstop>
+ <tabstop>m_Company</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/Authoring/Studio/_Win/UI/TimeEditDlg.cpp b/src/Authoring/Studio/_Win/UI/TimeEditDlg.cpp
new file mode 100644
index 00000000..619d7651
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/TimeEditDlg.cpp
@@ -0,0 +1,524 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//=============================================================================
+// Prefix
+//=============================================================================
+
+#include "ui_timeeditdlg.h"
+
+#include "stdafx.h"
+#include "StudioPreferences.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+
+#include "TimeEditDlg.h"
+#include "IDoc.h"
+#include "Bindings/ITimelineKeyframesManager.h"
+
+#include <QFont>
+#include <QPalette>
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CTimeEditDlg::CTimeEditDlg(QWidget *pParent)
+ : QDialog(pParent)
+ , m_MaxTime(0)
+ , m_MaxTimeDisplay(0)
+ , m_MinTimeDisplay(0)
+ , m_InitialTime1(0)
+ , m_InitialTime2(0)
+ , m_MinOffset(0)
+ , m_SecOffset(0)
+ , m_MSecOffset(0)
+ , m_MinMinorOffset(0)
+ , m_SecMinorOffset(0)
+ , m_MSecMinorOffset(0)
+ , m_CursorPosition(0)
+ , m_TimeFormat(0)
+ , m_PreviousFormat(0)
+ , m_TimeInMins(0.0f)
+ , m_NumberOfDigitsDrop(0)
+ , m_ColonPosition1(0)
+ , m_ColonPosition2(0)
+ , m_ObjectAssociation(0)
+ , m_OffsetFromInitialTime(0)
+ , m_Time1(0)
+ , m_Time2(0)
+ , m_Doc(nullptr)
+ , m_KeyframesManager(nullptr)
+ , m_Callback(nullptr)
+ , m_ui(new Ui::TimeEditDlg)
+{
+ m_ui->setupUi(this);
+ setAutoFillBackground(true);
+
+ connect(m_ui->startTimeEdit, &QDateTimeEdit::timeChanged, this, &CTimeEditDlg::OnEnChangeTimeEdit1);
+ connect(m_ui->endTimeEdit, &QDateTimeEdit::timeChanged, this, &CTimeEditDlg::OnEnChangeTimeEdit2);
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CTimeEditDlg::~CTimeEditDlg()
+{
+ delete m_ui;
+}
+
+void CTimeEditDlg::SetKeyframesManager(ITimelineKeyframesManager *inKeyframesManager)
+{
+ m_KeyframesManager = inKeyframesManager;
+}
+
+//=============================================================================
+/**
+ * InitData: Initializes the Time Edit Dialog Box.
+ * @param inTime1 is the intial start time, which will be shown when the time edit
+ * dialog box pops up
+ * @param inTime2 is the intial end time, which will be shown when the time edit
+ * dialog box pops up
+ * @param inDoc this can be nullptr where its not applicable
+ * @param inObjectAssociation is the identifier for that identifies the object
+ * associated with the time edit dialog
+ *(e.g.
+ * playhead, keyframe, timebar)
+ */
+void CTimeEditDlg::ShowDialog(long inTime1, long inTime2, IDoc *inDoc, long inObjectAssociation,
+ ITimeChangeCallback *inCallback /*= nullptr*/)
+{
+ m_InitialTime1 = inTime1;
+ m_InitialTime2 = inTime2;
+ m_ObjectAssociation = inObjectAssociation;
+ m_Doc = inDoc;
+ m_Callback = inCallback;
+
+ m_OffsetFromInitialTime = 0;
+ m_NumberOfDigitsDrop = 0;
+ m_MinTimeDisplay = 0;
+ // if it is a Timebar, this will be adjusted, else this should be initialized to some value at
+ // least,
+ // for OverflowHandling to work correctly
+ m_MaxTimeDisplay = LONG_MAX;
+
+ // 9999:59:999 converted to milliseconds
+ m_MaxTime =
+ TimeConversion(9999, CONVERT_MIN_TO_MSEC) + TimeConversion(59, CONVERT_SEC_TO_MSEC) + 999;
+
+ m_TimeDisplay2 = FormatTime(m_InitialTime2);
+ // In cases Keyframes, where its only one set of time, do m_InitialTime1 later so that
+ // m_Min, etc. values are initializd to the valid values.
+ m_TimeDisplay1 = FormatTime(m_InitialTime1);
+
+ // Present the dialog
+ exec();
+}
+
+//==============================================================================
+/**
+ * FormatTime: Called to break up a given time in milliseconds into min:sec:msec
+ * store it in a string. The string is formatted in 99:59:999 form.
+ * @param inTime stores the time in milliseconds, which will be converted
+ * and stored in ioTimeString in 99:59:999 format.
+ * @return ioTimeString stores the result of the FormatTime method.
+ */
+QString CTimeEditDlg::FormatTime(long inTime)
+{
+ long theTime = inTime;
+ long hour = 0;
+ long min = 0;
+ long sec = 0;
+ long msec = 0;
+ // Translates the m_InitialTime1 (in milliseconds) into Minutes, Seconds and Milliseconds
+ if (inTime != 0) {
+ min = TimeConversion(theTime, CONVERT_MSEC_TO_MIN);
+ hour = min / 60;
+ theTime = theTime - TimeConversion(min, CONVERT_MIN_TO_MSEC);
+ min -= hour * 60;
+ sec = TimeConversion(theTime, CONVERT_MSEC_TO_SEC);
+ theTime = theTime - TimeConversion(sec, CONVERT_SEC_TO_MSEC);
+ msec = theTime;
+ }
+ return QString::asprintf("%02d:%02d:%02d:%03d", hour, min, sec, msec);
+}
+
+//==============================================================================
+/**
+ * OnInitDialog: Handle the WM_INITDIALOG message.
+ * @param None
+ * @return Returns TRUE always.
+ */
+void CTimeEditDlg::showEvent(QShowEvent *ev)
+{
+ OnInitDialog();
+ QDialog::showEvent(ev);
+}
+
+
+void CTimeEditDlg::OnInitDialog()
+{
+ // Hide the window items associated with end time.
+ if (m_ObjectAssociation != TIMEBAR)
+ HideUnnecessaryFields();
+ m_ui->startTimeEdit->setTime(QTime::fromString(m_TimeDisplay1, "hh:mm:ss:zzz"));
+ Q3DStudio::CString theTitle;
+ // Display the window captions for the correct object type
+ switch (m_ObjectAssociation) {
+ case PLAYHEAD:
+ theTitle = ::LoadResourceString(IDS_TIME_EDIT_DLG_GO_TO_TIME);
+ setWindowTitle(theTitle.toQString());
+ break;
+ case ASSETKEYFRAME:
+ theTitle = ::LoadResourceString(IDS_TIME_EDIT_DLG_SET_KEYFRAME_TIME);
+ setWindowTitle(theTitle.toQString());
+ break;
+ case TIMEBAR:
+ theTitle = ::LoadResourceString(IDS_TIME_EDIT_DLG_SET_TIMEBAR_TIME);
+ // m_TimeEditBoxNumber is a flag that switches the output between
+ // the first and second text box.
+ // When m_TimeEditBoxNumber = EDITBOX1, we are writing to the first text box
+ // (the one that appears on top).
+ // When it is EDITBOX2, the SetTimeEdit will write to the second text box.
+ setWindowTitle(theTitle.toQString());
+ m_ui->endTimeEdit->setTime(QTime::fromString(m_TimeDisplay2, "hh:mm:ss:zzz"));
+ break;
+ }
+}
+//==============================================================================
+/**
+ * accept: Upon clicking ok, the dialog will be exited
+ */
+void CTimeEditDlg::accept()
+{
+ // Only commit here, cos dup keyframes will be deleted.
+ if (m_ObjectAssociation == ASSETKEYFRAME && m_Doc && m_KeyframesManager)
+ m_KeyframesManager->CommitChangedKeyframes();
+ else if (m_ObjectAssociation == TIMEBAR)
+ m_Callback->Commit();
+
+ QDialog::accept();
+}
+
+//==============================================================================
+/**
+ * reject: Upon clicking Cancel, revert to the initial time.
+ */
+void CTimeEditDlg::reject()
+{
+ // Only commit here, cos dup keyframes will be deleted.
+ if (m_ObjectAssociation == ASSETKEYFRAME && m_Doc && m_KeyframesManager)
+ m_KeyframesManager->RollbackChangedKeyframes();
+ else if (m_ObjectAssociation == TIMEBAR)
+ m_Callback->Rollback();
+ QDialog::reject();
+}
+
+//==============================================================================
+/**
+ * CountDigits: Counts the number of digits in a given number
+ * @param inNumber is the number that is used to count the number of digits
+ * @return the number of digits
+ */
+long CTimeEditDlg::CountDigits(long inNumber)
+{
+ long theNumberOfDigits = 0;
+ for (long theNumber = inNumber; theNumber >= 1; theNumber = theNumber / 10)
+ theNumberOfDigits++;
+ return theNumberOfDigits;
+}
+
+
+//==============================================================================
+/**
+ * TimeOverflowUnderflow: Checks if inMin, inSec or inMSec has exceeded the overflow/
+ * underflow limit. It will correct the time if it
+ *overflow/
+ * underflow.
+ * @param inMin stores the min segment of the time
+ * @param inSec stores the sec segment of the time
+ * @param inMSec stores the msec segment of the time
+ * @param inTimeLimit stores the time limit before the overflow/underflow occurs
+ * @param inOverflowOrUnderflow is set to true if we are checking for overflow else
+ * we are checking for underflow
+ * @returns true if overflow/underflow occurs, otherwise returns false.
+ */
+bool CTimeEditDlg::TimeOverflowUnderflow(long *inMin, long *inSec, long *inMSec, long inTimeLimit,
+ bool inOverflowOrUnderflow)
+{
+ // The codes below translates inTimeLimit into theLimitMin:theLimitSec:theLimitMsec
+ bool theLimitExceeds = false;
+ long theLimitMin;
+ long theLimitSec;
+ long theLimitMsec;
+ TimeConversion(inTimeLimit, &theLimitMin, &theLimitSec, &theLimitMsec,
+ CONVERT_MSEC_TO_MIN_SEC_MSEC);
+ // Handle time overflow/underflow
+ if ((*inMin > theLimitMin && inOverflowOrUnderflow)
+ || (*inMin < theLimitMin && inOverflowOrUnderflow == false)) // Minutes exceeds limit
+ {
+ // Set all time segments to the limit
+ *inMin = theLimitMin;
+ *inSec = theLimitSec;
+ *inMSec = theLimitMsec;
+ theLimitExceeds = true;
+ } else if (*inMin == theLimitMin) {
+ if ((*inSec > theLimitSec && inOverflowOrUnderflow)
+ || (*inSec < theLimitSec && inOverflowOrUnderflow == false)) // Seconds exceeds limit
+ {
+ // Set the Sec and Msec segments to the limit
+ *inSec = theLimitSec;
+ *inMSec = theLimitMsec;
+ theLimitExceeds = true;
+ } else if (*inSec == theLimitSec) {
+ if ((*inMSec > theLimitMsec && inOverflowOrUnderflow)
+ || (*inMSec < theLimitMsec
+ && inOverflowOrUnderflow == false)) // Milliseconds exceeds limit
+ {
+ // Msec segments to the limit
+ *inMSec = theLimitMsec;
+ theLimitExceeds = true;
+ }
+ }
+ }
+ return theLimitExceeds;
+}
+
+//==============================================================================
+/**
+ * TimeConversion: Converts inTime to the format specified by inFlags.
+ * For example:
+ * inTime = 5 sec inFlags = CONVERT_SEC_TO_MSEC
+ * The method will convert 5 sec into 5000 msec and
+ *returns
+ * the result.
+ * @param inTime stores the time to be converted.
+ * inOperationCode determines the type of time conversion to be done on the
+ * inTime.
+ * @return theResult stores the result of the time conversion.
+ */
+long CTimeEditDlg::TimeConversion(long inTime, long inOperationCode)
+{
+ long theResult = 0;
+ switch (inOperationCode) {
+ case CONVERT_MIN_TO_MSEC:
+ theResult = inTime * 60 * 1000;
+ break;
+ case CONVERT_SEC_TO_MSEC:
+ theResult = inTime * 1000;
+ break;
+ case CONVERT_MSEC_TO_MIN:
+ theResult = inTime / (60 * 1000);
+ break;
+ case CONVERT_MSEC_TO_SEC:
+ theResult = inTime / 1000;
+ break;
+ }
+ return theResult;
+}
+
+//==============================================================================
+/**
+ * TimeConversion: Takes in the time in mins:secs:msec and convert it to
+ * the corresponding time in msec.
+ * @param inMin stores the minutes to be converted.
+ * inSec stores the seconds to be converted.
+ * inMsec stores the milliseconds to be converted.
+ * inOperationCode determines the type of time conversion to be done on the
+ * inMin, inSec and inMsec.
+ * @return theResult stores the result of the time conversion.
+ */
+long CTimeEditDlg::TimeConversion(long inMin, long inSec, long inMsec, long inOperationCode)
+{
+ long theResult = 0;
+ switch (inOperationCode) {
+ case CONVERT_TIME_TO_MSEC:
+ theResult = TimeConversion(inMin, CONVERT_MIN_TO_MSEC)
+ + TimeConversion(inSec, CONVERT_SEC_TO_MSEC) + inMsec;
+ break;
+ }
+ return theResult;
+}
+
+//==============================================================================
+/**
+ * TimeConversion: Takes in the time in milliseconds and converts them
+ * to min : sec : msec.
+ * @param inTotalTime stores the total time in msec.
+ * ioMin stores the mins result of the time conversion
+ * ioSec stores the secs result of the time conversion
+ * ioMsec stores the msecs result of the time conversion
+ * inOperationCode determines the type of time conversion to be done on the
+ * inTotalTime.
+ * @return NONE
+ */
+void CTimeEditDlg::TimeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec,
+ long inOperationCode)
+{
+ switch (inOperationCode) {
+ case CONVERT_MSEC_TO_MIN_SEC_MSEC:
+ *ioMin = TimeConversion(inTotalTime, CONVERT_MSEC_TO_MIN);
+ *ioSec = inTotalTime - TimeConversion(*ioMin, CONVERT_MIN_TO_MSEC);
+ *ioSec = TimeConversion(*ioSec, CONVERT_MSEC_TO_SEC);
+ *ioMsec = inTotalTime - TimeConversion(*ioMin, CONVERT_MIN_TO_MSEC)
+ - TimeConversion(*ioSec, CONVERT_SEC_TO_MSEC);
+ break;
+ }
+}
+//==============================================================================
+/**
+ * UpdateObjectTime: It updates the playhead, keyframe or timebar time according
+ * to the time displayed in the time edit dialogue.
+ * @param inTime is the time that will be updated.
+ * @param startTime if true, updates the start time on the object, otherwise the end time
+ * @return NONE
+ */
+void CTimeEditDlg::UpdateObjectTime(long inTime, bool startTime)
+{
+ long theDiff = 0;
+ switch (m_ObjectAssociation) {
+ case PLAYHEAD: // Update the playhead time
+ if (m_Doc) {
+ m_Doc->NotifyTimeChanged(inTime);
+ m_Doc->NotifyTimeChanged(inTime);
+ }
+ break;
+ case ASSETKEYFRAME: // Update the keyframe time
+ if (m_Doc) {
+ theDiff = inTime - m_OffsetFromInitialTime - m_InitialTime1;
+ m_OffsetFromInitialTime = m_OffsetFromInitialTime + theDiff;
+ if (theDiff != 0 && m_KeyframesManager) {
+ m_KeyframesManager->OffsetSelectedKeyframes(theDiff);
+ }
+ }
+ break;
+ case TIMEBAR: // Update the timebar start/end time
+ if (m_Callback) {
+ if (startTime) // Update Start Time
+ m_Callback->ChangeStartTime(inTime);
+ else // Update End Time
+ m_Callback->ChangeEndTime(inTime);
+ }
+ break;
+ }
+}
+
+//==============================================================================
+/** The codes methods below are provides platform independent wrappers for
+ * User Interface.
+ */
+//==============================================================================
+
+/**
+ * OnEnChangeTimeEdit: Event triggered when the text in the 1st time edit box has
+ * been changed.
+ * @param NONE
+ * @return NONE
+ */
+void CTimeEditDlg::OnEnChangeTimeEdit1()
+{
+ // Making sure that the start time is not greater than the end time, when
+ // the user modifies the start time of the timebar
+ if (m_ObjectAssociation == TIMEBAR) {
+ m_MaxTimeDisplay = m_InitialTime2; // the initial end time
+ }
+ m_MinTimeDisplay = 0;
+
+ const auto time = m_ui->startTimeEdit->time();
+ long min = time.minute() + time.hour() * 60;
+ long sec = time.second();
+ long msec = time.msec();
+ TimeOverflowUnderflow(&min, &sec, &sec, m_MaxTimeDisplay, true);
+ TimeOverflowUnderflow(&min, &sec, &msec, m_MinTimeDisplay, false);
+ if (min != time.minute() || sec != time.second() ||
+ msec != time.msec()) {
+ m_ui->startTimeEdit->setTime(QTime(0, min, sec, msec));
+ }
+ m_ui->startTimeMin->setText(QString::number(min));
+ m_ui->startTimeSec->setText(QString::number(sec));
+ m_ui->startTimeMsec->setText(QString::number(msec));
+
+ long theGoToTime = TimeConversion(min, CONVERT_MIN_TO_MSEC)
+ + TimeConversion(sec, CONVERT_SEC_TO_MSEC) + msec;
+ // Go to the time specified in the time edit display
+ UpdateObjectTime(theGoToTime, true /*start time*/);
+}
+
+//==============================================================================
+/**
+ * OnEnChangeTimeEdit2: Event triggered when the text in the 2nd time edit box has
+ * been changed.
+ * @param NONE
+ * @return NONE
+ */
+void CTimeEditDlg::OnEnChangeTimeEdit2()
+{
+ // Let the end time of the time bar go as far as possible
+ if (m_ObjectAssociation == TIMEBAR) {
+ m_MaxTimeDisplay = m_MaxTime;
+ m_MinTimeDisplay = m_InitialTime1; // the initial start time
+ }
+ const auto time = m_ui->endTimeEdit->time();
+ long min = time.minute() + time.hour() * 60;
+ long sec = time.second();
+ long msec = time.msec();
+ TimeOverflowUnderflow(&min, &sec, &sec, m_MaxTimeDisplay, true);
+ TimeOverflowUnderflow(&min, &sec, &msec, m_MinTimeDisplay, false);
+ if (min != time.minute() || sec != time.second() ||
+ msec != time.msec()) {
+ m_ui->endTimeEdit->setTime(QTime(0, min, sec, msec));
+ }
+ m_ui->endTimeMin->setText(QString::number(min));
+ m_ui->endTimeSec->setText(QString::number(sec));
+ m_ui->endTimeMsec->setText(QString::number(msec));
+
+ long theGoToTime = TimeConversion(min, CONVERT_MIN_TO_MSEC)
+ + TimeConversion(sec, CONVERT_SEC_TO_MSEC) + msec;
+ // Go to the time specified in the time edit display
+ UpdateObjectTime(theGoToTime, false /*end time*/);
+}
+
+
+//==============================================================================
+/**
+ * HideUnnecessaryFields: Hides unused dialog items
+ * @param NONE
+ * @return NONE
+ */
+void CTimeEditDlg::HideUnnecessaryFields()
+{
+ m_ui->endTimeGroup->hide();
+ m_ui->startTimeLabel->hide();
+}
+
diff --git a/src/Authoring/Studio/_Win/UI/TimeEditDlg.h b/src/Authoring/Studio/_Win/UI/TimeEditDlg.h
new file mode 100644
index 00000000..7103dd3e
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/TimeEditDlg.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+
+#ifndef INCLUDED_TIME_EDIT_DIALOG_H
+#define INCLUDED_TIME_EDIT_DIALOG_H 1
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Dialogs.h"
+#include <QDialog>
+#include <QDateTimeEdit>
+
+//==============================================================================
+// Forwards
+//==============================================================================
+class CTimebarControl;
+class IDoc;
+class ITimelineKeyframesManager;
+
+//==============================================================================
+// Defines
+//==============================================================================
+
+enum ECursorPosition { MIN_POSITION, SEC_POSITION, MSEC_POSITION };
+
+enum ETimeFormat { MSEC, SEC_MSEC, MIN_SEC_MSEC };
+
+enum ETimeConversionOperation {
+ CONVERT_MIN_TO_MSEC,
+ CONVERT_SEC_TO_MSEC,
+ CONVERT_MSEC_TO_MIN,
+ CONVERT_MSEC_TO_SEC,
+ CONVERT_TIME_TO_MSEC,
+ CONVERT_MSEC_TO_MIN_SEC_MSEC
+};
+
+enum ETimeEditBoxNumber { EDITBOX1, EDITBOX2 };
+
+enum EObjectAssociation {
+ PLAYHEAD,
+ ASSETKEYFRAME,
+ TIMEBAR,
+};
+
+class ITimeChangeCallback
+{
+public:
+ virtual ~ITimeChangeCallback() {}
+ virtual void ChangeStartTime(long) = 0;
+ virtual void ChangeEndTime(long) = 0;
+ virtual void Commit() = 0;
+ virtual void Rollback() = 0;
+};
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class TimeEditDlg;
+}
+QT_END_NAMESPACE
+
+// This class workaround the problem of QDateTimeEdit overwriting the selected section
+// See https://bugreports.qt.io/browse/QTBUG-34759
+class CDateTimeEdit : public QDateTimeEdit
+{
+ Q_OBJECT
+public:
+ using QDateTimeEdit::QDateTimeEdit;
+
+protected:
+ void focusInEvent(QFocusEvent *event) {
+ QDateTimeEdit::focusInEvent(event);
+ setSelectedSection(QDateTimeEdit::SecondSection);
+ }
+};
+
+//==============================================================================
+/**
+ * CTimeEditDlg: It is a dialog box that allows user to specify the time that
+ * he/she wishes to go to. This dialog box can be activated by
+ * pressing Control G or clicking on the time edit box in the
+ * timeline.
+ */
+//==============================================================================
+class CTimeEditDlg : public QDialog
+{
+ Q_OBJECT
+
+public:
+ CTimeEditDlg(QWidget *pParent = nullptr); // standard constructor
+ virtual ~CTimeEditDlg();
+ void SetKeyframesManager(ITimelineKeyframesManager *inKeyframeManager);
+ void ShowDialog(long inTime1, long inTime2, IDoc *inDoc, long inObjectAssociation,
+ ITimeChangeCallback *inCallback = nullptr);
+
+public Q_SLOTS:
+ void accept() override;
+ void reject() override;
+
+protected:
+ void showEvent(QShowEvent *) override;
+
+ // Generated message map functions
+ void OnInitDialog();
+ void OnEnChangeTimeEdit1();
+ void OnEnChangeTimeEdit2();
+
+ QString FormatTime(long inTime);
+ long CountDigits(long inNumber);
+ long TimeConversion(long inTime, long inOperationCode);
+ long TimeConversion(long inMin, long inSec, long inMsec, long inOperationCode);
+ void TimeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec,
+ long inOperationCode);
+ void UpdateObjectTime(long inTime, bool startTime);
+ bool TimeOverflow(long *inMin, long *inSec, long *inMSec, long inTimeLimit);
+ bool TimeOverflowUnderflow(long *inMin, long *inSec, long *inMSec, long inTimeLimit,
+ bool inOverflowOrUnderflow);
+ void HideUnnecessaryFields();
+
+protected:
+ QString m_MinString;
+ QString m_SecString;
+ QString m_MSecString;
+ QString m_TimeDisplay1;
+ QString m_TimeDisplay2;
+ long m_MaxTime;
+ long m_MaxTimeDisplay;
+ long m_MinTimeDisplay;
+ long m_InitialTime1;
+ long m_InitialTime2;
+ long m_MinOffset;
+ long m_SecOffset;
+ long m_MSecOffset;
+ long m_MinMinorOffset;
+ long m_SecMinorOffset;
+ long m_MSecMinorOffset;
+ long m_CursorPosition;
+ long m_TimeFormat;
+ long m_PreviousFormat;
+ double m_TimeInMins;
+ long m_NumberOfDigitsDrop;
+ long m_ColonPosition1;
+ long m_ColonPosition2;
+ long m_ObjectAssociation;
+ long m_OffsetFromInitialTime;
+ QString m_DTime;
+ QString m_DTimeDisplay;
+ long m_Time1;
+ long m_Time2;
+ IDoc *m_Doc;
+ ITimelineKeyframesManager *m_KeyframesManager;
+
+ ITimeChangeCallback *m_Callback;
+ Ui::TimeEditDlg *m_ui;
+};
+#endif // INCLUDED_TIME_EDIT_DIALOG_H
diff --git a/src/Authoring/Studio/_Win/UI/timeeditdlg.ui b/src/Authoring/Studio/_Win/UI/timeeditdlg.ui
new file mode 100644
index 00000000..f46cb1a2
--- /dev/null
+++ b/src/Authoring/Studio/_Win/UI/timeeditdlg.ui
@@ -0,0 +1,364 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TimeEditDlg</class>
+ <widget class="QDialog" name="TimeEditDlg">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>371</width>
+ <height>274</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Go To Time</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QWidget" name="startTimeGroup" native="true">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="CDateTimeEdit" name="startTimeEdit">
+ <property name="currentSection">
+ <enum>QDateTimeEdit::SecondSection</enum>
+ </property>
+ <property name="displayFormat">
+ <string>hh:mm:ss:zzz</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="startTimeMin">
+ <property name="text">
+ <string>00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>sec</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="startTimeSec">
+ <property name="text">
+ <string>00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QLabel" name="startTimeMsec">
+ <property name="text">
+ <string>000</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="startTimeLabel">
+ <property name="text">
+ <string>Start Time</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="endTimeGroup" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="endTimeLabel">
+ <property name="text">
+ <string>End Time</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="CDateTimeEdit" name="endTimeEdit">
+ <property name="currentSection">
+ <enum>QDateTimeEdit::SecondSection</enum>
+ </property>
+ <property name="displayFormat">
+ <string>hh:mm:ss:zzz</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="endTimeMin">
+ <property name="text">
+ <string>00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>sec</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="endTimeSec">
+ <property name="text">
+ <string>00</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>min</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QLabel" name="endTimeMsec">
+ <property name="text">
+ <string>000</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>CDateTimeEdit</class>
+ <extends>QTimeEdit</extends>
+ <header>TimeEditDlg.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>TimeEditDlg</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>TimeEditDlg</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/Authoring/Studio/_Win/Utils/MouseCursor.cpp b/src/Authoring/Studio/_Win/Utils/MouseCursor.cpp
new file mode 100644
index 00000000..9cd7328d
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Utils/MouseCursor.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// WIN32 VERSION
+
+//=============================================================================
+// Prefix
+//=============================================================================
+#include "stdafx.h"
+
+//=============================================================================
+// Includes
+//=============================================================================
+#include "MouseCursor.h"
+#include <QPixmap>
+#include <QDebug>
+
+//#ifdef WIN32
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_ARROW = 0; // IDC_ARROW
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_WAIT = 1; // IDC_WAIT
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_RESIZE_LEFTRIGHT = 2; // IDC_SIZEWE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_RESIZE_UPDOWN = 3; // IDC_SIZENS
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_GROUP_MOVE = 4; // IDC_GROUP_MOVE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_GROUP_ROTATE = 5; // IDC_GROUP_ROTATE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_GROUP_SCALE = 6; // IDC_GROUP_SCALE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_ITEM_MOVE = 7; // IDC_ITEM_MOVE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_ITEM_ROTATE = 8; // IDC_ITEM_ROTATE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_ITEM_SCALE = 9; // IDC_ITEM_SCALE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_EDIT_CAMERA_PAN =
+ 10; // IDC_EDIT_CAMERA_PAN
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_EDIT_CAMERA_ROTATE =
+ 11; // IDC_EDIT_CAMERA_ROTATE
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_EDIT_CAMERA_ZOOM =
+ 12; // IDC_EDIT_CAMERA_ZOOM
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_BLANK = 13; // blank cursor
+const CMouseCursor::TUICMouseCursor CMouseCursor::CURSOR_IBEAM = 14;
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CMouseCursor::CMouseCursor()
+ : m_IsThemeCursor(false)
+ , m_ThemeCursor(-1)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CMouseCursor::~CMouseCursor()
+{
+ Destroy();
+}
+
+//=============================================================================
+/**
+ * @return A platform-specific handle to the cursor, or nullptr if no cursor has been loaded
+ */
+QCursor CMouseCursor::GetHandle()
+{
+ return m_Handle;
+}
+
+//=============================================================================
+/**
+ * If a cursor has been loaded, this function will change the current cursor
+ * to the newly loaded one.
+ */
+void CMouseCursor::Show()
+{
+ qWarning() << Q_FUNC_INFO << "QCursor doesn't work this way - set a breakpoint and debug caller";
+}
+
+//=============================================================================
+/**
+* Sets the cursor position
+* @param inXPos x position of the cursor (in pixels)
+* @param inYPos y position of the cursor (in pixels)
+*/
+void CMouseCursor::SetCursorPos(long inXPos, long inYPos)
+{
+ QCursor::setPos(QPoint(inXPos, inYPos));
+}
+
+//=============================================================================
+/**
+ * Releases the cursor if one has been loaded. Called by the destructor.
+ * WINDOWS IMPLEMENTATION
+ */
+void CMouseCursor::Destroy()
+{
+}
+
+//=============================================================================
+/**
+ * Loads the specified cursor resource. All cursors are expected to be
+ * resources that are loaded from ".cur" files. Some standard cursors are
+ * defined by the system and can be found in the MSDN (IDC_ARROW for example).
+ * WINDOWS IMPLEMENTATION
+ *
+ * @param inCursor ID of the cursor to be loaded
+ * @return true if the cursor was successfully loaded, otherwise false
+ */
+bool CMouseCursor::Load(TUICMouseCursor inCursor)
+{
+ // Convert from our cursors to Windows specific cursors
+ switch (inCursor) {
+ case CURSOR_ARROW:
+ m_Handle = Qt::ArrowCursor;
+ break;
+
+ case CURSOR_WAIT:
+ m_Handle = Qt::WaitCursor;
+ break;
+
+ case CURSOR_RESIZE_LEFTRIGHT:
+ m_Handle = Qt::SizeHorCursor;
+ break;
+
+ case CURSOR_RESIZE_UPDOWN:
+ m_Handle = Qt::SizeVerCursor;
+ break;
+
+ case CURSOR_GROUP_MOVE:
+ m_Handle = QCursor(QPixmap(":/cursors/group_move.png"), 0, 0);
+ break;
+
+ case CURSOR_GROUP_ROTATE:
+ m_Handle = QCursor(QPixmap(":/cursors/group_rotate.png"), 0, 0);
+ break;
+
+ case CURSOR_GROUP_SCALE:
+ m_Handle = QCursor(QPixmap(":/cursors/group_scale.png"), 0, 0);
+ break;
+
+ case CURSOR_ITEM_MOVE:
+ m_Handle = QCursor(QPixmap(":/cursors/item_move.png"), 0, 0);
+ break;
+
+ case CURSOR_ITEM_ROTATE:
+ m_Handle = QCursor(QPixmap(":/cursors/item_rotate.png"), 0, 0);
+ break;
+
+ case CURSOR_ITEM_SCALE:
+ m_Handle = QCursor(QPixmap(":/cursors/item_scale.png"), 0, 0);
+ break;
+
+ case CURSOR_EDIT_CAMERA_PAN:
+ m_Handle = QCursor(QPixmap(":/cursors/edit_camera_pan.png"), 10, 10);
+ break;
+
+ case CURSOR_EDIT_CAMERA_ROTATE:
+ m_Handle = QCursor(QPixmap(":/cursors/edit_camera_rot.png"), 8, 10);
+ break;
+
+ case CURSOR_EDIT_CAMERA_ZOOM:
+ m_Handle = QCursor(QPixmap(":/cursors/edit_camera_zoom.png"), 8, 8);
+ break;
+
+ case CURSOR_BLANK:
+ m_Handle = Qt::BlankCursor;
+ break;
+ case CURSOR_IBEAM:
+ m_Handle = Qt::IBeamCursor;
+ break;
+ default:
+ m_Handle = QCursor();
+ break;
+ }
+ return true;
+}
diff --git a/src/Authoring/Studio/_Win/Utils/WinUtils.cpp b/src/Authoring/Studio/_Win/Utils/WinUtils.cpp
new file mode 100644
index 00000000..e159fa12
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Utils/WinUtils.cpp
@@ -0,0 +1,600 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "Strings.h"
+
+#include "WinUtils.h"
+
+//==============================================================================
+/**
+ * DrawBitmap
+ *
+ * Draw a bitmap given a device context, position and HBITMAP handle.
+ *
+ * @param inDC Device context for drawing
+ * @param inPos CPoint position for drawing
+ * @param inBitmap Handle of the bitmap to draw
+ */
+//==============================================================================
+void CWinUtils::DrawBitmap(CDC *inDC, CPoint inPos, HBITMAP inBitmap)
+{
+ CDC theMemDC;
+ BITMAP theBitmapStruct;
+ CBitmap *theOldBitmap;
+
+ if ((inDC != nullptr) && (inBitmap != nullptr)) {
+ GetObject(inBitmap, sizeof(BITMAP), &theBitmapStruct);
+
+ theMemDC.CreateCompatibleDC(inDC);
+ theOldBitmap = theMemDC.SelectObject(CBitmap::FromHandle(inBitmap));
+
+ inDC->BitBlt(inPos.x, inPos.y, theBitmapStruct.bmWidth, theBitmapStruct.bmHeight, &theMemDC,
+ 0, 0, SRCCOPY);
+
+ theMemDC.SelectObject(theOldBitmap);
+ theMemDC.DeleteDC();
+ }
+}
+
+//==============================================================================
+/**
+ * DrawTransparentBitmap
+ *
+ * Draw a bitmap given a device context, position and HBITMAP handle using transparency
+ *
+ * @param inDC Device context for drawing
+ * @param inDestX Destination X position
+ * @param inDestY Destination Y position
+ * @param inBmp Bitmap for drawing
+ * @param inColorTransparent Transparent color
+ * @param inWidth Destination width
+ * @param inHeight Destination height
+ * @param inSourceX Source X position
+ * @param inSourceY Source Y position
+ */
+//==============================================================================
+void CWinUtils::DrawTransparentBitmap(CDC *inDC, long inDestX, long inDestY, HBITMAP inBmp,
+ COLORREF inColorTransparent, long inWidth, long inHeight,
+ long inSourceX, long inSourceY)
+{
+ CDC theMemDC;
+ BITMAP theBitmap;
+ CBitmap *theOldBmp;
+
+ GetObject(inBmp, sizeof(BITMAP), &theBitmap);
+
+ if (inWidth == -1)
+ inWidth = theBitmap.bmWidth;
+ if (inHeight == -1)
+ inHeight = theBitmap.bmHeight;
+
+ theMemDC.CreateCompatibleDC(inDC);
+ theOldBmp = theMemDC.SelectObject(CBitmap::FromHandle(inBmp));
+
+ TransparentBltExt(inDC->m_hDC, inDestX, inDestY, inWidth, inHeight, theMemDC.m_hDC, inSourceX,
+ inSourceY, inWidth, inHeight, inColorTransparent);
+
+ theMemDC.SelectObject(theOldBmp);
+ theMemDC.DeleteDC();
+}
+
+//==============================================================================
+/**
+ * TransparentBltExt
+ *
+ * Draw a bitmap given a device context, position and HBITMAP handle using transparency
+ *
+ * @param inDC Device context for drawing
+ * @param inDestX Destination X position
+ * @param inDestY Destination Y position
+ * @param inBmp Bitmap for drawing
+ * @param inColorTransparent Transparent color
+ * @param inWidth Destination width
+ * @param inHeight Destination height
+ * @param inSourceX Source X position
+ * @param inSourceY Source Y position
+ */
+//==============================================================================
+BOOL CWinUtils::TransparentBltExt(HDC inDCDest, long inXOriginDest, long inYOriginDest,
+ long inWidthDest, long inHeightDest, HDC inDCSrc,
+ long inXOriginSrc, long inYOriginSrc, long inWidthSrc,
+ long inHeightSrc, UINT inColorTransparent)
+{
+ if ((inWidthDest < 1) || (inWidthSrc < 1) || (inHeightDest < 1) || (inHeightSrc < 1))
+ return FALSE;
+
+ HDC theDC, theMaskDC;
+ HDC theNewMaskDC;
+ HBITMAP theBmp, theOldBmp, theMaskBmp, theOldMask;
+ HBITMAP theNewMask;
+
+ theDC = CreateCompatibleDC(nullptr);
+ theBmp = CreateBitmap(inWidthSrc, inHeightSrc, 1, GetDeviceCaps(theDC, BITSPIXEL), nullptr);
+
+ if (theBmp == nullptr) {
+ DeleteDC(theDC);
+ return FALSE;
+ }
+
+ theOldBmp = (HBITMAP)SelectObject(theDC, theBmp);
+
+ if (!BitBlt(theDC, 0, 0, inWidthSrc, inHeightSrc, inDCSrc, inXOriginSrc, inYOriginSrc,
+ SRCCOPY)) {
+ SelectObject(theDC, theOldBmp);
+ DeleteObject(theBmp);
+ DeleteDC(theDC);
+ return FALSE;
+ }
+
+ theMaskDC = CreateCompatibleDC(nullptr);
+ theMaskBmp = CreateBitmap(inWidthSrc, inHeightSrc, 1, 1, nullptr);
+
+ if (theMaskBmp == nullptr) {
+ SelectObject(theDC, theOldBmp);
+ DeleteObject(theBmp);
+ DeleteDC(theDC);
+ DeleteDC(theMaskDC);
+ return FALSE;
+ }
+
+ theOldMask = (HBITMAP)SelectObject(theMaskDC, theMaskBmp);
+ SetBkColor(theMaskDC, RGB(0x00, 0x00, 0x00));
+ SetTextColor(theMaskDC, RGB(0xFF, 0xFF, 0xFF));
+
+ if (!BitBlt(theMaskDC, 0, 0, inWidthSrc, inHeightSrc, nullptr, 0, 0, BLACKNESS)) {
+ SelectObject(theMaskDC, theOldMask);
+ DeleteObject(theMaskBmp);
+ DeleteDC(theMaskDC);
+ SelectObject(theDC, theOldBmp);
+ DeleteObject(theBmp);
+ DeleteDC(theDC);
+ return FALSE;
+ }
+
+ SetBkColor(theDC, inColorTransparent);
+ BitBlt(theMaskDC, 0, 0, inWidthSrc, inHeightSrc, theDC, 0, 0, SRCINVERT);
+
+ SetBkColor(theDC, RGB(0x00, 0x00, 0x00));
+ SetTextColor(theDC, RGB(0xFF, 0xFF, 0xFF));
+ BitBlt(theDC, 0, 0, inWidthSrc, inHeightSrc, theMaskDC, 0, 0, SRCAND);
+
+ theNewMaskDC = CreateCompatibleDC(nullptr);
+ theNewMask =
+ CreateBitmap(inWidthDest, inHeightDest, 1, GetDeviceCaps(theNewMaskDC, BITSPIXEL), nullptr);
+ if (theNewMask == nullptr) {
+ SelectObject(theDC, theOldBmp);
+ DeleteDC(theDC);
+ SelectObject(theMaskDC, theOldMask);
+ DeleteDC(theMaskDC);
+ DeleteDC(theNewMaskDC);
+ DeleteObject(theBmp);
+ DeleteObject(theMaskBmp);
+ return FALSE;
+ }
+
+ SetStretchBltMode(theNewMaskDC, COLORONCOLOR);
+ HBITMAP theOldNewMask = (HBITMAP)SelectObject(theNewMaskDC, theNewMask);
+ StretchBlt(theNewMaskDC, 0, 0, inWidthDest, inHeightDest, theMaskDC, 0, 0, inWidthSrc,
+ inHeightSrc, SRCCOPY);
+
+ SelectObject(theMaskDC, theOldMask);
+ DeleteDC(theMaskDC);
+ DeleteObject(theMaskBmp);
+
+ HDC theNewImageDC = CreateCompatibleDC(nullptr);
+ HBITMAP theNewImage =
+ CreateBitmap(inWidthDest, inHeightDest, 1, GetDeviceCaps(theNewMaskDC, BITSPIXEL), nullptr);
+ if (theNewImage == nullptr) {
+ SelectObject(theDC, theOldBmp);
+ DeleteDC(theDC);
+ DeleteDC(theNewMaskDC);
+ DeleteObject(theBmp);
+ return FALSE;
+ }
+
+ HBITMAP theOldNewImage = (HBITMAP)SelectObject(theNewImageDC, theNewImage);
+ StretchBlt(theNewImageDC, 0, 0, inWidthDest, inHeightDest, theDC, 0, 0, inWidthSrc, inHeightSrc,
+ SRCCOPY);
+
+ SelectObject(theDC, theOldBmp);
+ DeleteDC(theDC);
+ DeleteObject(theBmp);
+
+ // Finally, do the final blitting to get the image to the destination device context.
+ BitBlt(inDCDest, inXOriginDest, inYOriginDest, inWidthDest, inHeightDest, theNewMaskDC, 0, 0,
+ SRCAND);
+ BitBlt(inDCDest, inXOriginDest, inYOriginDest, inWidthDest, inHeightDest, theNewImageDC, 0, 0,
+ SRCPAINT);
+
+ SelectObject(theNewImageDC, theOldNewImage);
+ DeleteDC(theNewImageDC);
+ SelectObject(theNewMaskDC, theOldNewMask);
+ DeleteDC(theNewMaskDC);
+ DeleteObject(theNewImage);
+ DeleteObject(theNewMask);
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * DrawAlphaBlendBitmap
+ *
+ * Draw a bitmap given a device context, position and HBITMAP handle using
+ * alpha blending and transparency.
+ *
+ * @param inDC Device context for drawing
+ * @param inDestX Destination X position
+ * @param inDestY Destination Y position
+ * @param inBmp Bitmap for drawing
+ * @param inColorTransparent Transparent color
+ * @param inWidth Destination width
+ * @param inHeight Destination height
+ * @param inSourceX Source X position
+ * @param inSourceY Source Y position
+ */
+//==============================================================================
+void CWinUtils::DrawAlphaBlendBitmap(CDC *inDC, short inDestX, short inDestY, HBITMAP inBmp,
+ short inWidth, short inHeight, short inSourceX,
+ short inSourceY, short inAlpha, BOOL inTransparentFlag,
+ COLORREF inColorTransparent)
+{
+ Q_UNUSED(inColorTransparent);
+
+ CDC theMemDC;
+ BITMAP theBitmap;
+ CBitmap *theOldBmp;
+ short theWidth = inWidth;
+ short theHeight = inHeight;
+
+ COLORREF theBackColor;
+
+ // Get the size of the bitmap.
+ GetObject(inBmp, sizeof(BITMAP), &theBitmap);
+
+ if (inWidth == -1)
+ theWidth = (short)theBitmap.bmWidth;
+ if (inHeight == -1)
+ theHeight = (short)theBitmap.bmHeight;
+
+ theMemDC.CreateCompatibleDC(inDC);
+ theOldBmp = theMemDC.SelectObject(CBitmap::FromHandle(inBmp));
+
+ if (inTransparentFlag) {
+ // Get the current background pixel at 0,0
+ theBackColor = theMemDC.GetPixel(0, 0);
+
+ // Create a fill brush and select into the device context
+ CBrush theFillBrush(inColorTransparent);
+ CBrush *theOldFillBrush;
+
+ // Fill to replace the existing background color with the replacement color.
+ theOldFillBrush = theMemDC.SelectObject(&theFillBrush);
+ theMemDC.ExtFloodFill(0, 0, theBackColor, FLOODFILLSURFACE);
+ theMemDC.ExtFloodFill(theWidth - 1, 0, theBackColor, FLOODFILLSURFACE);
+ theMemDC.ExtFloodFill(theWidth - 1, theHeight - 1, theBackColor, FLOODFILLSURFACE);
+ theMemDC.ExtFloodFill(0, theHeight - 1, theBackColor, FLOODFILLSURFACE);
+ theMemDC.SelectObject(theOldFillBrush);
+ }
+
+ // Draw the alpha-blended image.
+ AlphaBlendExt(inDC->m_hDC, inDestX, inDestY, theWidth, theHeight, theMemDC.m_hDC, inSourceX,
+ inSourceY, theWidth, theHeight, inAlpha);
+
+ theMemDC.SelectObject(theOldBmp);
+ theMemDC.DeleteDC();
+}
+
+//==============================================================================
+/**
+ * AlphaBlendExt
+ *
+ * Draws a bitmap using alpha blending. Works in NT/2000/9x
+ *
+ * @param inDCDest Destination device context
+ * @param inX X coordinate for drawing
+ * @param inY Y coordinate for drawing
+ * @param inWidth Drawing width
+ * @param inHeight Drawing height
+ * @param inDCSrc Source device context
+ * @param inSourceX X source coordinate
+ * @param inSourceY Y source coordinate
+ * @param inSourceWidth Source drawing width
+ * @param inSourceHeight Source drawing height
+ * @param inAlpha Alpha blend: 0-255, where 255 is opaque
+ */
+//==============================================================================
+BOOL CWinUtils::AlphaBlendExt(HDC inDCDest, short inX, short inY, short inWidth, short inHeight,
+ HDC inDCSrc, short inSourceX, short inSourceY, short inSourceWidth,
+ short inSourceHeight, short inAlpha)
+{
+ BITMAPINFOHEADER theBMI;
+
+ theBMI.biSize = sizeof(BITMAPINFOHEADER);
+ theBMI.biWidth = inWidth;
+ theBMI.biHeight = inHeight;
+ theBMI.biPlanes = 1;
+ theBMI.biBitCount = 32; // 24 bits + alpha channel
+ theBMI.biCompression = BI_RGB; // no compression
+ theBMI.biSizeImage = 0;
+ theBMI.biXPelsPerMeter = 0;
+ theBMI.biYPelsPerMeter = 0;
+ theBMI.biClrUsed = 0; // use the whole palette
+ theBMI.biClrImportant = 0;
+
+ BYTE *theSrcBits;
+ HBITMAP theBmpSrc;
+
+ // Create DIB section in shared memory
+ theBmpSrc = CreateDIBSection(inDCSrc, (BITMAPINFO *)&theBMI, DIB_RGB_COLORS,
+ (void **)&theSrcBits, 0, 0L);
+
+ BYTE *theDestBits;
+ HBITMAP theBmpDest;
+
+ // Create DIB section in shared memory
+ theBmpDest = CreateDIBSection(inDCDest, (BITMAPINFO *)&theBMI, DIB_RGB_COLORS,
+ (void **)&theDestBits, 0, 0L);
+
+ // Copy our source and destination bitmaps onto our DIBSections.
+ // so we can get access to their bits using the BYTE*'s we used
+ // in the CreateDIBSection()s above.
+
+ HDC theDC = CreateCompatibleDC(nullptr);
+ HBITMAP theDCOld = (HBITMAP)SelectObject(theDC, theBmpSrc);
+
+ if (!StretchBlt(theDC, 0, 0, inWidth, inHeight, inDCSrc, inSourceX, inSourceY, inSourceWidth,
+ inSourceHeight, SRCCOPY)) {
+ SelectObject(theDC, theDCOld);
+ DeleteDC(theDC);
+ DeleteObject(theBmpSrc);
+ DeleteObject(theBmpDest);
+ return FALSE;
+ }
+
+ SelectObject(theDC, theBmpDest);
+
+ if (!StretchBlt(theDC, 0, 0, inWidth, inHeight, inDCDest, inX, inY, inWidth, inHeight,
+ SRCCOPY)) {
+ SelectObject(theDC, theDCOld);
+ DeleteDC(theDC);
+ DeleteObject(theBmpSrc);
+ DeleteObject(theBmpDest);
+ return FALSE;
+ }
+
+ SelectObject(theDC, theDCOld);
+ DeleteDC(theDC);
+
+ short theXLoop, theYLoop;
+
+ for (theYLoop = 0; theYLoop < inHeight; ++theYLoop) {
+ LPBYTE theDestRGB = (LPBYTE) & ((DWORD *)theDestBits)[theYLoop * inWidth];
+ LPBYTE theSrcRGB = (LPBYTE) & ((DWORD *)theSrcBits)[theYLoop * inWidth];
+
+ for (theXLoop = 0; theXLoop < inWidth; theXLoop++) {
+ theSrcRGB[0] = (BYTE)((theDestRGB[0] * (255 - inAlpha) + theSrcRGB[0] * inAlpha) >> 8);
+ theSrcRGB[1] = (BYTE)((theDestRGB[1] * (255 - inAlpha) + theSrcRGB[1] * inAlpha) >> 8);
+ theSrcRGB[2] = (BYTE)((theDestRGB[2] * (255 - inAlpha) + theSrcRGB[2] * inAlpha) >> 8);
+
+ theSrcRGB += 4;
+ theDestRGB += 4;
+ }
+ }
+
+ theDC = CreateCompatibleDC(nullptr);
+
+ theDCOld = (HBITMAP)SelectObject(theDC, theBmpSrc);
+
+ if (!BitBlt(inDCDest, inX, inY, inWidth, inHeight, theDC, 0, 0, SRCCOPY)) {
+ SelectObject(theDC, theDCOld);
+ DeleteDC(theDC);
+ DeleteObject(theBmpSrc);
+ DeleteObject(theBmpDest);
+ return FALSE;
+ }
+
+ SelectObject(theDC, theDCOld);
+ DeleteDC(theDC);
+
+ DeleteObject(theBmpSrc);
+ DeleteObject(theBmpDest);
+
+ return TRUE;
+}
+
+//==============================================================================
+/**
+ * GetStudioLogFont: Fill a LOGFONT structure with the default font information for Studio
+ *objects.
+ *
+ * Create a LOGFONT structure with data used by the various palettes and controls in Studio.
+ *
+ * @param outLogFont LOGFONT structure returned by this function.
+ */
+//==============================================================================
+void CWinUtils::GetStudioLogFont(LOGFONT &outLogFont)
+{
+ // Create the default Studio font - specified in string table so that it can be changed for
+ // different languages
+ memset(&outLogFont, 0, sizeof(LOGFONT));
+ CString theFontSize = ::LoadResourceString(IDS_STUDIOFONT_SIZE);
+ CString theFontFace = L"SegoeUI";
+ outLogFont.lfHeight = _wtoi(theFontSize);
+ wcscpy(outLogFont.lfFaceName, theFontFace);
+ // Make sure that you set the character set to the system default or your strings won't be
+ // localizable
+ outLogFont.lfCharSet = DEFAULT_CHARSET;
+}
+
+//=============================================================================
+/**
+ * Copy the contents of a File into an Archive.
+ * This will read the entire file into the archive.
+ * The file should exist already.
+ * @param inFilename the name of the file to read in.
+ * @param inDestination the destination archive for the data.
+ */
+//=============================================================================
+void CWinUtils::CopyFileToArchive(CString inFilename, CArchive &inDestination)
+{
+ CFile theFile(inFilename, CFile::modeRead);
+
+ unsigned long theLength = (unsigned long)theFile.GetLength();
+ unsigned long theBufferSize = 1024;
+ while (theBufferSize < theLength / 10 && theBufferSize < 1e6)
+ theBufferSize *= 2; // double until we have a buffer that is about %10 of file size
+ char *theBuffer = new char[theBufferSize];
+
+ unsigned long theReadCount = theBufferSize;
+ unsigned long theTotalReadCount = 0;
+
+ // Loop around reading the file in chunks
+ while (theTotalReadCount < theLength) {
+ if (theLength - theTotalReadCount < theReadCount)
+ theReadCount = theLength - theTotalReadCount;
+
+ theFile.Read(theBuffer, theReadCount);
+ inDestination.Write(theBuffer, theReadCount);
+
+ theTotalReadCount += theReadCount;
+ }
+
+ theFile.Close();
+ delete[] theBuffer;
+}
+
+//=============================================================================
+/**
+ * Copy the contents of an Archive into a File.
+ * If the file does not exist it will be created. If it does exist it will be
+ * cleared.
+ * @param inArchive the source of the data for the file.
+ * @param inDestination the file the data is to be written to.
+ * @param inLength the amount of data to copy to the file.
+ */
+//=============================================================================
+void CWinUtils::CopyArchiveToFile(CArchive &inArchive, CString inDestination,
+ unsigned long inLength)
+{
+ CFile theFile(inDestination, CFile::modeWrite | CFile::modeCreate);
+
+ unsigned long theBufferSize = 1024;
+ while (theBufferSize < inLength / 10 && theBufferSize < 1e6)
+ theBufferSize *= 2; // double until we have a buffer that is about %10 of file size
+ char *theBuffer = new char[theBufferSize];
+
+ unsigned long theLength = inLength;
+ unsigned long theReadCount = theBufferSize;
+ unsigned long theTotalReadCount = 0;
+
+ // Loop around reading from the archive and writing to the file.
+ while (theTotalReadCount < theLength) {
+ if (theLength - theTotalReadCount < theReadCount)
+ theReadCount = theLength - theTotalReadCount;
+
+ inArchive.Read(theBuffer, theReadCount);
+ theFile.Write(theBuffer, theReadCount);
+
+ theTotalReadCount += theReadCount;
+ }
+ theFile.Close();
+ delete[] theBuffer;
+}
+
+//=============================================================================
+/**
+ * Used for StringToInt64 to avoid double errors in math.h's pow( ).
+ *
+ * @param inNumber Base number
+ * @param inPower Power to raise the base
+ *
+ * @return Result of x^y
+ */
+//=============================================================================
+unsigned __int64 CWinUtils::RaisePower(unsigned __int64 inNumber, unsigned long inPower)
+{
+ if (inPower == 0)
+ return 1;
+ return inNumber * RaisePower(inNumber, inPower - 1);
+}
+
+//=============================================================================
+/**
+ * Parse the string into an __int64.
+ *
+ * @param inString the string to parse.
+ * @param inStringLen the length of the string.
+ * @param inBase the base of the character encoding (16 is Hex, 10 is decimal).
+ *
+ * @return <the parsed string.
+ */
+//=============================================================================
+unsigned __int64 CWinUtils::StringToInt64(const char *inString, unsigned short inStringLen,
+ unsigned short inBase)
+{
+ unsigned __int64 theKey = 0;
+ char theChar;
+ unsigned short i;
+
+ for (i = 0; i < inStringLen; ++i) {
+ theChar = inString[i];
+ if (theChar < 'A')
+ theChar -= '0';
+ else if (theChar < 'a')
+ theChar -= 'A' - 10;
+ else
+ theChar -= 'a' - 10;
+
+ theKey +=
+ ((unsigned __int64)theChar) * (unsigned __int64)RaisePower(inBase, inStringLen - 1 - i);
+ }
+ return theKey;
+}
+
+//=============================================================================
+/**
+ * Get a wchar_t from an MFC CString (used to go to Q3DStudio::CString without flattening unicode)
+ *
+ * @param inMFCString the string convert
+ *
+ * @return <the null-terminated wchar_t buffer. caller must delete[]!!!!
+ */
+//=============================================================================
+wchar_t *CWinUtils::WideFromMFC(::CString inMFCString)
+{
+ TCHAR *theBuffer = inMFCString.GetBuffer(0);
+ long theMultiLength = inMFCString.GetLength();
+ wchar_t *theDestBuffer = new wchar_t[theMultiLength * 2 + 1]; // times 2 so we know we are big
+ // enough for 1 and 2 byte chars
+ // from multibyte
+ wcscpy(theDestBuffer, theBuffer);
+ inMFCString.ReleaseBuffer();
+ return theDestBuffer;
+} \ No newline at end of file
diff --git a/src/Authoring/Studio/_Win/Utils/WinUtils.h b/src/Authoring/Studio/_Win/Utils/WinUtils.h
new file mode 100644
index 00000000..121a61bd
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Utils/WinUtils.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_WIN_UTILS_H
+#define INCLUDED_WIN_UTILS_H 1
+
+#pragma once
+
+class CWinUtils
+{
+public:
+ static BOOL AlphaBlendExt(HDC hDCDest, short x, short y, short cx, short cy, HDC hDCSrc,
+ short sx, short sy, short sw, short sh, short inAlpha);
+ static void DrawAlphaBlendBitmap(CDC *inDC, short inDestX, short inDestY, HBITMAP inBmp,
+ short inWidth, short inHeight, short inSourceX,
+ short inSourceY, short inAlpha, BOOL inTransparentFlag = FALSE,
+ COLORREF inColorTransparent = RGB(0x00, 0x00, 0x00));
+ static void DrawBitmap(CDC *inDC, CPoint inPos, HBITMAP inBitmap);
+ static void DrawTransparentBitmap(CDC *pDC, long x, long y, HBITMAP hBmp,
+ COLORREF colorTransparent, long w = -1, long h = -1,
+ long sx = 0, long sy = 0);
+ static BOOL TransparentBltExt(HDC inDCDest, long inXOriginDest, long inYOriginDest,
+ long inWidthDest, long inHeightDest, HDC inDCSrc,
+ long inXOriginSrc, long inYOriginSrc, long inWidthSrc,
+ long inHeightSrc, UINT inColorTransparent);
+
+ static void GetStudioLogFont(LOGFONT &outLogFont);
+ static unsigned __int64 StringToInt64(const char *inString, unsigned short inStringLen,
+ unsigned short inBase);
+ static void CopyFileToArchive(CString inFilename, CArchive &inDestination);
+ static void CopyArchiveToFile(CArchive &inArchive, CString inDestination,
+ unsigned long inLength);
+ static wchar_t *WideFromMFC(::CString inMFCString);
+
+protected:
+ static unsigned __int64 RaisePower(unsigned __int64 inNumber, unsigned long inPower);
+};
+
+#endif // INCLUDED_WIN_UTILS_H
diff --git a/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp b/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp
new file mode 100644
index 00000000..517aed97
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Workspace/Dialogs.cpp
@@ -0,0 +1,1332 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "stdafx.h"
+#include "qtAuthoring-config.h"
+#include "Strings.h"
+#include "StringLoader.h"
+
+#include "Dialogs.h"
+#include "StudioApp.h"
+#include "Doc.h"
+
+#include "InterpolationDlg.h"
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "UICMessageBox.h"
+#include "StringTokenizer.h"
+#include "Resource.h"
+#include "Preferences.h"
+#include "ProgressPalette.h"
+#include "ProgressView.h"
+#include "Views.h"
+#include "WinUtils.h"
+#include "MasterP.h"
+#include "TimeEditDlg.h"
+#include "StudioPreferences.h"
+#include "ResetKeyframeValuesDlg.h"
+#include "GLVersionDlg.h"
+#endif
+#include "Core.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "UICMacros.h"
+#include "IDocumentEditor.h"
+#include "UICFileTools.h"
+#endif
+#include "ImportUtils.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "FileDialogEx.h"
+#endif
+
+#include "StringLoader.h"
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QStandardPaths>
+
+#include <iostream>
+
+//=============================================================================
+/**
+ * Constructor
+ * @param inShowGUI true if dialogs should be displayed or piped to std:cout instead
+ */
+CDialogs::CDialogs(bool inShowGUI /*= true*/)
+#ifdef KDAB_TEMPORARILY_REMOVED
+ : m_ProgressPalette(nullptr)
+ , m_ShowGUI(inShowGUI)
+ , m_LastSaveFile(CUICFile::GetApplicationDirectory().GetAbsolutePath())
+#endif
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CDialogs::~CDialogs()
+{
+}
+
+//=============================================================================
+/**
+ * Displays a multiline text edit.
+ * @param ioText
+ * @param inNotification
+ */
+#if 0
+void CDialogs::DisplayMultilineTextEdit(Q3DStudio::CString &ioText,
+ CMultilineEditDlg::INotification *inNotifiction)
+{
+ CRct theLocation = CStudioPreferences::GetMultilineTextLocation();
+
+ CMultilineEditDlg theMultilineEditDlg(inNotifiction, theLocation);
+ theMultilineEditDlg.SetString(ioText);
+ if (theMultilineEditDlg.DoModal() == IDOK) {
+ // Set the string
+ ioText = theMultilineEditDlg.GetString();
+
+ // Save the window position
+ theLocation = theMultilineEditDlg.GetLocation();
+ CStudioPreferences::SetMultilineTextLocation(theLocation);
+ }
+}
+#endif
+
+//=============================================================================
+/**
+ * Displays a dialog asking the user to choose the keyframe interpolation.
+ *
+ * @param ioEaseIn value to be set as the ease in default - passes back the value chosen by the user
+ * @param ioEaseOut value to be set as the ease out default - passes back the value chosen by the
+ * user
+ * @return true if the user clicked OK on the dialog (indicating that the values should be updated
+ * on the track)
+ */
+bool CDialogs::PromptForKeyframeInterpolation(float &ioEaseIn, float &ioEaseOut)
+{
+ bool theReturnValue = false;
+
+ CInterpolationDlg theInterpolationDialog;
+ theInterpolationDialog.setEaseIn(ioEaseIn);
+ theInterpolationDialog.setEaseOut(ioEaseOut);
+
+ // If the user presses the OK button
+ if (theInterpolationDialog.exec() == QDialog::Accepted) {
+ // Retrieve the new interpolation values
+ ioEaseIn = theInterpolationDialog.easeIn();
+ ioEaseOut = theInterpolationDialog.easeOut();
+ theReturnValue = true;
+ }
+
+ return theReturnValue;
+}
+
+//=============================================================================
+/**
+ * Notify the user that the deletion of an asset has failed.
+ */
+void CDialogs::DisplayAssetDeleteFailed()
+{
+ Q3DStudio::CString theMessage =
+ ::LoadResourceString(IDS_ERROR_CLIENTSAVE); // TODO: Should display the correct string
+ Q3DStudio::CString theTitle = ::LoadResourceString(IDS_ERROR_MSGTITLE);
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theMessage, CUICMessageBox::ICON_ERROR, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << theMessage.GetCharStar() << std::endl;
+ }
+}
+
+//=============================================================================
+/**
+ * Get the export choice.
+ */
+CUICFile CDialogs::GetExportChoice(const Q3DStudio::CString &, const Q3DStudio::CString &)
+{
+ // Need to fix this for windows if we decide to use it
+ return CUICFile("", false, false);
+}
+
+//==============================================================================
+/**
+ * Notify that we are unable to refresh the resource.
+ */
+void CDialogs::DisplayRefreshResourceFailed(const Q3DStudio::CString &inResourceName,
+ const Q3DStudio::CString &inDescription)
+{
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_ERROR_REFRESHRESOURCETITLE));
+
+ Q3DStudio::CString theText;
+ theText.Format(::LoadResourceString(IDS_ERROR_REFRESHRESOURCETEXT),
+ static_cast<const wchar_t *>(inResourceName));
+
+ if (!inDescription.IsEmpty())
+ theText += inDescription;
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theText, CUICMessageBox::ICON_WARNING, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << theText.GetCharStar() << std::endl << std::endl;
+ }
+}
+
+//=============================================================================
+/**
+ * Notify the user that the loading of the requested resource failed.
+ *
+ * Possible failed import messages are:
+ * IDS_ERROR_IMPORTLUARESOURCETEXT - Sorry - Studio was unable to
+ *import the LUA script.\nPlease check the file and try again.\nNote that LUA files must be
+ *syntactically correct to be importable.
+ * IDS_ERROR_IMPORTRESOURCETEXT - Sorry - Studio was unable to
+ *import the resource.\nPlease check the above file and try again.
+ * IDS_ERROR_IMPORTUNSUPPORTEDRESOURCETYPETEXT - Sorry - Studio was unable to import
+ *the resource file.\nThis resource file type is not currently supported by Studio.\n
+ *
+ * @param inURL the URL for the asset that was to have been imported
+ * @param inErrorDescription description for the failure, if any
+ * @param inWarningsOnly not a failure, just warnings
+ */
+void CDialogs::DisplayImportFailed(const QUrl &inURL, const QString &inDescription,
+ bool inWarningsOnly)
+{
+ // Notify the user we couldn't load the resource.
+ Q3DStudio::CString theTitle, theText, theMsgText;
+
+ theTitle = ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETITLE);
+ // specify if its an error or warning
+ theTitle = (!inWarningsOnly) ? ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETITLE_ERROR)
+ : ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETITLE_WARNING);
+
+ // Determine the asset type
+ EStudioObjectType theAssetType =
+ Q3DStudio::ImportUtils::GetObjectFileTypeForFile(inURL.path(), false)
+ .m_ObjectType;
+
+ bool theIsStudioObject = theAssetType != OBJTYPE_UNKNOWN;
+
+ // Is this a LUA file, but perhaps incorrectly formatted?
+ if (theAssetType == OBJTYPE_BEHAVIOR) {
+ // Load the message about the LUA format
+ if (inWarningsOnly)
+ theText = ::LoadResourceString(IDS_WARNING_IMPORTLUARESOURCETEXT);
+ else
+ theText = ::LoadResourceString(IDS_ERROR_IMPORTLUARESOURCETEXT);
+ if (!inDescription.isEmpty()) {
+ theText += L"\n";
+ theText += Q3DStudio::CString::fromQString(inDescription);
+ }
+ } else if (theAssetType != OBJTYPE_UNKNOWN || theIsStudioObject) {
+ // Valid registered file type, but invalid file
+
+ bool theNoDescription = inDescription.isEmpty();
+ // Load default text stating that the import resource failed.
+ // descriptions if present are presented as "reasons" for failure.
+ if (!inWarningsOnly || theNoDescription) {
+ theText.Format(::LoadResourceString(IDS_ERROR_IMPORTRESOURCETEXT), theNoDescription
+ ? L"."
+ : ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETEXT_REASON).c_str());
+ theText += "\n";
+ }
+ if (!theNoDescription)
+ theText += Q3DStudio::CString::fromQString(inDescription);
+ else
+ theText += ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETEXT_CHECKFILE);
+ } else {
+ // Display the warning messsage if we have one
+ // instead of a meaningless message. This provides more feed back
+ if (!inDescription.isEmpty())
+ theText += Q3DStudio::CString::fromQString(inDescription);
+ else
+ theText = ::LoadResourceString(
+ IDS_ERROR_IMPORTUNSUPPORTEDRESOURCETYPETEXT); // Load default text stating that the
+ // import resource failed.
+ }
+
+ Q3DStudio::CString theFormatString(
+ !inWarningsOnly ? ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETEXT_FAILED)
+ : ::LoadResourceString(IDS_ERROR_IMPORTRESOURCETEXT_COMPLETEWITHWARNING));
+ theFormatString += _UIC("\n%ls\n\n");
+ std::wstring wstr = inURL.path().toStdWString();
+ theMsgText.Format(theFormatString, static_cast<const wchar_t *>(wstr.c_str()));
+ theMsgText += theText;
+
+ // Display the failed import resource message.
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theMsgText, CUICMessageBox::ICON_WARNING, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << theMsgText.GetCharStar() << std::endl;
+ }
+}
+
+namespace {
+
+inline Q3DStudio::CString CreateExtensionsList(const char **extList)
+{
+ Q3DStudio::CString retval;
+ for (const char **ext = extList; *ext != nullptr; ++ext) {
+ if (retval.Length())
+ retval += " ";
+ retval += Q3DStudio::CString("*.") + *ext;
+ }
+ return retval;
+}
+
+struct SAllowedTypesEntry
+{
+ Q3DStudio::DocumentEditorFileType::Enum m_FileType;
+ long m_ResourceStringId; // Model Files, Image Files, etc
+ const char **m_FileExtensions;
+};
+const char *imgExts[] = {
+ "png", "jpg", "jpeg", "dds", "bmp", "gif", "hdr", nullptr,
+};
+const wchar_t *wideImgExts[] = {
+ L"png", L"jpg", L"jpeg", L"dds", L"bmp", L"gif", L"hdr", nullptr,
+};
+const char *modelExts[] = {
+ CDialogs::GetDAEFileExtension(),
+#ifdef QT_3DSTUDIO_FBX
+ CDialogs::GetFbxFileExtension(),
+#endif
+ // TODO CDialogs::GetImportFileExtension(),
+ // TODO CDialogs::GetMeshFileExtension(),
+ nullptr,
+};
+const char *meshExts[] = {
+ CDialogs::GetMeshFileExtension(), nullptr,
+};
+const char *importExts[] = {
+ CDialogs::GetImportFileExtension(), nullptr,
+};
+const char *behaviorExts[] = {
+ CDialogs::GetLUAFileExtension(), nullptr,
+};
+const char *fontExts[] = {
+ "ttf", "otf", nullptr,
+};
+const wchar_t *wideFontExts[] = {
+ L"ttf", L"otf", nullptr,
+};
+const char *effectExts[] = {
+ "effect", nullptr,
+};
+const wchar_t *wideEffectExts[] = {
+ L"effect", nullptr,
+};
+const char *materialExts[] = {
+ "material", nullptr,
+};
+const wchar_t *wideMaterialExts[] = {
+ L"material", nullptr,
+};
+const char *soundExts[] = {
+ "wav", nullptr,
+};
+const wchar_t *wideSoundExts[] = {
+ L"wav", nullptr,
+};
+
+// List of file types allowed during import
+SAllowedTypesEntry g_AllowedImportTypes[] = {
+ { Q3DStudio::DocumentEditorFileType::DAE, IDS_LIBRARYIMPORT_MODEL, modelExts },
+#ifdef QT_3DSTUDIO_FBX
+ { Q3DStudio::DocumentEditorFileType::FBX, IDS_LIBRARYIMPORT_MODEL, modelExts },
+#endif
+};
+size_t g_NumAllowedImportTypes = sizeof(g_AllowedImportTypes) / sizeof(*g_AllowedImportTypes);
+
+// List of file types allowed for file references
+SAllowedTypesEntry g_AllowedFileReferencesTypes[] = {
+ { Q3DStudio::DocumentEditorFileType::Image, IDS_LIBRARYIMPORT_IMAGE, imgExts },
+ { Q3DStudio::DocumentEditorFileType::Behavior, IDS_LIBRARYIMPORT_BEHAVIOR, behaviorExts },
+ { Q3DStudio::DocumentEditorFileType::Mesh, IDS_LIBRARYIMPORT_MESH, meshExts },
+ { Q3DStudio::DocumentEditorFileType::Import, IDS_LIBRARYIMPORT_IMPORT, importExts },
+ { Q3DStudio::DocumentEditorFileType::Effect, IDS_LIBRARYIMPORT_EFFECT, effectExts },
+};
+size_t g_NumAllowedFileReferencesTypes =
+ sizeof(g_AllowedFileReferencesTypes) / sizeof(*g_AllowedFileReferencesTypes);
+}
+
+QString CDialogs::ConfirmRefreshModelFile(const QString &inFile)
+{
+ // this produces an extension string which contains all allowed formats specified in
+ // g_AllowedImportTypes
+ // currently DAE and FBX
+ QString theFileFilter =
+ CreateAllowedTypesString(Q3DStudio::DocumentEditorFileType::DAE, true);
+
+
+ return QFileDialog::getOpenFileName(qApp->activeWindow(), QObject::tr("Open"),
+ inFile, theFileFilter);
+}
+
+//==============================================================================
+/**
+ * Allows the user to select/confirm the file to fix broken file reference.
+ * This method opens a file dialog to the specified location, and allows the user to
+ * accept, or select a different file.
+ * @param outFile The output path
+ * @param inFileType The file type filter for the dialog.
+ * @return true If the user accepted the file, false if the user canceled.
+ */
+bool CDialogs::LocateFileReference(CUICFile &outFile, long inFileType)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ Q3DStudio::CString theFileFilter = CreateAllowedTypesString(inFileType, false);
+
+ bool theResult = false;
+
+ // Open the dialog.
+ CFileDialogEx theFileDialog(TRUE, nullptr, nullptr, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
+ theFileFilter, nullptr);
+
+ // Set the initial directory to document path's directory (it should exist).
+ CUICFile theDocumentFile(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath());
+ Q3DStudio::CFilePath theInitDir =
+ Q3DStudio::CFilePath(theDocumentFile.GetAbsolutePath()).GetDirectory();
+ theFileDialog.SetInitialDirectory(theInitDir);
+
+ // Set the title of the dialog
+ theFileDialog.SetWindowTitle(::LoadResourceString(IDS_PROJECT_CM_LOCATE_FILE));
+
+ INT_PTR refreshResult = theFileDialog.DoModal();
+
+ if (refreshResult == IDOK) {
+ Q3DStudio::CString thePath = theFileDialog.GetPathName();
+ Q3DStudio::CString theName = theFileDialog.GetFileName();
+ outFile = CUICFile(thePath, theName);
+
+ // The user has accepted this file.
+ theResult = true;
+ }
+
+ return theResult;
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * Allows the user to select/confirm the folder to fix broken folder reference.
+ * This method opens a folder dialog to the specified location, and allows the user to
+ * accept, or select a different folder.
+ * @param outFile The output path
+ * @return true If the user accepted the file, false if the user canceled.
+ */
+bool CDialogs::LocateFolderReference(CUICFile &outFile)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // Set the initial directory to document path's directory (it should exist).
+ CUICFile theDocumentFile(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath());
+ Q3DStudio::CFilePath theInitDir =
+ Q3DStudio::CFilePath(theDocumentFile.GetAbsolutePath()).GetDirectory();
+
+ // Set the title of the dialog
+ Q3DStudio::CString theDialogTitle(::LoadResourceString(IDS_PROJECT_CM_LOCATE_FOLDER));
+
+ return BrowseForFolderDialog(outFile, theInitDir, theDialogTitle);
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * BrowseCallbackProc: Handles the callback proc for SHBrowseForFolder()
+ *
+ * Handles selecting the correct path when the SHBrowseForFolder dialog is
+ * initially displayed.
+ */
+#ifdef KDAB_TEMPORARILY_REMOVED
+static int CALLBACK BrowseCallbackProc(HWND inHwnd, UINT inMsg, LPARAM /*inlParam*/,
+ LPARAM inlpData)
+{
+ switch (inMsg) {
+ case BFFM_INITIALIZED:
+ // Indicates the browse dialog box has finished initializing. The lParam value is zero.
+ if (inlpData != nullptr) {
+ // Do so by sendint the BFFM_SETSELECTION message to the inHwnd:
+ // Selects the specified folder. To use a PIDL to specify the folder, set the
+ // message's lParam to the PIDL, and set wParam to FALSE. To specify the folder's
+ // path, set the message's lParam value to point to a nullptr-terminated string with
+ // the path, and set wParam to TRUE.
+
+ SendMessage(inHwnd, BFFM_SETSELECTION, (WPARAM)TRUE, (LPARAM)inlpData);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+//==============================================================================
+/**
+ * Show browse for folder dialog
+ * @param outFile The output path
+ * @param inDefaultDir The default directory
+ * @param inDialogTitle The title of the dialog
+ * @return true If the user accepted the file, false if the user canceled.
+ */
+bool CDialogs::BrowseForFolderDialog(CUICFile &outFile, Q3DStudio::CString inDefaultDir,
+ Q3DStudio::CString inDialogTitle)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ bool theResult = false;
+ BROWSEINFO theInfo = { 0 };
+ TCHAR theDispName[MAX_PATH] = { '\0' };
+
+ theInfo.pszDisplayName = theDispName;
+ theInfo.lpszTitle = inDialogTitle;
+ theInfo.ulFlags = BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON;
+ theInfo.lpfn = BrowseCallbackProc;
+ theInfo.lParam = (LPARAM)inDefaultDir.c_str();
+
+ // Ask the user to choose a directory
+ LPITEMIDLIST theItem = SHBrowseForFolder(&theInfo);
+
+ if (theItem != nullptr) {
+ TCHAR thePath[MAX_PATH] = { '\0' };
+ // If a path was chosen
+ if (::SHGetPathFromIDList(theItem, thePath)) {
+ theResult = true;
+ outFile = CUICFile(Q3DStudio::CString(thePath));
+ }
+
+ // Free Memory used
+ LPMALLOC thePMalloc = 0;
+ if (SUCCEEDED(SHGetMalloc(&thePMalloc))) {
+ thePMalloc->Free(theItem);
+ thePMalloc->Release();
+ }
+ }
+
+ return theResult;
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * Notify the user that the presentation we tried to load has failed.
+ * @param inPresentation The AKFile that we failed to load.
+ */
+void CDialogs::DisplayLoadingPresentationFailed(const CUICFile &inPresentation,
+ long inErrorIDS /*= -1 */)
+{
+ Q_UNUSED(inPresentation);
+
+ if (inErrorIDS == -1) // if unspecified, default to the 'generic' string
+ inErrorIDS = IDS_ERROR_LOADPRESENTATION;
+ Q3DStudio::CString theErrorMessage;
+ theErrorMessage.Format(::LoadResourceString(inErrorIDS),
+ static_cast<const wchar_t *>(inPresentation.GetName()));
+ Q3DStudio::CString theErrorTitle(::LoadResourceString(IDS_ERROR_LOADPRESENTATION_TITLE));
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theErrorTitle, theErrorMessage, CUICMessageBox::ICON_WARNING, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theErrorTitle.GetCharStar() << ": " << theErrorMessage.GetCharStar() << std::endl;
+ }
+}
+
+//==============================================================================
+/**
+ * Notify the user that the presentation we tried to save has failed.
+ *
+ * @param inSavedLocation The AKFile that we failed to save.
+ */
+void CDialogs::DisplaySavingPresentationFailed()
+{
+ Q3DStudio::CString theErrorMessage = ::LoadResourceString(IDS_ERROR_EXPORTPRESENTATION);
+ Q3DStudio::CString theErrorTitle = ::LoadResourceString(IDS_PROJNAME);
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theErrorTitle, theErrorMessage, CUICMessageBox::ICON_WARNING, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theErrorTitle.GetCharStar() << ": " << theErrorMessage.GetCharStar() << std::endl;
+ }
+}
+
+//==============================================================================
+/**
+ * Display a message box to indicate failure to overwrite a read-only file
+ *
+ * @param inSavedLocation
+ * the file location to be saved
+ *
+ * @return void
+ */
+void CDialogs::DisplaySaveReadOnlyFailed(const CUICFile &inSavedLocation)
+{
+ Q3DStudio::CString theMsg = ::LoadResourceString(IDS_SAVE_READONLY_WARNING);
+ Q3DStudio::CString theTitle = ::LoadResourceString(IDS_PROJNAME);
+
+ Q3DStudio::CString theFormattedText;
+ theFormattedText.Format(theMsg, static_cast<const wchar_t *>(inSavedLocation.GetName()));
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theFormattedText, CUICMessageBox::ICON_WARNING, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << theFormattedText.GetCharStar() << std::endl;
+ }
+}
+
+//==============================================================================
+/**
+ * Displays a CUICMessageBox using the specified parameters. The message box
+ * is modal to the main frame. This provides an easy way to place modal dialogs
+ * to the user, without requiring your class to know about the main frame or
+ * window refs.
+ * @param inTitle Title of the message box (not used on Mac)
+ * @param inText Text of the message
+ * @param inIcon Icon to be displayed next to the text
+ * @param inShowCancel true to show a Cancel button, false only show an OK button
+ * @return Indication of which button was pressed to dismiss the dialog
+ */
+CUICMessageBox::EMessageBoxReturn
+CDialogs::DisplayMessageBox(const Q3DStudio::CString &inTitle, const Q3DStudio::CString &inText,
+ CUICMessageBox::EMessageBoxIcon inIcon, bool inShowCancel)
+{
+ CUICMessageBox::EMessageBoxReturn theUserChoice;
+
+ if (m_ShowGUI) {
+ theUserChoice =
+ CUICMessageBox::Show(inTitle, inText, inIcon, inShowCancel, qApp->activeWindow());
+ } else {
+ std::cout << inTitle.GetCharStar() << ": " << inText.GetCharStar() << std::endl;
+ theUserChoice = CUICMessageBox::MSGBX_OK;
+ }
+
+ return theUserChoice;
+}
+
+int CDialogs::DisplayChoiceBox(const Q3DStudio::CString &inTitle, const Q3DStudio::CString &inText,
+ int inIcon)
+{
+ if (m_ShowGUI) {
+ QMessageBox box;
+ box.setWindowTitle(inTitle.toQString());
+ box.setText(inText.toQString());
+ switch (inIcon) {
+ case CUICMessageBox::ICON_WARNING:
+ box.setIcon(QMessageBox::Warning);
+ break;
+ case CUICMessageBox::ICON_ERROR:
+ box.setIcon(QMessageBox::Critical);
+ break;
+ case CUICMessageBox::ICON_INFO:
+ box.setIcon(QMessageBox::Information);
+ break;
+ default:
+ break;
+ }
+ box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+ switch (box.exec()) {
+ case QMessageBox::Yes:
+ return IDYES;
+ case QMessageBox::No:
+ return IDNO;
+ default:
+ Q_UNREACHABLE();
+ }
+ } else {
+ std::cout << inTitle.GetCharStar() << ": " << inText << std::endl;
+ return IDYES;
+ }
+}
+
+const char *CDialogs::GetDAEFileExtension()
+{
+ return "dae";
+}
+const char *CDialogs::GetFbxFileExtension()
+{
+ return "fbx";
+}
+// Null terminated list
+const char **CDialogs::GetImgFileExtensions()
+{
+ return imgExts;
+}
+const char *CDialogs::GetImportFileExtension()
+{
+ return "import";
+}
+const char *CDialogs::GetMeshFileExtension()
+{
+ return "mesh";
+}
+const char *CDialogs::GetLUAFileExtension()
+{
+ return "lua";
+}
+const char *CDialogs::GetQmlFileExtension()
+{
+ return "qml";
+}
+const char **CDialogs::GetFontFileExtensions()
+{
+ return fontExts;
+}
+const char **CDialogs::GetEffectFileExtensions()
+{
+ return effectExts;
+}
+const char **CDialogs::GetMaterialFileExtensions()
+{
+ return materialExts;
+}
+const char **CDialogs::GetSoundFileExtensions()
+{
+ return soundExts;
+}
+
+bool IsFileExtension(const char *inExt, const char **inExts)
+{
+ if (inExt == nullptr)
+ return false;
+ for (const char **ext = inExts; *ext != nullptr; ++ext)
+ if (QString::compare(inExt, *ext, Qt::CaseInsensitive) == 0)
+ return true;
+ return false;
+}
+bool CDialogs::IsImageFileExtension(const char *inExt)
+{
+ return IsFileExtension(inExt, imgExts);
+}
+bool CDialogs::IsFontFileExtension(const char *inExt)
+{
+ return IsFileExtension(inExt, fontExts);
+}
+bool CDialogs::IsEffectFileExtension(const char *inExt)
+{
+ return IsFileExtension(inExt, effectExts);
+}
+bool CDialogs::IsMaterialFileExtension(const char *inExt)
+{
+ return IsFileExtension(inExt, materialExts);
+}
+bool CDialogs::IsSoundFileExtension(const char *inExt)
+{
+ return IsFileExtension(inExt, soundExts);
+}
+
+const wchar_t **CDialogs::GetWideImgFileExtensions()
+{
+ return wideImgExts;
+}
+const wchar_t *CDialogs::GetWideDAEFileExtension()
+{
+ return L"dae";
+}
+const wchar_t *CDialogs::GetWideFbxFileExtension()
+{
+ return L"fbx";
+}
+const wchar_t *CDialogs::GetWideImportFileExtension()
+{
+ return L"import";
+}
+const wchar_t *CDialogs::GetWideMeshFileExtension()
+{
+ return L"mesh";
+}
+const wchar_t *CDialogs::GetWideLUAFileExtension()
+{
+ return L"lua";
+}
+const wchar_t **CDialogs::GetWideFontFileExtensions()
+{
+ return wideFontExts;
+}
+const wchar_t **CDialogs::GetWideEffectFileExtensions()
+{
+ return wideEffectExts;
+}
+const wchar_t **CDialogs::GetWideMaterialFileExtensions()
+{
+ return wideMaterialExts;
+}
+
+bool IsFileExtension(const wchar_t *inExt, const wchar_t **inExts)
+{
+ if (inExt == nullptr)
+ return false;
+ for (const wchar_t **ext = inExts; *ext != nullptr; ++ext) {
+ if (QString::compare(QString::fromWCharArray(inExt),
+ QString::fromWCharArray(*ext), Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+bool CDialogs::IsImageFileExtension(const wchar_t *inExt)
+{
+ return IsFileExtension(inExt, wideImgExts);
+}
+bool CDialogs::IsFontFileExtension(const wchar_t *inExt)
+{
+ return IsFileExtension(inExt, wideFontExts);
+}
+bool CDialogs::IsEffectFileExtension(const wchar_t *inExt)
+{
+ return IsFileExtension(inExt, wideEffectExts);
+}
+
+bool CDialogs::IsMaterialFileExtension(const wchar_t *inExt)
+{
+ return IsFileExtension(inExt, wideMaterialExts);
+}
+
+bool CDialogs::IsPathFileExtension(const wchar_t *inExt)
+{
+ return QString::compare(QString::fromWCharArray(inExt), "svg", Qt::CaseInsensitive) == 0;
+}
+
+bool CDialogs::IsPathBufferExtension(const wchar_t *inExt)
+{
+ return QString::compare(QString::fromWCharArray(inExt), "path", Qt::CaseInsensitive) == 0;
+}
+
+bool CDialogs::IsSoundFileExtension(const wchar_t *inExt)
+{
+ return IsFileExtension(inExt, wideSoundExts);
+}
+
+//==============================================================================
+/**
+ * File picker.
+ * Format for filter is as follows:
+ * Each entry in the filter list is '<description>|*.ext'
+ * String must end with '|'
+ * Example, XML Exporter (*.xml)|*.xml|All files (*.*)|*.*|
+ *
+ * @param inFileExtensionFilter Well-formed extension filter string. See comments
+ *above for the format.
+ * @param inDefault Default string in case of abortion or
+ *failure
+ * @return chosen file path if successful, inDefault otherwise.
+ */
+Q3DStudio::CString CDialogs::GetFilePathChoice(const Q3DStudio::CString &inFileExtensionFilter,
+ const Q3DStudio::CString &inDefault,
+ const Q3DStudio::CString *inDialogTitle /*= nullptr */)
+{
+ Q3DStudio::CString theResult(inDefault);
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CFileDialogEx theFileDlg(true, nullptr, nullptr, 0, inFileExtensionFilter, AfxGetMainWnd());
+
+ Q3DStudio::CString theInitDir =
+ CPreferences::GetUserPreferences().GetStringValue("LastChosenFile", "");
+ theFileDlg.SetInitialDirectory(theInitDir);
+
+ // Remember the filter setting between invokations
+ static long s_LastFilterIndex = -1;
+ if (s_LastFilterIndex > 0)
+ theFileDlg.SetFilterIndex(s_LastFilterIndex);
+
+ if (inDialogTitle) // Set the title of the dialog if specied
+ theFileDlg.SetWindowTitle(*inDialogTitle);
+
+ if (theFileDlg.DoModal() == IDOK) {
+ theResult = theFileDlg.GetPathName();
+ CPreferences::GetUserPreferences().SetStringValue("LastChosenFile", theResult);
+ s_LastFilterIndex = theFileDlg.GetLastFilterIndex();
+ }
+#endif
+
+ return theResult;
+}
+
+//==============================================================================
+/**
+ * CreateAllowedTypesString: Creates the string used to determine allowable types for import or
+ *for filereferences
+ * @return the string that dynamicly created with the extensions supported.
+ */
+QString CDialogs::CreateAllowedTypesString(long inFileTypeFilter, bool inForImport)
+{
+ QString theReturnString;
+ size_t theCount = inForImport ? g_NumAllowedImportTypes : g_NumAllowedFileReferencesTypes;
+ for (size_t idx = 0; idx < theCount; ++idx) {
+ const SAllowedTypesEntry &entry =
+ inForImport ? g_AllowedImportTypes[idx] : g_AllowedFileReferencesTypes[idx];
+ if (inFileTypeFilter == Q3DStudio::DocumentEditorFileType::Unknown
+ || inFileTypeFilter == entry.m_FileType) {
+ QString theTypeString(::LoadResourceString(entry.m_ResourceStringId).toQString());
+ QString theExtensions(CreateExtensionsList(entry.m_FileExtensions).toQString());
+ theReturnString += theTypeString + " (" + theExtensions + ");;";
+ }
+ }
+ return theReturnString;
+}
+
+//==============================================================================
+/**
+ * Display a error dialog box with the given text string that describes the error.
+ */
+void CDialogs::DisplayKnownErrorDialog(const Q3DStudio::CString &inErrorText)
+{
+ if (inErrorText.Length() > 0) // make sure this is valid
+ {
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_PROJNAME));
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, inErrorText, CUICMessageBox::ICON_ERROR, false,
+ qApp->activeWindow());
+ else
+ std::cout << inErrorText.GetCharStar() << ": " << inErrorText.GetCharStar() << std::endl;
+ }
+}
+
+//==============================================================================
+/**
+ * Prompt the user to save the document before losing their changes.
+ * This is used when closing, loading or newing up a document when the current
+ * one has modifications.
+ * @return the user's choice.
+ */
+CDialogs::ESavePromptResult CDialogs::PromptForSave()
+{
+ Q3DStudio::CString theDocTitle;
+
+ CUICFile theCurrentDoc = g_StudioApp.GetCore()->GetDoc()->GetDocumentPath();
+ if (theCurrentDoc.IsFile())
+ theDocTitle = theCurrentDoc.GetName();
+ else // if the current doc has not been saved then use the default title.
+ theDocTitle = ::LoadResourceString(IDS_UNTITLED_DOCUMENT_TITLE);
+
+ Q3DStudio::CString thePrompt = ::LoadResourceString(IDS_PROMPT_FOR_SAVE);
+ thePrompt.Format(thePrompt, static_cast<const wchar_t *>(theDocTitle));
+
+ int theChoice = QMessageBox::warning(nullptr, ::LoadResourceString(IDS_PROJNAME).toQString(),
+ thePrompt.toQString(), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
+
+ ESavePromptResult theResult = CANCEL_OPERATION;
+
+ switch (theChoice) {
+ case QMessageBox::Yes:
+ theResult = SAVE_FIRST;
+ break;
+ case QMessageBox::No:
+ theResult = CONTINUE_NO_SAVE;
+ break;
+ case QMessageBox::Cancel:
+ theResult = CANCEL_OPERATION;
+ break;
+ default:
+ break;
+ }
+
+ return theResult;
+}
+
+//==============================================================================
+/**
+ * Prompt the user for a file to save to from the SaveAs menu option.
+ * @return an invalid file if the user cancels the save dialog.
+ */
+std::pair<CUICFile, bool> CDialogs::GetSaveAsChoice(const Q3DStudio::CString &inDialogTitle,
+ bool inFilenameUntitled)
+{
+ CUICFile theFile("");
+ Q3DStudio::CString theFileExt;
+ Q3DStudio::CString theImportFilter;
+
+ Q3DStudio::CString theFilename = g_StudioApp.GetCore()->GetDoc()->GetDocumentPath().GetAbsolutePath();
+
+ if (theFilename == "" || inFilenameUntitled)
+ theFilename = ::LoadResourceString(IDS_UNTITLED_DOCUMENT_TITLE);
+
+ theFileExt = ::LoadResourceString(IDS_FILE_EXT_UIP);
+ theImportFilter = ::LoadResourceString(IDS_FILE_DESC_UIP);
+
+ theImportFilter += " (*" + theFileExt + ")|*" + theFileExt + "|";
+
+ QFileDialog theFileDlg;
+ theFileDlg.setOption(QFileDialog::DontConfirmOverwrite);
+ const QFileInfo fi(m_LastSaveFile.toQString());
+ theFileDlg.setDirectory((fi.path() == QLatin1Char('.')) ? QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) : fi.path());
+ theFileDlg.setAcceptMode(QFileDialog::AcceptSave);
+ theFileDlg.setDefaultSuffix(theFileExt.toQString());
+ if (inDialogTitle != "")
+ theFileDlg.setWindowTitle(inDialogTitle.toQString());
+
+ bool theCreateDir = false;
+ bool theShowDialog = true;
+
+ while (theShowDialog && theFileDlg.exec()) {
+ theShowDialog = false;
+ theFile = CUICFile(Q3DStudio::CString::fromQString(theFileDlg.selectedFiles().front()));
+
+ m_LastSaveFile = theFile.GetAbsolutePath();
+ // customising a dialog box will force us to use non-native.
+ // defaulting this for now, until we can agree a better workflow for
+ // creating new projects
+ theCreateDir = true;
+
+ if (theCreateDir) {
+ // If user checks "Create directory for project"
+ // we need to check manually if the file in the directory already exists (because the
+ // default file dialog can't do that for you)
+ // If the file exists, show warning message if user wants to overwrite the file
+ // This is to fix bug #6315: Create new project in folder with same name will overwrite
+ // existing file without warning
+ Q3DStudio::CFilePath theFinalDir;
+ Q3DStudio::CFilePath theFinalDoc;
+ g_StudioApp.GetCore()->GetCreateDirectoryFileName(theFile, theFinalDir, theFinalDoc);
+
+ // Update last save file to final doc
+ m_LastSaveFile = theFinalDoc;
+ if (theFinalDoc.Exists()) {
+ const QString theTitle(QObject::tr("Confirm Save As"));
+ const QString filePath(theFinalDir.GetFileName().toQString() + QDir::separator() + theFinalDoc.GetFileName().toQString());
+ const QString theString = QObject::tr("%1 already exists.\nDo you want to replace it?").arg(filePath);
+
+ auto result = QMessageBox::question(nullptr, theTitle, theString);
+ if (result != QMessageBox::Yes) {
+ // Reset the file and show the file dialog again
+ theFile = CUICFile("");
+ theShowDialog = true;
+ continue;
+ }
+ } // of over-writing case
+ }
+ }
+
+ return std::make_pair(theFile, theCreateDir);
+}
+
+//==============================================================================
+/**
+ * Prompt the user for a file to create.
+ * @return an invalid file if the user cancels the save dialog.
+ */
+std::pair<CUICFile, bool>
+CDialogs::GetNewDocumentChoice(const Q3DStudio::CString &inInitialDirectory)
+{
+ if (inInitialDirectory.size())
+ m_LastSaveFile = inInitialDirectory;
+ return GetSaveAsChoice(::LoadResourceString(IDS_CREATE_NEW_DOCUMENT_TITLE), true);
+}
+
+//==============================================================================
+/**
+ * Prompt the user for a file to open.
+ * This will return an invalid file if the user cancels the save dialog.
+ */
+CUICFile CDialogs::GetFileOpenChoice(const Q3DStudio::CString &inInitialDirectory)
+{
+ CUICFile theFile("");
+ Q3DStudio::CString theFileExt;
+ Q3DStudio::CString theImportFilter;
+
+ theFileExt = ::LoadResourceString(IDS_FILE_EXT_UIP);
+ theImportFilter = ::LoadResourceString(IDS_FILE_DESC_UIP);
+
+ theImportFilter += " (*" + theFileExt + ")";
+
+ QFileDialog theFileDlg(qApp->activeWindow(), QString(), inInitialDirectory.toQString(), theImportFilter.toQString());
+ theFileDlg.setAcceptMode(QFileDialog::AcceptOpen);
+
+ if (theFileDlg.exec() == QDialog::Accepted) {
+ QFileInfo fi(theFileDlg.selectedFiles().first());
+ Q3DStudio::CString thePath = Q3DStudio::CString::fromQString(fi.absolutePath());
+ Q3DStudio::CString theName = Q3DStudio::CString::fromQString(fi.fileName());
+ theFile = CUICFile(thePath, theName);
+
+ m_LastSaveFile = theFile.GetAbsolutePath();
+ }
+
+ return theFile;
+}
+
+//==============================================================================
+/**
+ * Prompt the user to make sure they want to revert the current project.
+ * @return true if they do want to continue with the revert.
+ */
+bool CDialogs::ConfirmRevert()
+{
+ bool theConfirmation = false;
+ Q3DStudio::CString thePrompt = ::LoadResourceString(IDS_PROMPT_FOR_REVERT);
+ Q3DStudio::CString theTitle = ::LoadResourceString(IDS_PROJNAME);
+
+ CUICMessageBox::EMessageBoxReturn theChoice;
+
+ if (m_ShowGUI)
+ theChoice = CUICMessageBox::Show(theTitle, thePrompt, CUICMessageBox::ICON_WARNING, true,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << thePrompt.GetCharStar() << std::endl;
+ theChoice = CUICMessageBox::MSGBX_OK;
+ }
+
+ // user decided to go ahead and delete all unused resources
+ if (theChoice == CUICMessageBox::MSGBX_OK)
+ theConfirmation = true;
+
+ return theConfirmation;
+}
+
+//==============================================================================
+/**
+ * Displays a progress screen, if there is not one aleady being shown. The
+ * progress screen doesn't get dismissed until you call
+ * CDialogs::DestroyProgressScreen().
+ * @param inActionText text to be displayed above the file name on the screen
+ * @param inFileName name of the file that you are loading
+ * @param inWindowTitle the window's title
+ */
+void CDialogs::DisplayProgressScreen(const Q3DStudio::CString &inActionText,
+ const Q3DStudio::CString &inFileName,
+ const Q3DStudio::CString &inWindowTitle)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ if (m_ShowGUI && (!m_ProgressPalette || !m_ProgressPalette->GetSafeHwnd())) {
+ m_ProgressPalette = new CProgressPalette();
+ CViews::CreateDialogAndView(RUNTIME_CLASS(CProgressView), m_ProgressPalette,
+ (CFrameWnd *)AfxGetMainWnd(), inWindowTitle);
+ m_ProgressPalette->ShowDialog();
+ m_ProgressPalette->InitialUpdate();
+ m_ProgressPalette->SetActionText(inActionText);
+ m_ProgressPalette->SetFileName(inFileName);
+ m_ProgressPalette->RedrawWindow(nullptr, nullptr, RDW_UPDATENOW);
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Retrieve the pointer to the progress screen.
+ * @return m_ProgressPalette
+ */
+IProgressCallback *CDialogs::GetProgressScreen() const
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ return m_ProgressPalette;
+#endif
+ return nullptr;
+}
+
+//==============================================================================
+/**
+ * If a loading screen is currently being shown, this function destroys it. You
+ * can show the loading screen again with another call to
+ * CDialogs::DisplayLoadingScreen().
+ */
+void CDialogs::DestroyProgressScreen()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ if (m_ShowGUI && (m_ProgressPalette && m_ProgressPalette->GetSafeHwnd())) {
+ // Don't call delete on m_ProgressPalette. You should only call DestroyWindow on
+ // CFrameWnds. See the MSDN.
+ m_ProgressPalette->DestroyWindow();
+ m_ProgressPalette = nullptr;
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Prompt the user for the Object Timebar color.
+ * @param ioColor
+ * @return true if we successfully obtained a color using the displayed dialog.
+ */
+bool CDialogs::PromptObjectTimebarColor(CColor &ioColor)
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ std::auto_ptr<CColorDialog> theColorDlg(new CColorDialog(ioColor, CC_SOLIDCOLOR | CC_FULLOPEN));
+ ::CColor theSelectedColor;
+ if (theColorDlg->DoModal() == IDOK) {
+ ioColor = theColorDlg->GetColor();
+ return true;
+ }
+ return false;
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * Display statistics from profiling.
+ */
+void CDialogs::DisplayProfilingStatistics()
+{
+ Q3DStudio::CString theStatistics = "<Not Enabled>";
+#ifdef KDAB_TEMPORARILY_REMOVED
+#ifdef PERFORM_PROFILE
+ CMasterProf *theMasterP = CMasterProf::GetInstance();
+
+ for (long theIndex = 0; theIndex < theMasterP->GetProfilerCount(); ++theIndex) {
+ CMethProf *theProf = theMasterP->GetProfiler(theIndex);
+ if (theProf->GetCount() > 0) {
+ theStatistics += theProf->GetDescription();
+ theStatistics += "\n";
+ }
+ }
+#endif // #ifdef PERFORM_PROFILE
+ if (m_ShowGUI)
+ CUICMessageBox::Show("Profiling Statistics", theStatistics, CUICMessageBox::ICON_INFO);
+ else {
+ std::cout << "Profiling Statistics"
+ << ": " << theStatistics.GetMulti() << std::endl;
+ }
+#endif
+}
+
+//==============================================================================
+/**
+ * Inform the user that the environment variables entered does not match the format
+ * expected, listing down all those settings that are wrong.
+ * @param inErrorMessage the listing of all those errors.
+ */
+void CDialogs::DisplayEnvironmentVariablesError(const Q3DStudio::CString &inErrorMessage)
+{
+ Q3DStudio::CString theTitle = ::LoadResourceString(IDS_ERROR_PROJECT_VARIABLES_TITLE);
+ Q3DStudio::CString theMessage = ::LoadResourceString(IDS_ERROR_PROJECT_VARIABLES_MSG);
+ theMessage += inErrorMessage;
+ theMessage += ::LoadResourceString(IDS_PROJECT_VARIABLES_FORMAT);
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theMessage, CUICMessageBox::ICON_ERROR, false,
+ qApp->activeWindow());
+ else {
+ std::cout << theTitle.GetCharStar() << ": " << theMessage.GetCharStar() << std::endl;
+ }
+}
+
+//==============================================================================
+/**
+ * Reset settings.
+ * Typically inCurrentDocPath is only set when Studio is first launched.
+ * @param inCurrentDocPath the current document path, if any. Application directory if
+ *there is none.
+ */
+void CDialogs::ResetSettings(const Q3DStudio::CString &inCurrentDocPath)
+{
+ // Initialize the default dir/paths to the current document path if specified, otherwise leave
+ // everything as it is.
+ if (!inCurrentDocPath.IsEmpty()) {
+ m_LastSaveFile = inCurrentDocPath;
+ }
+}
+
+bool CDialogs::DisplayResetKeyframeValuesDlg()
+{
+#ifdef KDAB_TEMPORARILY_REMOVED
+ CResetKeyframeValuesDlg theDialog;
+ return theDialog.DoModal() == IDOK;
+#endif
+ return false;
+}
+
+//==============================================================================
+/**
+ * User trying to do a pathological paste, such as pasting a component copied from a different
+ *instance
+ * of Studio into an instance of the same component that already exists in the current instance
+ *of Studio, and
+ * is potentially replaced and deleted.
+ */
+void CDialogs::DisplayPasteFailed()
+{
+ Q3DStudio::CString theTitle(::LoadResourceString(IDS_ERROR_PATHOLOGICAL_PASTE_TITLE));
+ Q3DStudio::CString theMessage(::LoadResourceString(IDS_ERROR_PATHOLOGICAL_PASTE_MESSAGE));
+
+ if (m_ShowGUI)
+ CUICMessageBox::Show(theTitle, theMessage, CUICMessageBox::ICON_ERROR, false,
+ qApp->activeWindow());
+ else
+ std::cout << theTitle.GetCharStar() << ": " << theMessage.GetCharStar() << std::endl;
+}
+
+//==============================================================================
+/**
+ * Video card OpenGL version is too low to be supported.
+ */
+void CDialogs::DisplayGLVersionError(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inMinVersion)
+{
+ DisplayGLVersionDialog(inGLVersion, inMinVersion, true);
+}
+
+//==============================================================================
+/**
+ * Video card OpenGL version is outdated, but could be usable.
+ */
+void CDialogs::DisplayGLVersionWarning(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inRecommendedVersion)
+{
+ DisplayGLVersionDialog(inGLVersion, inRecommendedVersion, false);
+}
+
+//==============================================================================
+/**
+ * Display the error dialog or warning dialog that OpenGL version is lower than what is
+ *expected
+ */
+void CDialogs::DisplayGLVersionDialog(const Q3DStudio::CString &inGLVersion,
+ const Q3DStudio::CString &inRecommendedVersion, bool inError)
+{
+ long theTitleResourceID;
+ long theMessageResourceID;
+
+ if (inError) {
+ theTitleResourceID = IDS_ERROR_MSGTITLE;
+ theMessageResourceID = IDS_GL_VERSION_ERROR;
+ } else {
+ theTitleResourceID = IDS_WARNING_MSGTITLE;
+ theMessageResourceID = IDS_GL_VERSION_WARNING;
+ }
+
+#ifdef KDAB_TEMPORARILY_REMOVED
+ Q3DStudio::CString theTitle(::LoadResourceString(theTitleResourceID));
+ Q3DStudio::CString theMessage(::LoadResourceString(theMessageResourceID));
+ Q3DStudio::CString theFormattedMessage;
+ theFormattedMessage.Format(theMessage, static_cast<const wchar_t *>(inGLVersion),
+ static_cast<const wchar_t *>(inRecommendedVersion));
+
+ CGLVersionDlg theGLVersionDlg;
+ theGLVersionDlg.Initialize(theTitle, theFormattedMessage, inError);
+ theGLVersionDlg.DoModal();
+
+ if (theGLVersionDlg.GetDontShowAgain())
+ CStudioPreferences::SetDontShowGLVersionDialog(true);
+#endif
+}
diff --git a/src/Authoring/Studio/_Win/Workspace/Views.cpp b/src/Authoring/Studio/_Win/Workspace/Views.cpp
new file mode 100644
index 00000000..50ab6f62
--- /dev/null
+++ b/src/Authoring/Studio/_Win/Workspace/Views.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2002 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Prefix
+//==============================================================================
+#include "stdafx.h"
+#include "Views.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "Doc.h"
+#endif
+#include "MainFrm.h"
+#ifdef KDAB_TEMPORARILY_REMOVED
+#include "TimelineTimelineLayout.h"
+#include "ScalableScroller.h"
+#include "PaletteManager.h"
+#include "HotKeys.h"
+#include "Dispatch.h"
+#include "ActionControl.h"
+#include "StudioPaletteBar.h"
+#endif
+
+//==============================================================================
+// Implementation
+//==============================================================================
+
+//=============================================================================
+/**
+ * Constructor
+ */
+CViews::CViews(CStudioApp * /*inStudioApp*/)
+ : m_MainFrame(nullptr)
+{
+}
+
+//=============================================================================
+/**
+ * Destructor
+ */
+CViews::~CViews()
+{
+ DestroyViews();
+}
+
+//=============================================================================
+/**
+ *
+ */
+void CViews::CreateViews()
+{
+ // To create the main window, this code creates a new frame window
+ // object and then sets it as the application's main window object
+ m_MainFrame = new CMainFrame();
+ m_MainFrame->show();
+#ifdef KDAB_TEMPORARILY_REMOVED
+ // create and load the frame with its resources
+ m_MainFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, nullptr, nullptr);
+
+ m_MainFrame->RestoreLayout();
+#endif
+}
+
+//=============================================================================
+/**
+ *
+ */
+void CViews::DestroyViews()
+{
+ if (m_MainFrame) {
+ // Do NOT use delete to destroy a CFrameWnd object! Use DestroyWindow() instead.
+ m_MainFrame->deleteLater();
+ m_MainFrame = nullptr;
+ }
+}
+
+//==============================================================================
+// Keyboard
+//==============================================================================
+
+//=============================================================================
+/**
+ * Register all Application specific shortcut keys.
+ * Used to map hotkeys that are active through the whole app, not just one
+ * view.
+ * @param inHotKeys the handler to register on.
+ */
+void CViews::RegisterGlobalKeyboardShortcuts(CHotKeys *inHotKeys)
+{
+ if (m_MainFrame)
+ m_MainFrame->RegisterGlobalKeyboardShortcuts(inHotKeys);
+}
+
+//=============================================================================
+/**
+ * NO ONE SHOULD USE THIS FUNCTION OTHER THAN THE MAINFRAME ON WINDOWS
+ */
+CMainFrame *CViews::GetMainFrame()
+{
+ return m_MainFrame;
+}
+
+//=============================================================================
+/**
+ *
+ */
+void CViews::RecheckMainframeSizingMode()
+{
+ if (m_MainFrame != nullptr)
+ m_MainFrame->RecheckSizingMode();
+}
+
+//==============================================================================
+// Static
+//==============================================================================
+
+//=============================================================================
+/**
+ * Creates a palette and the associated view.
+ * @param inClass Should be RUNTIME_CLASS( CCLASS ) where CCLASS is a class that
+ * inherits from MFC::CView (must implement dynamic creation) and is the view
+ * that you want associated with the palette.
+ * @param inPalette Palette that you want the view attached to
+ * @param inParent Parent window or nullptr for top level windows
+ * @param inWindowTitle Title caption to appear on the title bar of the palette
+ * @param inShowWindow true to show the window after creation, false to hide it
+ */
+#ifdef KDAB_TEMPORARILY_REMOVED
+void CViews::CreatePaletteAndView(CRuntimeClass *inClass, CStudioPaletteBar *inPalette,
+ CFrameWnd *inParent, Q3DStudio::CString inWindowTitle)
+{
+ CCreateContext theCreateContext;
+ memset(&theCreateContext, 0, sizeof(CCreateContext));
+ theCreateContext.m_pNewViewClass = inClass;
+ theCreateContext.m_pCurrentFrame = inParent;
+ theCreateContext.m_pCurrentDoc = nullptr;
+
+ inPalette->Create(::CString(inWindowTitle.GetMulti()), &theCreateContext,
+ CPaletteManager::GetUniquePaletteId(), inParent);
+}
+#endif
+
+//=============================================================================
+/**
+ * Creates a dialog and the associated view.
+ * @param inClass Should be RUNTIME_CLASS( CCLASS ) where CCLASS is a class that
+ * inherits from MFC::CView (must implement dynamic creation) and is the view
+ * that you want associated with the palette.
+ * @param inPalette Palette that you want the view attached to
+ * @param inParent Parent window or nullptr for top level windows
+ * @param inWindowTitle Title caption to appear on the title bar of the palette
+ * @param inShowWindow true to show the window after creation, false to hide it
+ */
+#ifdef KDAB_TEMPORARILY_REMOVED
+void CViews::CreateDialogAndView(CRuntimeClass *inClass, CStudioDialog *inDialog,
+ CFrameWnd *inParent, Q3DStudio::CString inWindowTitle)
+{
+ CCreateContext theCreateContext;
+ memset(&theCreateContext, 0, sizeof(CCreateContext));
+ theCreateContext.m_pNewViewClass = inClass;
+ theCreateContext.m_pCurrentFrame = inParent;
+ theCreateContext.m_pCurrentDoc = nullptr;
+
+ inDialog->Create(::CString(inWindowTitle.GetMulti()), &theCreateContext, inParent);
+}
+#endif
diff --git a/src/Authoring/Studio/images.qrc b/src/Authoring/Studio/images.qrc
new file mode 100644
index 00000000..19cabdc1
--- /dev/null
+++ b/src/Authoring/Studio/images.qrc
@@ -0,0 +1,179 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/Action-MasterAction.png</file>
+ <file>images/Action-Trash-Disabled.png</file>
+ <file>images/Action-Trash-Disabled@2x.png</file>
+ <file>images/Action-Trash-Normal.png</file>
+ <file>images/Action-Trash-Normal@2x.png</file>
+ <file>images/add.png</file>
+ <file>images/add@2x.png</file>
+ <file>images/arrow.png</file>
+ <file>images/arrow@2x.png</file>
+ <file>images/arrow_down.png</file>
+ <file>images/arrow_down@2x.png</file>
+ <file>images/Asset-Alias-Normal.png</file>
+ <file>images/Asset-Alias-Normal@2x.png</file>
+ <file>images/Asset-Camera-Normal.png</file>
+ <file>images/Asset-Camera-Normal@2x.png</file>
+ <file>images/Asset-Component-Normal.png</file>
+ <file>images/Asset-Component-Normal@2x.png</file>
+ <file>images/Asset-Cone-Normal.png</file>
+ <file>images/Asset-Cone-Normal@2x.png</file>
+ <file>images/Asset-Cube-Normal.png</file>
+ <file>images/Asset-Cube-Normal@2x.png</file>
+ <file>images/Asset-Cylinder-Normal.png</file>
+ <file>images/Asset-Cylinder-Normal@2x.png</file>
+ <file>images/Asset-Group-Normal.png</file>
+ <file>images/Asset-Group-Normal@2x.png</file>
+ <file>images/Asset-Layer-Normal.png</file>
+ <file>images/Asset-Layer-Normal@2x.png</file>
+ <file>images/Asset-Light-Normal.png</file>
+ <file>images/Asset-Light-Normal@2x.png</file>
+ <file>images/Asset-Rectangle-Normal.png</file>
+ <file>images/Asset-Rectangle-Normal@2x.png</file>
+ <file>images/Asset-Sphere-Normal.png</file>
+ <file>images/Asset-Sphere-Normal@2x.png</file>
+ <file>images/Asset-Text-Normal.png</file>
+ <file>images/Asset-Text-Normal@2x.png</file>
+ <file>images/checkbox-checked.png</file>
+ <file>images/checkbox-unchecked.png</file>
+ <file>images/Inspector-AnimateToggle-Active.png</file>
+ <file>images/Inspector-AnimateToggle-Active@2x.png</file>
+ <file>images/Inspector-AnimateToggle-Normal.png</file>
+ <file>images/Inspector-AnimateToggle-Normal@2x.png</file>
+ <file>images/Objects-Behavior-Normal.png</file>
+ <file>images/Objects-Effect-Normal.png</file>
+ <file>images/Objects-Folder-Normal.png</file>
+ <file>images/Objects-Image-Normal.png</file>
+ <file>images/Objects-Layer-Normal.png</file>
+ <file>images/Objects-Layer-Normal@2x.png</file>
+ <file>images/Objects-Material-Normal.png</file>
+ <file>images/Objects-Model-Normal.png</file>
+ <file>images/Objects-Text-Normal.png</file>
+ <file>images/Objects-Text-Normal@2x.png</file>
+ <file>images/Slide-Active.png</file>
+ <file>images/Slide-Active@2x.png</file>
+ <file>images/Slide-Master-Active.png</file>
+ <file>images/Slide-Normal.png</file>
+ <file>images/Objects-Group-Normal.png</file>
+ <file>images/Objects-Group-Normal@2x.png</file>
+ <file>images/Objects-Light-Normal.png</file>
+ <file>images/Objects-Light-Normal@2x.png</file>
+ <file>images/Objects-Property-Normal.png</file>
+ <file>images/Objects-Property-Normal@2x.png</file>
+ <file>images/filter-shy-down.png</file>
+ <file>images/filter-shy-down@2x.png</file>
+ <file>images/filter-toggle-eye-down.png</file>
+ <file>images/filter-toggle-eye-down@2x.png</file>
+ <file>images/filter-toggle-eye-up.png</file>
+ <file>images/filter-toggle-eye-up@2x.png</file>
+ <file>images/PlaybackHead.png</file>
+ <file>images/PlaybackHead@2x.png</file>
+ <file>images/Toggle-HideShow.png</file>
+ <file>images/Toggle-HideShow@2x.png</file>
+ <file>images/obsolete_placeholder.png</file>
+ <file>images/empty-pixel.png</file>
+ <file>images/Toggle-Lock.png</file>
+ <file>images/Toggle-Lock@2x.png</file>
+ <file>images/Toggle-Shy.png</file>
+ <file>images/Toggle-Shy@2x.png</file>
+ <file>images/Toggle-Empty.png</file>
+ <file>images/Objects-Scene-Normal.png</file>
+ <file>images/Objects-Scene-Normal@2x.png</file>
+ <file>images/Action-Action.png</file>
+ <file>images/Action-ChildAction.png</file>
+ <file>images/Objects-Camera-Normal.png</file>
+ <file>images/timebarhandle-disabled-left.png</file>
+ <file>images/timebarhandle-disabled-right.png</file>
+ <file>images/timebarhandle-left.png</file>
+ <file>images/timebarhandle-right.png</file>
+ <file>images/Toggle-HideShow-disabled.png</file>
+ <file>images/Toggle-HideShow-disabled@2x.png</file>
+ <file>images/Objects-Alias-Normal.png</file>
+ <file>images/Objects-Alias-Normal@2x.png</file>
+ <file>images/Objects-Behavior-Normal@2x.png</file>
+ <file>images/Objects-Camera-Normal@2x.png</file>
+ <file>images/Objects-Component-Normal.png</file>
+ <file>images/Objects-Component-Normal@2x.png</file>
+ <file>images/Objects-Effect-Normal@2x.png</file>
+ <file>images/Objects-Folder-Normal@2x.png</file>
+ <file>images/Objects-Image-Normal@2.png</file>
+ <file>images/Objects-Material-Normal@2x.png</file>
+ <file>images/Objects-Model-Normal@2x.png</file>
+ <file>images/scrollbar-arrows-down-depressed.png</file>
+ <file>images/scrollbar-arrows-down-disabled.png</file>
+ <file>images/scrollbar-arrows-down-normal.png</file>
+ <file>images/scrollbar-arrows-left-depressed.png</file>
+ <file>images/scrollbar-arrows-left-disabled.png</file>
+ <file>images/scrollbar-arrows-left-normal.png</file>
+ <file>images/scrollbar-arrows-right-depressed.png</file>
+ <file>images/scrollbar-arrows-right-disabled.png</file>
+ <file>images/scrollbar-arrows-right-normal.png</file>
+ <file>images/scrollbar-arrows-up-depressed.png</file>
+ <file>images/scrollbar-arrows-up-disabled.png</file>
+ <file>images/scrollbar-arrows-up-normal.png</file>
+ <file>images/Keyframe-Master-Disabled.png</file>
+ <file>images/Keyframe-MasterDynamic-Normal.png</file>
+ <file>images/Keyframe-MasterDynamic-Selected.png</file>
+ <file>images/Keyframe-MasterLeft-disabled.png</file>
+ <file>images/Keyframe-MasterLeftDynamic-Normal.png</file>
+ <file>images/Keyframe-MasterLeftDynamic-Selected.png</file>
+ <file>images/Keyframe-MasterLeft-Normal.png</file>
+ <file>images/Keyframe-MasterLeft-Selected.png</file>
+ <file>images/Keyframe-Master-Normal.png</file>
+ <file>images/Keyframe-MasterRight-disabled.png</file>
+ <file>images/Keyframe-MasterRightDynamic-Normal.png</file>
+ <file>images/Keyframe-MasterRightDynamic-Selected.png</file>
+ <file>images/Keyframe-MasterRight-Normal.png</file>
+ <file>images/Keyframe-MasterRight-Selected.png</file>
+ <file>images/Keyframe-Master-Selected.png</file>
+ <file>images/Keyframe-Property-Disabled.png</file>
+ <file>images/Keyframe-PropertyDynamic-Normal.png</file>
+ <file>images/Keyframe-PropertyDynamic-Selected.png</file>
+ <file>images/Keyframe-Property-Normal.png</file>
+ <file>images/Keyframe-Property-Selected.png</file>
+ <file>images/Objects-Property-Disabled.png</file>
+ <file>images/Objects-Property-Disabled@2x.png</file>
+ <file>images/Action-ChildMasterAction.png</file>
+ <file>images/Action-ComponentAction.png</file>
+ <file>images/Action-ComponentMasterAction.png</file>
+ <file>images/breadcrumb_component_button.png</file>
+ <file>images/breadcrumb_component_colon_button.png</file>
+ <file>images/breadcrumb_component_grey_button.png</file>
+ <file>images/breadcrumb_component_scene.png</file>
+ <file>images/Insert-Left.png</file>
+ <file>images/Insert-Rearrange-Left.png</file>
+ <file>images/Insert-Rearrange-Right.png</file>
+ <file>images/Insert-Right.png</file>
+ <file>images/Objects-Alias-Disabled.png</file>
+ <file>images/Objects-Behavior-Disabled.png</file>
+ <file>images/Objects-Camera-Disabled.png</file>
+ <file>images/Objects-Component-Disabled.png</file>
+ <file>images/Objects-Effect-Disabled.png</file>
+ <file>images/Objects-Group-Disabled.png</file>
+ <file>images/Objects-Group-Disabled@2x.png</file>
+ <file>images/Objects-Image-Disabled.png</file>
+ <file>images/Objects-Layer-Disabled.png</file>
+ <file>images/Objects-Layer-Disabled@2x.png</file>
+ <file>images/Objects-Light-Disabled.png</file>
+ <file>images/Objects-Light-Disabled@2x.png</file>
+ <file>images/Objects-Material-Disabled.png</file>
+ <file>images/Objects-Model-Disabled.png</file>
+ <file>images/Objects-Scene-Disabled.png</file>
+ <file>images/Objects-Text-Disabled.png</file>
+ <file>images/Objects-Alias-Disabled@2x.png</file>
+ <file>images/Objects-Behavior-Disabled@2x.png</file>
+ <file>images/Objects-Camera-Disabled@2x.png</file>
+ <file>images/Objects-Component-Disabled@2x.png</file>
+ <file>images/Objects-Effect-Disabled@2x.png</file>
+ <file>images/Objects-Folder-Disabled.png</file>
+ <file>images/Objects-Folder-Disabled@2x.png</file>
+ <file>images/Objects-Image-Disabled@2x.png</file>
+ <file>images/Objects-Material-Disabled@2x.png</file>
+ <file>images/Objects-Model-Disabled@2x.png</file>
+ <file>images/Objects-Scene-Disabled@2x.png</file>
+ <file>images/Objects-Text-Disabled@2x.png</file>
+ <file>images/Slide-Master-Active@2x.png</file>
+ <file>images/Slide-Normal@2x.png</file>
+ </qresource>
+</RCC>
diff --git a/src/Authoring/Studio/images/Action-Action.png b/src/Authoring/Studio/images/Action-Action.png
new file mode 100644
index 00000000..4b8d9c8e
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-Action.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-ChildAction.png b/src/Authoring/Studio/images/Action-ChildAction.png
new file mode 100644
index 00000000..6589b104
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-ChildAction.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-ChildMasterAction.png b/src/Authoring/Studio/images/Action-ChildMasterAction.png
new file mode 100644
index 00000000..39159191
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-ChildMasterAction.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-ComponentAction.png b/src/Authoring/Studio/images/Action-ComponentAction.png
new file mode 100644
index 00000000..d7287f46
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-ComponentAction.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-ComponentMasterAction.png b/src/Authoring/Studio/images/Action-ComponentMasterAction.png
new file mode 100644
index 00000000..bb34a3da
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-ComponentMasterAction.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-MasterAction.png b/src/Authoring/Studio/images/Action-MasterAction.png
new file mode 100644
index 00000000..74f06661
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-MasterAction.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-Trash-Disabled.png b/src/Authoring/Studio/images/Action-Trash-Disabled.png
new file mode 100644
index 00000000..c175290d
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-Trash-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-Trash-Disabled@2x.png b/src/Authoring/Studio/images/Action-Trash-Disabled@2x.png
new file mode 100644
index 00000000..2793f0c2
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-Trash-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-Trash-Normal.png b/src/Authoring/Studio/images/Action-Trash-Normal.png
new file mode 100644
index 00000000..616a85a0
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-Trash-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Action-Trash-Normal@2x.png b/src/Authoring/Studio/images/Action-Trash-Normal@2x.png
new file mode 100644
index 00000000..1365907e
--- /dev/null
+++ b/src/Authoring/Studio/images/Action-Trash-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Alias-Normal.png b/src/Authoring/Studio/images/Asset-Alias-Normal.png
new file mode 100644
index 00000000..4a40aecf
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Alias-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Alias-Normal@2x.png b/src/Authoring/Studio/images/Asset-Alias-Normal@2x.png
new file mode 100644
index 00000000..6b14d31f
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Alias-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Camera-Normal.png b/src/Authoring/Studio/images/Asset-Camera-Normal.png
new file mode 100644
index 00000000..faa642c2
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Camera-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Camera-Normal@2x.png b/src/Authoring/Studio/images/Asset-Camera-Normal@2x.png
new file mode 100644
index 00000000..d69675e8
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Camera-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Component-Normal.png b/src/Authoring/Studio/images/Asset-Component-Normal.png
new file mode 100644
index 00000000..9b1fc237
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Component-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Component-Normal@2x.png b/src/Authoring/Studio/images/Asset-Component-Normal@2x.png
new file mode 100644
index 00000000..45b10cb5
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Component-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cone-Normal.png b/src/Authoring/Studio/images/Asset-Cone-Normal.png
new file mode 100644
index 00000000..5b42abe9
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cone-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cone-Normal@2x.png b/src/Authoring/Studio/images/Asset-Cone-Normal@2x.png
new file mode 100644
index 00000000..4edac2fa
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cone-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cube-Normal.png b/src/Authoring/Studio/images/Asset-Cube-Normal.png
new file mode 100644
index 00000000..c858a011
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cube-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cube-Normal@2x.png b/src/Authoring/Studio/images/Asset-Cube-Normal@2x.png
new file mode 100644
index 00000000..07bfc514
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cube-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cylinder-Normal.png b/src/Authoring/Studio/images/Asset-Cylinder-Normal.png
new file mode 100644
index 00000000..99b8b418
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cylinder-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Cylinder-Normal@2x.png b/src/Authoring/Studio/images/Asset-Cylinder-Normal@2x.png
new file mode 100644
index 00000000..887855e9
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Cylinder-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Group-Normal.png b/src/Authoring/Studio/images/Asset-Group-Normal.png
new file mode 100644
index 00000000..4575da6e
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Group-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Group-Normal@2x.png b/src/Authoring/Studio/images/Asset-Group-Normal@2x.png
new file mode 100644
index 00000000..162c008b
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Group-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Layer-Normal.png b/src/Authoring/Studio/images/Asset-Layer-Normal.png
new file mode 100644
index 00000000..e11e3d26
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Layer-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Layer-Normal@2x.png b/src/Authoring/Studio/images/Asset-Layer-Normal@2x.png
new file mode 100644
index 00000000..d479fca7
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Layer-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Light-Normal.png b/src/Authoring/Studio/images/Asset-Light-Normal.png
new file mode 100644
index 00000000..47bc8d89
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Light-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Light-Normal@2x.png b/src/Authoring/Studio/images/Asset-Light-Normal@2x.png
new file mode 100644
index 00000000..d36e0bf8
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Light-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Rectangle-Normal.png b/src/Authoring/Studio/images/Asset-Rectangle-Normal.png
new file mode 100644
index 00000000..dd904770
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Rectangle-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Rectangle-Normal@2x.png b/src/Authoring/Studio/images/Asset-Rectangle-Normal@2x.png
new file mode 100644
index 00000000..1ad5bc31
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Rectangle-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Sphere-Normal.png b/src/Authoring/Studio/images/Asset-Sphere-Normal.png
new file mode 100644
index 00000000..c53885ba
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Sphere-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Sphere-Normal@2x.png b/src/Authoring/Studio/images/Asset-Sphere-Normal@2x.png
new file mode 100644
index 00000000..e14312f1
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Sphere-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Text-Normal.png b/src/Authoring/Studio/images/Asset-Text-Normal.png
new file mode 100644
index 00000000..0ae00949
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Text-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Asset-Text-Normal@2x.png b/src/Authoring/Studio/images/Asset-Text-Normal@2x.png
new file mode 100644
index 00000000..734900e8
--- /dev/null
+++ b/src/Authoring/Studio/images/Asset-Text-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Insert-Left.png b/src/Authoring/Studio/images/Insert-Left.png
new file mode 100644
index 00000000..14871c90
--- /dev/null
+++ b/src/Authoring/Studio/images/Insert-Left.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Insert-Rearrange-Left.png b/src/Authoring/Studio/images/Insert-Rearrange-Left.png
new file mode 100644
index 00000000..7a0d2703
--- /dev/null
+++ b/src/Authoring/Studio/images/Insert-Rearrange-Left.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Insert-Rearrange-Right.png b/src/Authoring/Studio/images/Insert-Rearrange-Right.png
new file mode 100644
index 00000000..58bbc0c8
--- /dev/null
+++ b/src/Authoring/Studio/images/Insert-Rearrange-Right.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Insert-Right.png b/src/Authoring/Studio/images/Insert-Right.png
new file mode 100644
index 00000000..4393a84a
--- /dev/null
+++ b/src/Authoring/Studio/images/Insert-Right.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Inspector-AnimateToggle-Active.png b/src/Authoring/Studio/images/Inspector-AnimateToggle-Active.png
new file mode 100644
index 00000000..1ed0e731
--- /dev/null
+++ b/src/Authoring/Studio/images/Inspector-AnimateToggle-Active.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Inspector-AnimateToggle-Active@2x.png b/src/Authoring/Studio/images/Inspector-AnimateToggle-Active@2x.png
new file mode 100644
index 00000000..ef83fea8
--- /dev/null
+++ b/src/Authoring/Studio/images/Inspector-AnimateToggle-Active@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal.png b/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal.png
new file mode 100644
index 00000000..ff00f71c
--- /dev/null
+++ b/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal@2x.png b/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal@2x.png
new file mode 100644
index 00000000..dc1cee05
--- /dev/null
+++ b/src/Authoring/Studio/images/Inspector-AnimateToggle-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Master-Disabled.png b/src/Authoring/Studio/images/Keyframe-Master-Disabled.png
new file mode 100644
index 00000000..7e66cd27
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Master-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Master-Normal.png b/src/Authoring/Studio/images/Keyframe-Master-Normal.png
new file mode 100644
index 00000000..9fb6281b
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Master-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Master-Selected.png b/src/Authoring/Studio/images/Keyframe-Master-Selected.png
new file mode 100644
index 00000000..5d3806ba
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Master-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png
new file mode 100644
index 00000000..919c310f
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png
new file mode 100644
index 00000000..7712d96f
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterLeft-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterLeft-Normal.png
new file mode 100644
index 00000000..bb198c49
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterLeft-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterLeft-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterLeft-Selected.png
new file mode 100644
index 00000000..1fbd3e06
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterLeft-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterLeft-disabled.png b/src/Authoring/Studio/images/Keyframe-MasterLeft-disabled.png
new file mode 100644
index 00000000..46fb034f
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterLeft-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Normal.png
new file mode 100644
index 00000000..2e8a3f1b
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Selected.png
new file mode 100644
index 00000000..51d2be2e
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterLeftDynamic-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterRight-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterRight-Normal.png
new file mode 100644
index 00000000..56bcacff
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterRight-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterRight-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterRight-Selected.png
new file mode 100644
index 00000000..1d98fc59
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterRight-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterRight-disabled.png b/src/Authoring/Studio/images/Keyframe-MasterRight-disabled.png
new file mode 100644
index 00000000..2b1086a7
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterRight-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Normal.png
new file mode 100644
index 00000000..105f61f4
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Selected.png
new file mode 100644
index 00000000..5c706f5c
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-MasterRightDynamic-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Property-Disabled.png b/src/Authoring/Studio/images/Keyframe-Property-Disabled.png
new file mode 100644
index 00000000..87e5dde9
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Property-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Property-Normal.png b/src/Authoring/Studio/images/Keyframe-Property-Normal.png
new file mode 100644
index 00000000..7afc5b09
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Property-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-Property-Selected.png b/src/Authoring/Studio/images/Keyframe-Property-Selected.png
new file mode 100644
index 00000000..7eb45ab3
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-Property-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png
new file mode 100644
index 00000000..2231a10c
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png
new file mode 100644
index 00000000..02186956
--- /dev/null
+++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Alias-Disabled.png b/src/Authoring/Studio/images/Objects-Alias-Disabled.png
new file mode 100644
index 00000000..85e6dd94
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Alias-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Alias-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Alias-Disabled@2x.png
new file mode 100644
index 00000000..6f9dffc3
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Alias-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Alias-Normal.png b/src/Authoring/Studio/images/Objects-Alias-Normal.png
new file mode 100644
index 00000000..c97c38f2
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Alias-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Alias-Normal@2x.png b/src/Authoring/Studio/images/Objects-Alias-Normal@2x.png
new file mode 100644
index 00000000..620608d8
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Alias-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Behavior-Disabled.png b/src/Authoring/Studio/images/Objects-Behavior-Disabled.png
new file mode 100644
index 00000000..a7c71f93
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Behavior-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Behavior-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Behavior-Disabled@2x.png
new file mode 100644
index 00000000..5ded4633
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Behavior-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Behavior-Normal.png b/src/Authoring/Studio/images/Objects-Behavior-Normal.png
new file mode 100644
index 00000000..0aa09e8f
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Behavior-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Behavior-Normal@2x.png b/src/Authoring/Studio/images/Objects-Behavior-Normal@2x.png
new file mode 100644
index 00000000..aa65ef2d
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Behavior-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Camera-Disabled.png b/src/Authoring/Studio/images/Objects-Camera-Disabled.png
new file mode 100644
index 00000000..5d87e29a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Camera-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Camera-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Camera-Disabled@2x.png
new file mode 100644
index 00000000..47efdc5c
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Camera-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Camera-Normal.png b/src/Authoring/Studio/images/Objects-Camera-Normal.png
new file mode 100644
index 00000000..69f6e385
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Camera-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Camera-Normal@2x.png b/src/Authoring/Studio/images/Objects-Camera-Normal@2x.png
new file mode 100644
index 00000000..2c8bc946
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Camera-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Component-Disabled.png b/src/Authoring/Studio/images/Objects-Component-Disabled.png
new file mode 100644
index 00000000..f417a7a2
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Component-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Component-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Component-Disabled@2x.png
new file mode 100644
index 00000000..bfb5d2d2
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Component-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Component-Normal.png b/src/Authoring/Studio/images/Objects-Component-Normal.png
new file mode 100644
index 00000000..c95f7b4d
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Component-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Component-Normal@2x.png b/src/Authoring/Studio/images/Objects-Component-Normal@2x.png
new file mode 100644
index 00000000..e852f0a7
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Component-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Effect-Disabled.png b/src/Authoring/Studio/images/Objects-Effect-Disabled.png
new file mode 100644
index 00000000..0b0bde6e
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Effect-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Effect-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Effect-Disabled@2x.png
new file mode 100644
index 00000000..34eb98d8
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Effect-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Effect-Normal.png b/src/Authoring/Studio/images/Objects-Effect-Normal.png
new file mode 100644
index 00000000..d54948a4
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Effect-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Effect-Normal@2x.png b/src/Authoring/Studio/images/Objects-Effect-Normal@2x.png
new file mode 100644
index 00000000..c2edac40
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Effect-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Folder-Disabled.png b/src/Authoring/Studio/images/Objects-Folder-Disabled.png
new file mode 100644
index 00000000..67585cd3
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Folder-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Folder-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Folder-Disabled@2x.png
new file mode 100644
index 00000000..fc1fadc8
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Folder-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Folder-Normal.png b/src/Authoring/Studio/images/Objects-Folder-Normal.png
new file mode 100644
index 00000000..2cc95c46
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Folder-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Folder-Normal@2x.png b/src/Authoring/Studio/images/Objects-Folder-Normal@2x.png
new file mode 100644
index 00000000..44c2e3b6
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Folder-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Group-Disabled.png b/src/Authoring/Studio/images/Objects-Group-Disabled.png
new file mode 100644
index 00000000..0e4e6eaf
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Group-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Group-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Group-Disabled@2x.png
new file mode 100644
index 00000000..2feb6c6b
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Group-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Group-Normal.png b/src/Authoring/Studio/images/Objects-Group-Normal.png
new file mode 100644
index 00000000..3c22bb61
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Group-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Group-Normal@2x.png b/src/Authoring/Studio/images/Objects-Group-Normal@2x.png
new file mode 100644
index 00000000..286be9b8
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Group-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Image-Disabled.png b/src/Authoring/Studio/images/Objects-Image-Disabled.png
new file mode 100644
index 00000000..34654dae
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Image-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Image-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Image-Disabled@2x.png
new file mode 100644
index 00000000..2f1dfd5e
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Image-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Image-Normal.png b/src/Authoring/Studio/images/Objects-Image-Normal.png
new file mode 100644
index 00000000..3d657b8a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Image-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Image-Normal@2.png b/src/Authoring/Studio/images/Objects-Image-Normal@2.png
new file mode 100644
index 00000000..c99df5d2
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Image-Normal@2.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Layer-Disabled.png b/src/Authoring/Studio/images/Objects-Layer-Disabled.png
new file mode 100644
index 00000000..c52e0340
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Layer-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Layer-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Layer-Disabled@2x.png
new file mode 100644
index 00000000..6fa6f7aa
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Layer-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Layer-Normal.png b/src/Authoring/Studio/images/Objects-Layer-Normal.png
new file mode 100644
index 00000000..d06bb2cd
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Layer-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Layer-Normal@2x.png b/src/Authoring/Studio/images/Objects-Layer-Normal@2x.png
new file mode 100644
index 00000000..532764ab
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Layer-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Light-Disabled.png b/src/Authoring/Studio/images/Objects-Light-Disabled.png
new file mode 100644
index 00000000..9fea9d3f
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Light-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Light-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Light-Disabled@2x.png
new file mode 100644
index 00000000..d476c9b3
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Light-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Light-Normal.png b/src/Authoring/Studio/images/Objects-Light-Normal.png
new file mode 100644
index 00000000..68fec179
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Light-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Light-Normal@2x.png b/src/Authoring/Studio/images/Objects-Light-Normal@2x.png
new file mode 100644
index 00000000..4b627f1d
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Light-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Material-Disabled.png b/src/Authoring/Studio/images/Objects-Material-Disabled.png
new file mode 100644
index 00000000..ec77957f
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Material-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Material-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Material-Disabled@2x.png
new file mode 100644
index 00000000..f9b216d0
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Material-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Material-Normal.png b/src/Authoring/Studio/images/Objects-Material-Normal.png
new file mode 100644
index 00000000..225bb4b5
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Material-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Material-Normal@2x.png b/src/Authoring/Studio/images/Objects-Material-Normal@2x.png
new file mode 100644
index 00000000..d51fd360
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Material-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Model-Disabled.png b/src/Authoring/Studio/images/Objects-Model-Disabled.png
new file mode 100644
index 00000000..ead6ede5
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Model-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Model-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Model-Disabled@2x.png
new file mode 100644
index 00000000..8c0f635a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Model-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Model-Normal.png b/src/Authoring/Studio/images/Objects-Model-Normal.png
new file mode 100644
index 00000000..9e066414
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Model-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Model-Normal@2x.png b/src/Authoring/Studio/images/Objects-Model-Normal@2x.png
new file mode 100644
index 00000000..075663bf
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Model-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Property-Disabled.png b/src/Authoring/Studio/images/Objects-Property-Disabled.png
new file mode 100644
index 00000000..84c55833
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Property-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Property-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Property-Disabled@2x.png
new file mode 100644
index 00000000..15428fe9
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Property-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Property-Normal.png b/src/Authoring/Studio/images/Objects-Property-Normal.png
new file mode 100644
index 00000000..a75d0561
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Property-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Property-Normal@2x.png b/src/Authoring/Studio/images/Objects-Property-Normal@2x.png
new file mode 100644
index 00000000..e1cf503a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Property-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Scene-Disabled.png b/src/Authoring/Studio/images/Objects-Scene-Disabled.png
new file mode 100644
index 00000000..a4aba973
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Scene-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Scene-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Scene-Disabled@2x.png
new file mode 100644
index 00000000..2d21542a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Scene-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Scene-Normal.png b/src/Authoring/Studio/images/Objects-Scene-Normal.png
new file mode 100644
index 00000000..56253242
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Scene-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Scene-Normal@2x.png b/src/Authoring/Studio/images/Objects-Scene-Normal@2x.png
new file mode 100644
index 00000000..c963c77b
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Scene-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Text-Disabled.png b/src/Authoring/Studio/images/Objects-Text-Disabled.png
new file mode 100644
index 00000000..f8ba1b9a
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Text-Disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Text-Disabled@2x.png b/src/Authoring/Studio/images/Objects-Text-Disabled@2x.png
new file mode 100644
index 00000000..22cec550
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Text-Disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Text-Normal.png b/src/Authoring/Studio/images/Objects-Text-Normal.png
new file mode 100644
index 00000000..0ae00949
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Text-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Objects-Text-Normal@2x.png b/src/Authoring/Studio/images/Objects-Text-Normal@2x.png
new file mode 100644
index 00000000..734900e8
--- /dev/null
+++ b/src/Authoring/Studio/images/Objects-Text-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/PlaybackHead.png b/src/Authoring/Studio/images/PlaybackHead.png
new file mode 100644
index 00000000..ce001ad2
--- /dev/null
+++ b/src/Authoring/Studio/images/PlaybackHead.png
Binary files differ
diff --git a/src/Authoring/Studio/images/PlaybackHead@2x.png b/src/Authoring/Studio/images/PlaybackHead@2x.png
new file mode 100644
index 00000000..f1a579a7
--- /dev/null
+++ b/src/Authoring/Studio/images/PlaybackHead@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Active.png b/src/Authoring/Studio/images/Slide-Active.png
new file mode 100644
index 00000000..8ee8a8bd
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Active.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Active@2x.png b/src/Authoring/Studio/images/Slide-Active@2x.png
new file mode 100644
index 00000000..8c2031e1
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Active@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Master-Active.png b/src/Authoring/Studio/images/Slide-Master-Active.png
new file mode 100644
index 00000000..0672c3c0
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Master-Active.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Master-Active@2x.png b/src/Authoring/Studio/images/Slide-Master-Active@2x.png
new file mode 100644
index 00000000..43baf38f
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Master-Active@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Normal.png b/src/Authoring/Studio/images/Slide-Normal.png
new file mode 100644
index 00000000..bb44401c
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Slide-Normal@2x.png b/src/Authoring/Studio/images/Slide-Normal@2x.png
new file mode 100644
index 00000000..5f6bf90e
--- /dev/null
+++ b/src/Authoring/Studio/images/Slide-Normal@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-Empty.png b/src/Authoring/Studio/images/Toggle-Empty.png
new file mode 100644
index 00000000..3f6660c9
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-Empty.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-HideShow-disabled.png b/src/Authoring/Studio/images/Toggle-HideShow-disabled.png
new file mode 100644
index 00000000..fa28ed9c
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-HideShow-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-HideShow-disabled@2x.png b/src/Authoring/Studio/images/Toggle-HideShow-disabled@2x.png
new file mode 100644
index 00000000..e75f821f
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-HideShow-disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-HideShow.png b/src/Authoring/Studio/images/Toggle-HideShow.png
new file mode 100644
index 00000000..bfa3c734
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-HideShow.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-HideShow@2x.png b/src/Authoring/Studio/images/Toggle-HideShow@2x.png
new file mode 100644
index 00000000..5425b285
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-HideShow@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-Lock.png b/src/Authoring/Studio/images/Toggle-Lock.png
new file mode 100644
index 00000000..627fa7f7
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-Lock.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-Lock@2x.png b/src/Authoring/Studio/images/Toggle-Lock@2x.png
new file mode 100644
index 00000000..d1a6dfc4
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-Lock@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-Shy.png b/src/Authoring/Studio/images/Toggle-Shy.png
new file mode 100644
index 00000000..2b990ad3
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-Shy.png
Binary files differ
diff --git a/src/Authoring/Studio/images/Toggle-Shy@2x.png b/src/Authoring/Studio/images/Toggle-Shy@2x.png
new file mode 100644
index 00000000..1c9fa9f4
--- /dev/null
+++ b/src/Authoring/Studio/images/Toggle-Shy@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/add.png b/src/Authoring/Studio/images/add.png
new file mode 100644
index 00000000..efd65386
--- /dev/null
+++ b/src/Authoring/Studio/images/add.png
Binary files differ
diff --git a/src/Authoring/Studio/images/add@2x.png b/src/Authoring/Studio/images/add@2x.png
new file mode 100644
index 00000000..98fd8d80
--- /dev/null
+++ b/src/Authoring/Studio/images/add@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/arrow.png b/src/Authoring/Studio/images/arrow.png
new file mode 100644
index 00000000..40ebda88
--- /dev/null
+++ b/src/Authoring/Studio/images/arrow.png
Binary files differ
diff --git a/src/Authoring/Studio/images/arrow@2x.png b/src/Authoring/Studio/images/arrow@2x.png
new file mode 100644
index 00000000..1a21ee06
--- /dev/null
+++ b/src/Authoring/Studio/images/arrow@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/arrow_down.png b/src/Authoring/Studio/images/arrow_down.png
new file mode 100644
index 00000000..238f8b6f
--- /dev/null
+++ b/src/Authoring/Studio/images/arrow_down.png
Binary files differ
diff --git a/src/Authoring/Studio/images/arrow_down@2x.png b/src/Authoring/Studio/images/arrow_down@2x.png
new file mode 100644
index 00000000..831189b6
--- /dev/null
+++ b/src/Authoring/Studio/images/arrow_down@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/breadcrumb_component_button.png b/src/Authoring/Studio/images/breadcrumb_component_button.png
new file mode 100644
index 00000000..4f0cd4b5
--- /dev/null
+++ b/src/Authoring/Studio/images/breadcrumb_component_button.png
Binary files differ
diff --git a/src/Authoring/Studio/images/breadcrumb_component_colon_button.png b/src/Authoring/Studio/images/breadcrumb_component_colon_button.png
new file mode 100644
index 00000000..ff80c8b7
--- /dev/null
+++ b/src/Authoring/Studio/images/breadcrumb_component_colon_button.png
Binary files differ
diff --git a/src/Authoring/Studio/images/breadcrumb_component_grey_button.png b/src/Authoring/Studio/images/breadcrumb_component_grey_button.png
new file mode 100644
index 00000000..0db36558
--- /dev/null
+++ b/src/Authoring/Studio/images/breadcrumb_component_grey_button.png
Binary files differ
diff --git a/src/Authoring/Studio/images/breadcrumb_component_scene.png b/src/Authoring/Studio/images/breadcrumb_component_scene.png
new file mode 100644
index 00000000..f1c68fe6
--- /dev/null
+++ b/src/Authoring/Studio/images/breadcrumb_component_scene.png
Binary files differ
diff --git a/src/Authoring/Studio/images/checkbox-checked.png b/src/Authoring/Studio/images/checkbox-checked.png
new file mode 100644
index 00000000..f13b9aa5
--- /dev/null
+++ b/src/Authoring/Studio/images/checkbox-checked.png
Binary files differ
diff --git a/src/Authoring/Studio/images/checkbox-unchecked.png b/src/Authoring/Studio/images/checkbox-unchecked.png
new file mode 100644
index 00000000..7482c028
--- /dev/null
+++ b/src/Authoring/Studio/images/checkbox-unchecked.png
Binary files differ
diff --git a/src/Authoring/Studio/images/empty-pixel.png b/src/Authoring/Studio/images/empty-pixel.png
new file mode 100644
index 00000000..0ae92115
--- /dev/null
+++ b/src/Authoring/Studio/images/empty-pixel.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-shy-down.png b/src/Authoring/Studio/images/filter-shy-down.png
new file mode 100644
index 00000000..2b990ad3
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-shy-down.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-shy-down@2x.png b/src/Authoring/Studio/images/filter-shy-down@2x.png
new file mode 100644
index 00000000..1c9fa9f4
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-shy-down@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-toggle-eye-down.png b/src/Authoring/Studio/images/filter-toggle-eye-down.png
new file mode 100644
index 00000000..fa28ed9c
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-toggle-eye-down.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-toggle-eye-down@2x.png b/src/Authoring/Studio/images/filter-toggle-eye-down@2x.png
new file mode 100644
index 00000000..e75f821f
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-toggle-eye-down@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-toggle-eye-up.png b/src/Authoring/Studio/images/filter-toggle-eye-up.png
new file mode 100644
index 00000000..bfa3c734
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-toggle-eye-up.png
Binary files differ
diff --git a/src/Authoring/Studio/images/filter-toggle-eye-up@2x.png b/src/Authoring/Studio/images/filter-toggle-eye-up@2x.png
new file mode 100644
index 00000000..5425b285
--- /dev/null
+++ b/src/Authoring/Studio/images/filter-toggle-eye-up@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/images/obsolete_placeholder.png b/src/Authoring/Studio/images/obsolete_placeholder.png
new file mode 100644
index 00000000..4837e5f0
--- /dev/null
+++ b/src/Authoring/Studio/images/obsolete_placeholder.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-down-depressed.png b/src/Authoring/Studio/images/scrollbar-arrows-down-depressed.png
new file mode 100644
index 00000000..3c50a1a0
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-down-depressed.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-down-disabled.png b/src/Authoring/Studio/images/scrollbar-arrows-down-disabled.png
new file mode 100644
index 00000000..be0b4a3d
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-down-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-down-normal.png b/src/Authoring/Studio/images/scrollbar-arrows-down-normal.png
new file mode 100644
index 00000000..9e70abe1
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-down-normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-left-depressed.png b/src/Authoring/Studio/images/scrollbar-arrows-left-depressed.png
new file mode 100644
index 00000000..fde5c4e6
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-left-depressed.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-left-disabled.png b/src/Authoring/Studio/images/scrollbar-arrows-left-disabled.png
new file mode 100644
index 00000000..15fb43a5
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-left-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-left-normal.png b/src/Authoring/Studio/images/scrollbar-arrows-left-normal.png
new file mode 100644
index 00000000..9ecd27d4
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-left-normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-right-depressed.png b/src/Authoring/Studio/images/scrollbar-arrows-right-depressed.png
new file mode 100644
index 00000000..4f473c04
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-right-depressed.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-right-disabled.png b/src/Authoring/Studio/images/scrollbar-arrows-right-disabled.png
new file mode 100644
index 00000000..a32ea1cc
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-right-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-right-normal.png b/src/Authoring/Studio/images/scrollbar-arrows-right-normal.png
new file mode 100644
index 00000000..8ad8f45f
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-right-normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-up-depressed.png b/src/Authoring/Studio/images/scrollbar-arrows-up-depressed.png
new file mode 100644
index 00000000..74060ef8
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-up-depressed.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-up-disabled.png b/src/Authoring/Studio/images/scrollbar-arrows-up-disabled.png
new file mode 100644
index 00000000..ee9fbecc
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-up-disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/images/scrollbar-arrows-up-normal.png b/src/Authoring/Studio/images/scrollbar-arrows-up-normal.png
new file mode 100644
index 00000000..2174ba70
--- /dev/null
+++ b/src/Authoring/Studio/images/scrollbar-arrows-up-normal.png
Binary files differ
diff --git a/src/Authoring/Studio/images/timebarhandle-disabled-left.png b/src/Authoring/Studio/images/timebarhandle-disabled-left.png
new file mode 100644
index 00000000..a5c0e661
--- /dev/null
+++ b/src/Authoring/Studio/images/timebarhandle-disabled-left.png
Binary files differ
diff --git a/src/Authoring/Studio/images/timebarhandle-disabled-right.png b/src/Authoring/Studio/images/timebarhandle-disabled-right.png
new file mode 100644
index 00000000..a23ba5fe
--- /dev/null
+++ b/src/Authoring/Studio/images/timebarhandle-disabled-right.png
Binary files differ
diff --git a/src/Authoring/Studio/images/timebarhandle-left.png b/src/Authoring/Studio/images/timebarhandle-left.png
new file mode 100644
index 00000000..598b6151
--- /dev/null
+++ b/src/Authoring/Studio/images/timebarhandle-left.png
Binary files differ
diff --git a/src/Authoring/Studio/images/timebarhandle-right.png b/src/Authoring/Studio/images/timebarhandle-right.png
new file mode 100644
index 00000000..149d834a
--- /dev/null
+++ b/src/Authoring/Studio/images/timebarhandle-right.png
Binary files differ
diff --git a/src/Authoring/Studio/qt3dstudio.qrc b/src/Authoring/Studio/qt3dstudio.qrc
new file mode 100644
index 00000000..dba77f4a
--- /dev/null
+++ b/src/Authoring/Studio/qt3dstudio.qrc
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/">
+ <file>res/Tutorial/button_back.png</file>
+ <file>res/Tutorial/button_next.png</file>
+ <file>res/Tutorial/screens/1.png</file>
+ <file>res/Tutorial/screens/2.png</file>
+ <file>res/Tutorial/screens/3.png</file>
+ <file>res/Tutorial/screens/4.png</file>
+ <file>res/Tutorial/screens/5.png</file>
+ <file>res/Tutorial/screens/6.png</file>
+ <file>res/Tutorial/screens/7.png</file>
+ <file>res/Tutorial/button_back@2x.png</file>
+ <file>res/Tutorial/button_next@2x.png</file>
+ <file alias="res/strings/Static.stro">English.lproj/Strings/Static.stro</file>
+ </qresource>
+</RCC>
diff --git a/src/Authoring/Studio/remoteproject.cpp b/src/Authoring/Studio/remoteproject.cpp
new file mode 100644
index 00000000..f1b0cfe1
--- /dev/null
+++ b/src/Authoring/Studio/remoteproject.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "remoteproject.h"
+
+#include <QtCore/qpair.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdiriterator.h>
+#include <QtWidgets/qinputdialog.h>
+#include <QtWidgets/qmessagebox.h>
+#include <QtWidgets/qdialog.h>
+#include <QtWidgets/qlabel.h>
+#include <QtWidgets/qgridlayout.h>
+#include <QtWidgets/qdialogbuttonbox.h>
+
+class ConnectionDialog : public QDialog
+{
+public:
+ static QPair<QString, int> getInfo(QWidget *parent);
+
+private:
+ ConnectionDialog(QWidget *parent);
+ QLineEdit *m_hostLineEdit = nullptr;
+ QLineEdit *m_portLineEdit = nullptr;
+};
+
+ConnectionDialog::ConnectionDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ m_hostLineEdit = new QLineEdit(this);
+ QLabel *hostLabel = new QLabel(tr("Address:"));
+ hostLabel->setBuddy(m_hostLineEdit);
+
+ m_portLineEdit = new QLineEdit(this);
+ m_portLineEdit->setText("36000");
+ QLabel *portLabel = new QLabel(tr("Port:"));
+ portLabel->setBuddy(m_portLineEdit);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(
+ QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
+
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+ QGridLayout *mainLayout = new QGridLayout(this);
+ mainLayout->addWidget(hostLabel, 0, 0);
+ mainLayout->addWidget(m_hostLineEdit, 0, 1);
+ mainLayout->addWidget(portLabel, 1, 0);
+ mainLayout->addWidget(m_portLineEdit, 1, 1);
+ mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
+
+ setWindowTitle(tr("Connect to Device"));
+ m_hostLineEdit->setFocus();
+}
+
+QPair<QString, int> ConnectionDialog::getInfo(QWidget *parent)
+{
+ ConnectionDialog dialog(parent);
+ if (!dialog.exec())
+ return QPair<QString, int>();
+
+ return qMakePair(dialog.m_hostLineEdit->text(),
+ dialog.m_portLineEdit->text().toInt());
+}
+
+RemoteProject::RemoteProject(QWidget *parent)
+ : QObject(parent)
+ , m_tcpSocket(0)
+ , m_mainWindow(parent)
+{
+}
+
+void RemoteProject::connect()
+{
+ if (isConnected())
+ return;
+
+ delete m_tcpSocket;
+ m_tcpSocket = new QTcpSocket(this);
+
+ QObject::connect(m_tcpSocket, &QTcpSocket::connected, this,
+ &RemoteProject::checkConnection);
+ QObject::connect(m_tcpSocket, &QTcpSocket::disconnected, this,
+ &RemoteProject::checkConnection);
+ QObject::connect(m_tcpSocket,
+ static_cast<void(QAbstractSocket::*)(QAbstractSocket::SocketError)>
+ (&QAbstractSocket::error),
+ this, &RemoteProject::connectionError);
+
+ QPair<QString, int> info = ConnectionDialog::getInfo(m_mainWindow);
+
+ m_tcpSocket->connectToHost(info.first, info.second);
+ if (!m_tcpSocket->waitForConnected(2000)) {
+ m_tcpSocket->abort();
+ checkConnection();
+ }
+}
+
+void RemoteProject::disconnect()
+{
+ Q_ASSERT(m_tcpSocket);
+ m_tcpSocket->disconnectFromHost();
+}
+
+bool RemoteProject::isConnected() const
+{
+ return m_tcpSocket && m_tcpSocket->state()
+ == QAbstractSocket::ConnectedState;
+}
+
+void RemoteProject::checkConnection()
+{
+ Q_EMIT connectionChanged(isConnected());
+}
+
+void RemoteProject::connectionError()
+{
+ if (m_tcpSocket) {
+ QMessageBox::warning(m_mainWindow, tr("Connect to Device"),
+ tr("Device connection error: ") + m_tcpSocket->errorString());
+ }
+ Q_EMIT connectionChanged(isConnected());
+}
+
+void RemoteProject::streamProject(const QString &projectFile)
+{
+ Q_ASSERT(isConnected());
+ if (!isConnected())
+ return;
+
+ QByteArray block;
+ QDataStream out(&block, QIODevice::WriteOnly);
+ out.setVersion(QDataStream::Qt_5_8);
+
+ const QFileInfo fileInfo(projectFile);
+ if (!fileInfo.exists()) {
+ qWarning() << "failed to find file " << projectFile;
+ return;
+ }
+
+ const QDir projectDirectory(fileInfo.absolutePath());
+
+ // The file to be loaded
+ const QString relativePath
+ = projectDirectory.relativeFilePath(fileInfo.filePath());
+
+ int fileCount = 0;
+ QDirIterator it(fileInfo.absolutePath(), QDir::Files,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ const QString filePath = it.next();
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "could not open file " << filePath;
+ return;
+ }
+
+ fileCount++;
+ const QString relativePath = projectDirectory.relativeFilePath(filePath);
+ const QByteArray payload = file.readAll();
+ out << relativePath;
+ out << payload;
+ }
+
+ QByteArray metaBlock;
+ QDataStream metaOut(&metaBlock, QIODevice::WriteOnly);
+ metaOut.setVersion(QDataStream::Qt_5_8);
+ metaOut << block.size();
+ metaOut << fileCount;
+ metaOut << relativePath;
+
+ m_tcpSocket->write(metaBlock);
+ m_tcpSocket->write(block);
+}
diff --git a/src/Authoring/Studio/remoteproject.h b/src/Authoring/Studio/remoteproject.h
new file mode 100644
index 00000000..9aeb096e
--- /dev/null
+++ b/src/Authoring/Studio/remoteproject.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef REMOTEPROJECT_H
+#define REMOTEPROJECT_H
+
+#include <QtCore/qobject.h>
+#include <QtWidgets/qwidget.h>
+#include <QtNetwork/qtcpsocket.h>
+
+class RemoteProject : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RemoteProject(QWidget *parent);
+
+ void connect();
+ void disconnect();
+ bool isConnected() const;
+
+ void streamProject(const QString &);
+
+public Q_SLOTS:
+ void checkConnection();
+ void connectionError();
+
+Q_SIGNALS:
+ void connectionChanged(bool) const;
+
+private:
+ QTcpSocket *m_tcpSocket;
+ QWidget *m_mainWindow;
+};
+
+#endif // REMOTEPROJECT_H
diff --git a/src/Authoring/Studio/res/About Icon.bmp b/src/Authoring/Studio/res/About Icon.bmp
new file mode 100644
index 00000000..97764f42
--- /dev/null
+++ b/src/Authoring/Studio/res/About Icon.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/Flippy-Right.bmp b/src/Authoring/Studio/res/Flippy-Right.bmp
new file mode 100644
index 00000000..e4471ec6
--- /dev/null
+++ b/src/Authoring/Studio/res/Flippy-Right.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/Flippy-down.bmp b/src/Authoring/Studio/res/Flippy-down.bmp
new file mode 100644
index 00000000..4c1dd0d2
--- /dev/null
+++ b/src/Authoring/Studio/res/Flippy-down.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/Studio.ico b/src/Authoring/Studio/res/Studio.ico
new file mode 100644
index 00000000..0911ac1f
--- /dev/null
+++ b/src/Authoring/Studio/res/Studio.ico
Binary files differ
diff --git a/src/Authoring/Studio/res/Studio.manifest b/src/Authoring/Studio/res/Studio.manifest
new file mode 100644
index 00000000..6386fac9
--- /dev/null
+++ b/src/Authoring/Studio/res/Studio.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="X86"
+ name="Microsoft.Windows.Studio"
+ type="win32"
+/>
+<description>Your app description here</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/src/Authoring/Studio/res/Studio.png b/src/Authoring/Studio/res/Studio.png
new file mode 100644
index 00000000..11c6e526
--- /dev/null
+++ b/src/Authoring/Studio/res/Studio.png
Binary files differ
diff --git a/src/Authoring/Studio/res/StudioDoc.ico b/src/Authoring/Studio/res/StudioDoc.ico
new file mode 100644
index 00000000..0d531110
--- /dev/null
+++ b/src/Authoring/Studio/res/StudioDoc.ico
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-00.png b/src/Authoring/Studio/res/Toolbar-00.png
new file mode 100644
index 00000000..4111f8f0
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-00.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-01.png b/src/Authoring/Studio/res/Toolbar-01.png
new file mode 100644
index 00000000..76e3b5c2
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-01.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-02.png b/src/Authoring/Studio/res/Toolbar-02.png
new file mode 100644
index 00000000..3cd03094
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-02.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-03.png b/src/Authoring/Studio/res/Toolbar-03.png
new file mode 100644
index 00000000..3cde7c2e
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-03.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-04.png b/src/Authoring/Studio/res/Toolbar-04.png
new file mode 100644
index 00000000..3e04587b
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-04.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-05.png b/src/Authoring/Studio/res/Toolbar-05.png
new file mode 100644
index 00000000..8b4dbb5c
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-05.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-06.png b/src/Authoring/Studio/res/Toolbar-06.png
new file mode 100644
index 00000000..b4cdcc3c
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-06.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar-07.png b/src/Authoring/Studio/res/Toolbar-07.png
new file mode 100644
index 00000000..27e904f8
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar-07.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Toolbar.bmp b/src/Authoring/Studio/res/Toolbar.bmp
new file mode 100644
index 00000000..8b7a3424
--- /dev/null
+++ b/src/Authoring/Studio/res/Toolbar.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/button_back.png b/src/Authoring/Studio/res/Tutorial/button_back.png
new file mode 100644
index 00000000..8113df5b
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/button_back.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/button_back@2x.png b/src/Authoring/Studio/res/Tutorial/button_back@2x.png
new file mode 100644
index 00000000..ec6a2f13
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/button_back@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/button_next.png b/src/Authoring/Studio/res/Tutorial/button_next.png
new file mode 100644
index 00000000..857dcada
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/button_next.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/button_next@2x.png b/src/Authoring/Studio/res/Tutorial/button_next@2x.png
new file mode 100644
index 00000000..f32c4f26
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/button_next@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/1.png b/src/Authoring/Studio/res/Tutorial/screens/1.png
new file mode 100644
index 00000000..028f939f
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/1.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/2.png b/src/Authoring/Studio/res/Tutorial/screens/2.png
new file mode 100644
index 00000000..9ba67e3b
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/2.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/3.png b/src/Authoring/Studio/res/Tutorial/screens/3.png
new file mode 100644
index 00000000..849dfce7
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/3.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/4.png b/src/Authoring/Studio/res/Tutorial/screens/4.png
new file mode 100644
index 00000000..4933f401
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/4.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/5.png b/src/Authoring/Studio/res/Tutorial/screens/5.png
new file mode 100644
index 00000000..26adcf4b
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/5.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/6.png b/src/Authoring/Studio/res/Tutorial/screens/6.png
new file mode 100644
index 00000000..25b7a5bf
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/6.png
Binary files differ
diff --git a/src/Authoring/Studio/res/Tutorial/screens/7.png b/src/Authoring/Studio/res/Tutorial/screens/7.png
new file mode 100644
index 00000000..9ed864a3
--- /dev/null
+++ b/src/Authoring/Studio/res/Tutorial/screens/7.png
Binary files differ
diff --git a/src/Authoring/Studio/res/arrow_down.png b/src/Authoring/Studio/res/arrow_down.png
new file mode 100644
index 00000000..b6cfcf77
--- /dev/null
+++ b/src/Authoring/Studio/res/arrow_down.png
Binary files differ
diff --git a/src/Authoring/Studio/res/arrow_down@2x.png b/src/Authoring/Studio/res/arrow_down@2x.png
new file mode 100644
index 00000000..5c409dcb
--- /dev/null
+++ b/src/Authoring/Studio/res/arrow_down@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_grayed_out.bmp b/src/Authoring/Studio/res/client_tools_grayed_out.bmp
new file mode 100644
index 00000000..da15cc18
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_grayed_out.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-00.png b/src/Authoring/Studio/res/client_tools_hi_color-00.png
new file mode 100644
index 00000000..f466a1c3
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-00.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-00@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-00@2x.png
new file mode 100644
index 00000000..0b68e6b3
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-00@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-01.png b/src/Authoring/Studio/res/client_tools_hi_color-01.png
new file mode 100644
index 00000000..aad1b000
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-01.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-01@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-01@2x.png
new file mode 100644
index 00000000..e9589689
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-01@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-02.png b/src/Authoring/Studio/res/client_tools_hi_color-02.png
new file mode 100644
index 00000000..871a5e7d
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-02.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-02@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-02@2x.png
new file mode 100644
index 00000000..235d6804
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-02@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-03.png b/src/Authoring/Studio/res/client_tools_hi_color-03.png
new file mode 100644
index 00000000..68a7848a
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-03.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-03@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-03@2x.png
new file mode 100644
index 00000000..bc861678
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-03@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-04.png b/src/Authoring/Studio/res/client_tools_hi_color-04.png
new file mode 100644
index 00000000..0a2a7bc9
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-04.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-04@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-04@2x.png
new file mode 100644
index 00000000..bdf10c1f
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-04@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-05.png b/src/Authoring/Studio/res/client_tools_hi_color-05.png
new file mode 100644
index 00000000..de315cab
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-05.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-05@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-05@2x.png
new file mode 100644
index 00000000..89ba3fba
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-05@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-06.png b/src/Authoring/Studio/res/client_tools_hi_color-06.png
new file mode 100644
index 00000000..eeb93638
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-06.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color-06@2x.png b/src/Authoring/Studio/res/client_tools_hi_color-06@2x.png
new file mode 100644
index 00000000..5fe71abb
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color-06@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_hi_color.bmp b/src/Authoring/Studio/res/client_tools_hi_color.bmp
new file mode 100644
index 00000000..50f90ab7
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_hi_color.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/client_tools_low_color.bmp b/src/Authoring/Studio/res/client_tools_low_color.bmp
new file mode 100644
index 00000000..7dccd64e
--- /dev/null
+++ b/src/Authoring/Studio/res/client_tools_low_color.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/cursors.bmp b/src/Authoring/Studio/res/cursors.bmp
new file mode 100644
index 00000000..dff7330e
--- /dev/null
+++ b/src/Authoring/Studio/res/cursors.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/edit_camera_pan.png b/src/Authoring/Studio/res/edit_camera_pan.png
new file mode 100644
index 00000000..9248a875
--- /dev/null
+++ b/src/Authoring/Studio/res/edit_camera_pan.png
Binary files differ
diff --git a/src/Authoring/Studio/res/edit_camera_rot.png b/src/Authoring/Studio/res/edit_camera_rot.png
new file mode 100644
index 00000000..55c6ddac
--- /dev/null
+++ b/src/Authoring/Studio/res/edit_camera_rot.png
Binary files differ
diff --git a/src/Authoring/Studio/res/edit_camera_zoom.png b/src/Authoring/Studio/res/edit_camera_zoom.png
new file mode 100644
index 00000000..46f69c43
--- /dev/null
+++ b/src/Authoring/Studio/res/edit_camera_zoom.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_disabled.bmp b/src/Authoring/Studio/res/editcamera_tools_disabled.bmp
new file mode 100644
index 00000000..53709e04
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_disabled.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-00.png b/src/Authoring/Studio/res/editcamera_tools_hi-00.png
new file mode 100644
index 00000000..ad07bf8f
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-00.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-00@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-00@2x.png
new file mode 100644
index 00000000..d8c5fc46
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-00@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled.png
new file mode 100644
index 00000000..f8499e9d
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled@2x.png
new file mode 100644
index 00000000..96564a1c
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-00_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-01.png b/src/Authoring/Studio/res/editcamera_tools_hi-01.png
new file mode 100644
index 00000000..10b16c6d
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-01.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-01@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-01@2x.png
new file mode 100644
index 00000000..b872a674
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-01@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled.png
new file mode 100644
index 00000000..ce5d85d2
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled@2x.png
new file mode 100644
index 00000000..a4a93e53
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-01_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-02.png b/src/Authoring/Studio/res/editcamera_tools_hi-02.png
new file mode 100644
index 00000000..77b5a93e
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-02.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-02@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-02@2x.png
new file mode 100644
index 00000000..13b2ce61
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-02@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled.png
new file mode 100644
index 00000000..a21cea23
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled@2x.png
new file mode 100644
index 00000000..bba1b104
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-02_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-03.png b/src/Authoring/Studio/res/editcamera_tools_hi-03.png
new file mode 100644
index 00000000..c8929b76
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-03.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-03@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-03@2x.png
new file mode 100644
index 00000000..26292608
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-03@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled.png
new file mode 100644
index 00000000..db8944b2
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled@2x.png
new file mode 100644
index 00000000..15904fee
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-03_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-04.png b/src/Authoring/Studio/res/editcamera_tools_hi-04.png
new file mode 100644
index 00000000..22da1b6d
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-04.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-04@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-04@2x.png
new file mode 100644
index 00000000..9110519b
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-04@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled.png
new file mode 100644
index 00000000..1d9e0ac0
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled@2x.png
new file mode 100644
index 00000000..0a3f5527
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-04_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-05.png b/src/Authoring/Studio/res/editcamera_tools_hi-05.png
new file mode 100644
index 00000000..147f8e29
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-05.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-05@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-05@2x.png
new file mode 100644
index 00000000..844a61e4
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-05@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled.png b/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled.png
new file mode 100644
index 00000000..ee55b391
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled@2x.png b/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled@2x.png
new file mode 100644
index 00000000..072be7ed
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi-05_disabled@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_hi.bmp b/src/Authoring/Studio/res/editcamera_tools_hi.bmp
new file mode 100644
index 00000000..c2b73a43
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_hi.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/editcamera_tools_low.bmp b/src/Authoring/Studio/res/editcamera_tools_low.bmp
new file mode 100644
index 00000000..66bf41a4
--- /dev/null
+++ b/src/Authoring/Studio/res/editcamera_tools_low.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/group_move.png b/src/Authoring/Studio/res/group_move.png
new file mode 100644
index 00000000..d7d06770
--- /dev/null
+++ b/src/Authoring/Studio/res/group_move.png
Binary files differ
diff --git a/src/Authoring/Studio/res/group_rotate.png b/src/Authoring/Studio/res/group_rotate.png
new file mode 100644
index 00000000..23a3eb3e
--- /dev/null
+++ b/src/Authoring/Studio/res/group_rotate.png
Binary files differ
diff --git a/src/Authoring/Studio/res/group_scale.png b/src/Authoring/Studio/res/group_scale.png
new file mode 100644
index 00000000..64024dfe
--- /dev/null
+++ b/src/Authoring/Studio/res/group_scale.png
Binary files differ
diff --git a/src/Authoring/Studio/res/item_move.png b/src/Authoring/Studio/res/item_move.png
new file mode 100644
index 00000000..d7b923bf
--- /dev/null
+++ b/src/Authoring/Studio/res/item_move.png
Binary files differ
diff --git a/src/Authoring/Studio/res/item_rotate.png b/src/Authoring/Studio/res/item_rotate.png
new file mode 100644
index 00000000..514c567c
--- /dev/null
+++ b/src/Authoring/Studio/res/item_rotate.png
Binary files differ
diff --git a/src/Authoring/Studio/res/item_scale.png b/src/Authoring/Studio/res/item_scale.png
new file mode 100644
index 00000000..6ef8acdb
--- /dev/null
+++ b/src/Authoring/Studio/res/item_scale.png
Binary files differ
diff --git a/src/Authoring/Studio/res/keyframe.bmp b/src/Authoring/Studio/res/keyframe.bmp
new file mode 100644
index 00000000..39a5fa17
--- /dev/null
+++ b/src/Authoring/Studio/res/keyframe.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/library_buttons_hi_color.bmp b/src/Authoring/Studio/res/library_buttons_hi_color.bmp
new file mode 100644
index 00000000..b648bcae
--- /dev/null
+++ b/src/Authoring/Studio/res/library_buttons_hi_color.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/library_buttons_low_color.bmp b/src/Authoring/Studio/res/library_buttons_low_color.bmp
new file mode 100644
index 00000000..682f3a2c
--- /dev/null
+++ b/src/Authoring/Studio/res/library_buttons_low_color.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/libtoolb.bmp b/src/Authoring/Studio/res/libtoolb.bmp
new file mode 100644
index 00000000..2a07520a
--- /dev/null
+++ b/src/Authoring/Studio/res/libtoolb.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/menubtn_down.bmp b/src/Authoring/Studio/res/menubtn_down.bmp
new file mode 100644
index 00000000..84df200b
--- /dev/null
+++ b/src/Authoring/Studio/res/menubtn_down.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/menubtn_normal.bmp b/src/Authoring/Studio/res/menubtn_normal.bmp
new file mode 100644
index 00000000..f2357600
--- /dev/null
+++ b/src/Authoring/Studio/res/menubtn_normal.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/menubtn_selected.bmp b/src/Authoring/Studio/res/menubtn_selected.bmp
new file mode 100644
index 00000000..7bc4618d
--- /dev/null
+++ b/src/Authoring/Studio/res/menubtn_selected.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/menuedit.bmp b/src/Authoring/Studio/res/menuedit.bmp
new file mode 100644
index 00000000..de0869ad
--- /dev/null
+++ b/src/Authoring/Studio/res/menuedit.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/menutool.bmp b/src/Authoring/Studio/res/menutool.bmp
new file mode 100644
index 00000000..73b54017
--- /dev/null
+++ b/src/Authoring/Studio/res/menutool.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/open_dialog.png b/src/Authoring/Studio/res/open_dialog.png
new file mode 100644
index 00000000..c7e74ef0
--- /dev/null
+++ b/src/Authoring/Studio/res/open_dialog.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_grayed.bmp b/src/Authoring/Studio/res/playback_tools_grayed.bmp
new file mode 100644
index 00000000..0225b6db
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_grayed.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_hi.bmp b/src/Authoring/Studio/res/playback_tools_hi.bmp
new file mode 100644
index 00000000..2047ac6b
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_hi.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-00.png b/src/Authoring/Studio/res/playback_tools_low-00.png
new file mode 100644
index 00000000..d7cae98d
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-00.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-00@2x.png b/src/Authoring/Studio/res/playback_tools_low-00@2x.png
new file mode 100644
index 00000000..88d6cf17
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-00@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-01.png b/src/Authoring/Studio/res/playback_tools_low-01.png
new file mode 100644
index 00000000..bf4d7f59
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-01.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-01@2x.png b/src/Authoring/Studio/res/playback_tools_low-01@2x.png
new file mode 100644
index 00000000..8230c6e8
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-01@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-02.png b/src/Authoring/Studio/res/playback_tools_low-02.png
new file mode 100644
index 00000000..471b2573
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-02.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-02@2x.png b/src/Authoring/Studio/res/playback_tools_low-02@2x.png
new file mode 100644
index 00000000..02370d25
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-02@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-03.png b/src/Authoring/Studio/res/playback_tools_low-03.png
new file mode 100644
index 00000000..5c5f9018
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-03.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low-03@2x.png b/src/Authoring/Studio/res/playback_tools_low-03@2x.png
new file mode 100644
index 00000000..e6fe8e10
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low-03@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/playback_tools_low.bmp b/src/Authoring/Studio/res/playback_tools_low.bmp
new file mode 100644
index 00000000..cb9b6b29
--- /dev/null
+++ b/src/Authoring/Studio/res/playback_tools_low.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/prefstab-00.png b/src/Authoring/Studio/res/prefstab-00.png
new file mode 100644
index 00000000..8a452d15
--- /dev/null
+++ b/src/Authoring/Studio/res/prefstab-00.png
Binary files differ
diff --git a/src/Authoring/Studio/res/prefstab-00@2x.png b/src/Authoring/Studio/res/prefstab-00@2x.png
new file mode 100644
index 00000000..5703d0be
--- /dev/null
+++ b/src/Authoring/Studio/res/prefstab-00@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/prefstab-01.png b/src/Authoring/Studio/res/prefstab-01.png
new file mode 100644
index 00000000..35c99cea
--- /dev/null
+++ b/src/Authoring/Studio/res/prefstab-01.png
Binary files differ
diff --git a/src/Authoring/Studio/res/prefstab-01@2x.png b/src/Authoring/Studio/res/prefstab-01@2x.png
new file mode 100644
index 00000000..4c35e2b0
--- /dev/null
+++ b/src/Authoring/Studio/res/prefstab-01@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/prefstab.bmp b/src/Authoring/Studio/res/prefstab.bmp
new file mode 100644
index 00000000..3201d15e
--- /dev/null
+++ b/src/Authoring/Studio/res/prefstab.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/preview_tools.bmp b/src/Authoring/Studio/res/preview_tools.bmp
new file mode 100644
index 00000000..2cfdbfd9
--- /dev/null
+++ b/src/Authoring/Studio/res/preview_tools.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/project-icon-new.bmp b/src/Authoring/Studio/res/project-icon-new.bmp
new file mode 100644
index 00000000..01123652
--- /dev/null
+++ b/src/Authoring/Studio/res/project-icon-new.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/project-icon-open.bmp b/src/Authoring/Studio/res/project-icon-open.bmp
new file mode 100644
index 00000000..50b279f7
--- /dev/null
+++ b/src/Authoring/Studio/res/project-icon-open.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/qt3ds_header.bmp b/src/Authoring/Studio/res/qt3ds_header.bmp
new file mode 100644
index 00000000..cfe81cc2
--- /dev/null
+++ b/src/Authoring/Studio/res/qt3ds_header.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/separator.png b/src/Authoring/Studio/res/separator.png
new file mode 100644
index 00000000..c86aac17
--- /dev/null
+++ b/src/Authoring/Studio/res/separator.png
Binary files differ
diff --git a/src/Authoring/Studio/res/separator@2x.png b/src/Authoring/Studio/res/separator@2x.png
new file mode 100644
index 00000000..052003a4
--- /dev/null
+++ b/src/Authoring/Studio/res/separator@2x.png
Binary files differ
diff --git a/src/Authoring/Studio/res/stmtool.bmp b/src/Authoring/Studio/res/stmtool.bmp
new file mode 100644
index 00000000..71ed95fc
--- /dev/null
+++ b/src/Authoring/Studio/res/stmtool.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/storagep.bmp b/src/Authoring/Studio/res/storagep.bmp
new file mode 100644
index 00000000..d784a3dc
--- /dev/null
+++ b/src/Authoring/Studio/res/storagep.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/tmchmenu.bmp b/src/Authoring/Studio/res/tmchmenu.bmp
new file mode 100644
index 00000000..329bbf96
--- /dev/null
+++ b/src/Authoring/Studio/res/tmchmenu.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/tmlntool.bmp b/src/Authoring/Studio/res/tmlntool.bmp
new file mode 100644
index 00000000..6678d093
--- /dev/null
+++ b/src/Authoring/Studio/res/tmlntool.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/tmtbmenu.bmp b/src/Authoring/Studio/res/tmtbmenu.bmp
new file mode 100644
index 00000000..ddf00990
--- /dev/null
+++ b/src/Authoring/Studio/res/tmtbmenu.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/weblinkc.bmp b/src/Authoring/Studio/res/weblinkc.bmp
new file mode 100644
index 00000000..e9f941b4
--- /dev/null
+++ b/src/Authoring/Studio/res/weblinkc.bmp
Binary files differ
diff --git a/src/Authoring/Studio/res/winxp1.bin b/src/Authoring/Studio/res/winxp1.bin
new file mode 100644
index 00000000..065c2edf
--- /dev/null
+++ b/src/Authoring/Studio/res/winxp1.bin
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly
+ xmlns="urn:schemas-microsoft-com:asm.v1"
+ manifestVersion="1.0">
+<assemblyIdentity
+ processorArchitecture="x86"
+ version="5.1.0.0"
+ type="win32"
+ name="test.exe"/>
+ <description>Test Application</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ processorArchitecture="x86"/>
+ </dependentAssembly>
+ </dependency>
+</assembly>
diff --git a/src/Authoring/Studio/style.qss b/src/Authoring/Studio/style.qss
new file mode 100644
index 00000000..1bb92ddc
--- /dev/null
+++ b/src/Authoring/Studio/style.qss
@@ -0,0 +1,219 @@
+/* General coloring and font size */
+QWidget {
+ color: #ffffff;
+ background: #2e2f30;
+ font-size: 12px;
+ border: 0px;
+}
+
+/* DockWidget separators */
+QMainWindow::separator {
+ background: #000000;
+ width: 1px; /* when vertical */
+ height: 1px; /* when horizontal */
+}
+
+/* Tabs */
+QTabBar::tab:selected {
+ background: #2e2f30;
+ padding: 5px;
+}
+
+QTabBar::tab:!selected {
+ background: #262829;
+ color: #727476;
+ padding: 5px;
+}
+
+QTabWidget::pane {
+ border: 1px solid #404244;
+}
+
+/* Scrollbar */
+QScrollBar:horizontal {
+ border: 1px solid #262829;
+ background: #404244;
+}
+
+QScrollBar::handle:horizontal {
+ background: #727476;
+}
+
+QScrollBar::add-line:horizontal,
+QScrollBar::sub-line:horizontal {
+ width: 0px;
+}
+
+QScrollBar:vertical {
+ border: 1px solid #262829;
+ background: #404244;
+}
+
+QScrollBar::handle:vertical {
+ background: #727476;
+}
+
+QScrollBar::add-line:vertical,
+QScrollBar::sub-line:vertical {
+ height: 0px;
+}
+
+/* Menubar */
+QMenuBar {
+ background: #404244;
+ border-top: 1px solid #262829;
+}
+
+QMenuBar::item {
+ background: transparent;
+}
+
+QMenuBar::item:selected {
+ background: #46a2da;
+ border: 1px solid #262829;
+}
+
+/* Menu */
+QMenu {
+ background: #262829;
+ border: 1px solid #727476;
+}
+
+QMenu::item {
+ padding: 4px 20px 4px 20px;
+}
+
+QMenu::item:selected:enabled {
+ background: #46a2da;
+}
+
+QMenu::item:disabled {
+ padding: 4px 20px 4px 20px;
+ color: #727476;
+}
+
+QMenu::separator {
+ height: 1px;
+ background: #727476;
+}
+
+/* Toolbar */
+QToolBar {
+ background: #404244;
+ border-top: 1px solid #262829;
+ min-height: 24px;
+ max-height: 24px;
+}
+
+QToolBar::handle {
+ image: url(:/res/separator.png);
+}
+
+QToolBar::separator {
+ background: #727476;
+ width: 1px;
+ margin: 3px;
+}
+
+QToolButton:!checked {
+ min-width: 22px;
+ min-height: 22px;
+ background-color: #404244;
+}
+
+QToolButton:checked, QToolButton:hover {
+ min-width: 22px;
+ min-height: 22px;
+ background-color: #262829;
+}
+
+QToolButton:pressed {
+ min-width: 22px;
+ min-height: 22px;
+ background-color: #46a2da;
+}
+
+QComboBox#cameraSelector {
+ /* We have a specific style for camera selector combobox, as it's in the toolbar and needs to
+ blend in */
+ background: #404244;
+ border: 0px;
+ border-radius: 0;
+ padding: 3px;
+}
+
+/* ToolTip */
+QToolTip {
+ background: #404244;
+ color: #ffffff;
+ font-size: 12px;
+ border: 1px solid #727476;
+ border-radius: 2;
+}
+
+/* Dialog widgets */
+QComboBox,
+QLineEdit,
+QSpinBox,
+QTimeEdit {
+ background: #404244;
+ border: 1px solid #262829;
+ border-radius: 2;
+ padding: 3px;
+}
+
+QComboBox::drop-down {
+ background: #404244;
+}
+
+QComboBox::down-arrow {
+ image: url(:/res/arrow_down.png);
+}
+
+QGroupBox {
+ padding: 14px 0px 0px 0px;
+ border: 1px solid #262829;
+}
+
+QGroupBox::title {
+ subcontrol-origin: padding;
+ margin-top: 2px;
+ margin-left: 10px;
+}
+
+QPushButton {
+ background: #404244;
+ border: 1px solid #262829;
+ border-radius: 2;
+ padding: 3px 10px 3px 10px;
+ min-width: 75;
+}
+
+/* Tutorial Dialog */
+QDialog#StudioTutorialWidget,
+QDialog#StudioTutorialWidget QWidget {
+ color: #ffffff;
+ background-color: transparent;
+ border: 0px;
+}
+
+QPushButton:!pressed#studioTutorialOpen,
+QPushButton:!pressed#studioTutorialNew {
+ background: transparent;
+ border: 1px solid #41cd52;
+}
+
+QPushButton:pressed#studioTutorialOpen,
+QPushButton:pressed#studioTutorialNew {
+ background: #41cd52;
+ border: 1px solid #41cd52;
+}
+
+/* Startup Dialog */
+QDialog#StartupDlg,
+QDialog#StartupDlg QWidget {
+ color: #ffffff;
+ background-color: transparent;
+ border: 0px;
+ font-size: 16px;
+}
diff --git a/src/Authoring/Studio/version.h b/src/Authoring/Studio/version.h
new file mode 100644
index 00000000..118d95b5
--- /dev/null
+++ b/src/Authoring/Studio/version.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef INCLUDED_VERSION
+
+#ifdef WIN32
+#include "..\Build\VersionNumber.h"
+#endif
+
+#endif