From 2b9f84b72b933a0a82b6607efca673f58b410cdb Mon Sep 17 00:00:00 2001 From: Abhijit Kundu Date: Mon, 1 Dec 2014 13:41:34 -0500 Subject: [PATCH] First commit --- .gitignore | 31 + CMakeLists.txt | 53 + GenerateQtPropertyBrowserConfig.cmake | 59 + QtPropertyBrowserConfig.cmake.in | 60 + README.md | 93 + UseQtPropertyBrowser.cmake.in | 36 + cmake/AddQt.cmake | 55 + cmake/CommonCMakeUtils.cmake | 27 + doc/images/canvas_typed.png | Bin 0 -> 16376 bytes doc/images/canvas_variant.png | Bin 0 -> 15293 bytes doc/images/decoration.png | Bin 0 -> 6620 bytes doc/images/demo.png | Bin 0 -> 76351 bytes doc/images/extension.png | Bin 0 -> 3994 bytes doc/images/object_controller.png | Bin 0 -> 39658 bytes doc/images/qt-logo.png | Bin 0 -> 4075 bytes doc/images/qtbuttonpropertybrowser.png | Bin 0 -> 7181 bytes doc/images/qtgroupboxpropertybrowser.png | Bin 0 -> 5819 bytes doc/images/qtpropertybrowser-duplicate.png | Bin 0 -> 1620 bytes doc/images/qtpropertybrowser.png | Bin 0 -> 9677 bytes doc/images/qttreepropertybrowser.png | Bin 0 -> 7250 bytes doc/images/simple.png | Bin 0 -> 9548 bytes examples/CMakeLists.txt | 15 + examples/canvas_typed/CMakeLists.txt | 19 + examples/canvas_typed/canvas_typed.qdoc | 96 + examples/canvas_typed/main.cpp | 51 + examples/canvas_typed/mainwindow.cpp | 521 ++ examples/canvas_typed/mainwindow.h | 120 + examples/canvas_typed/qtcanvas.cpp | 5906 +++++++++++++++ examples/canvas_typed/qtcanvas.h | 778 ++ examples/canvas_variant/CMakeLists.txt | 19 + examples/canvas_variant/canvas_variant.qdoc | 94 + examples/canvas_variant/main.cpp | 51 + examples/canvas_variant/mainwindow.cpp | 434 ++ examples/canvas_variant/mainwindow.h | 114 + examples/canvas_variant/qtcanvas.cpp | 5906 +++++++++++++++ examples/canvas_variant/qtcanvas.h | 778 ++ examples/decoration/CMakeLists.txt | 15 + examples/decoration/decoration.qdoc | 54 + examples/decoration/main.cpp | 312 + examples/demo/CMakeLists.txt | 19 + examples/demo/demo.pro | 8 + examples/demo/demo.qdoc | 59 + examples/demo/demo.qrc | 8 + examples/demo/images/down.png | Bin 0 -> 594 bytes examples/demo/images/left.png | Bin 0 -> 678 bytes examples/demo/images/right.png | Bin 0 -> 655 bytes examples/demo/images/up.png | Bin 0 -> 692 bytes examples/demo/main.cpp | 215 + examples/examples.pro | 7 + examples/extension/CMakeLists.txt | 15 + examples/extension/extension.pro | 8 + examples/extension/extension.qdoc | 53 + examples/extension/main.cpp | 238 + examples/object_controller/CMakeLists.txt | 16 + examples/object_controller/main.cpp | 167 + .../object_controller/object_controller.qdoc | 54 + .../object_controller/objectcontroller.cpp | 391 + examples/object_controller/objectcontroller.h | 64 + examples/simple/CMakeLists.txt | 9 + examples/simple/main.cpp | 180 + examples/simple/simple.qdoc | 51 + src/CMakeLists.txt | 116 + src/QtAbstractEditorFactoryBase | 1 + src/QtAbstractPropertyBrowser | 1 + src/QtAbstractPropertyManager | 1 + src/QtBoolPropertyManager | 1 + src/QtBrowserItem | 1 + src/QtButtonPropertyBrowser | 1 + src/QtCharEditorFactory | 1 + src/QtCharPropertyManager | 1 + src/QtCheckBoxFactory | 1 + src/QtColorEditorFactory | 1 + src/QtColorPropertyManager | 1 + src/QtCursorEditorFactory | 1 + src/QtCursorPropertyManager | 1 + src/QtDateEditFactory | 1 + src/QtDatePropertyManager | 1 + src/QtDateTimeEditFactory | 1 + src/QtDateTimePropertyManager | 1 + src/QtDoublePropertyManager | 1 + src/QtDoubleSpinBoxFactory | 1 + src/QtEnumEditorFactory | 1 + src/QtEnumPropertyManager | 1 + src/QtFlagPropertyManager | 1 + src/QtFontEditorFactory | 1 + src/QtFontPropertyManager | 1 + src/QtGroupBoxPropertyBrowser | 1 + src/QtGroupPropertyManager | 1 + src/QtIntPropertyManager | 1 + src/QtKeySequenceEditorFactory | 1 + src/QtKeySequencePropertyManager | 1 + src/QtLineEditFactory | 1 + src/QtLocalePropertyManager | 1 + src/QtPointFPropertyManager | 1 + src/QtPointPropertyManager | 1 + src/QtProperty | 1 + src/QtRectFPropertyManager | 1 + src/QtRectPropertyManager | 1 + src/QtScrollBarFactory | 1 + src/QtSizeFPropertyManager | 1 + src/QtSizePolicyPropertyManager | 1 + src/QtSizePropertyManager | 1 + src/QtSliderFactory | 1 + src/QtSpinBoxFactory | 1 + src/QtStringPropertyManager | 1 + src/QtTimeEditFactory | 1 + src/QtTimePropertyManager | 1 + src/QtTreePropertyBrowser | 1 + src/QtVariantEditorFactory | 1 + src/QtVariantProperty | 1 + src/QtVariantPropertyManager | 1 + src/images/cursor-arrow.png | Bin 0 -> 171 bytes src/images/cursor-busy.png | Bin 0 -> 201 bytes src/images/cursor-closedhand.png | Bin 0 -> 147 bytes src/images/cursor-cross.png | Bin 0 -> 130 bytes src/images/cursor-forbidden.png | Bin 0 -> 199 bytes src/images/cursor-hand.png | Bin 0 -> 159 bytes src/images/cursor-hsplit.png | Bin 0 -> 155 bytes src/images/cursor-ibeam.png | Bin 0 -> 124 bytes src/images/cursor-openhand.png | Bin 0 -> 160 bytes src/images/cursor-sizeall.png | Bin 0 -> 174 bytes src/images/cursor-sizeb.png | Bin 0 -> 161 bytes src/images/cursor-sizef.png | Bin 0 -> 161 bytes src/images/cursor-sizeh.png | Bin 0 -> 145 bytes src/images/cursor-sizev.png | Bin 0 -> 141 bytes src/images/cursor-uparrow.png | Bin 0 -> 132 bytes src/images/cursor-vsplit.png | Bin 0 -> 161 bytes src/images/cursor-wait.png | Bin 0 -> 172 bytes src/images/cursor-whatsthis.png | Bin 0 -> 191 bytes src/qtbuttonpropertybrowser.cpp | 629 ++ src/qtbuttonpropertybrowser.h | 88 + src/qteditorfactory.cpp | 2579 +++++++ src/qteditorfactory.h | 400 + src/qtgroupboxpropertybrowser.cpp | 536 ++ src/qtgroupboxpropertybrowser.h | 79 + src/qtpropertybrowser.cpp | 2048 ++++++ src/qtpropertybrowser.h | 335 + src/qtpropertybrowser.pri | 30 + src/qtpropertybrowser.qrc | 23 + src/qtpropertybrowserutils.cpp | 432 ++ src/qtpropertybrowserutils_p.h | 162 + src/qtpropertymanager.cpp | 6425 +++++++++++++++++ src/qtpropertymanager.h | 749 ++ src/qttreepropertybrowser.cpp | 1076 +++ src/qttreepropertybrowser.h | 137 + src/qtvariantproperty.cpp | 2358 ++++++ src/qtvariantproperty.h | 190 + 147 files changed, 35700 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 GenerateQtPropertyBrowserConfig.cmake create mode 100644 QtPropertyBrowserConfig.cmake.in create mode 100644 README.md create mode 100644 UseQtPropertyBrowser.cmake.in create mode 100644 cmake/AddQt.cmake create mode 100644 cmake/CommonCMakeUtils.cmake create mode 100644 doc/images/canvas_typed.png create mode 100644 doc/images/canvas_variant.png create mode 100644 doc/images/decoration.png create mode 100644 doc/images/demo.png create mode 100644 doc/images/extension.png create mode 100644 doc/images/object_controller.png create mode 100644 doc/images/qt-logo.png create mode 100644 doc/images/qtbuttonpropertybrowser.png create mode 100644 doc/images/qtgroupboxpropertybrowser.png create mode 100644 doc/images/qtpropertybrowser-duplicate.png create mode 100644 doc/images/qtpropertybrowser.png create mode 100644 doc/images/qttreepropertybrowser.png create mode 100644 doc/images/simple.png create mode 100644 examples/CMakeLists.txt create mode 100644 examples/canvas_typed/CMakeLists.txt create mode 100644 examples/canvas_typed/canvas_typed.qdoc create mode 100644 examples/canvas_typed/main.cpp create mode 100644 examples/canvas_typed/mainwindow.cpp create mode 100644 examples/canvas_typed/mainwindow.h create mode 100644 examples/canvas_typed/qtcanvas.cpp create mode 100644 examples/canvas_typed/qtcanvas.h create mode 100644 examples/canvas_variant/CMakeLists.txt create mode 100644 examples/canvas_variant/canvas_variant.qdoc create mode 100644 examples/canvas_variant/main.cpp create mode 100644 examples/canvas_variant/mainwindow.cpp create mode 100644 examples/canvas_variant/mainwindow.h create mode 100644 examples/canvas_variant/qtcanvas.cpp create mode 100644 examples/canvas_variant/qtcanvas.h create mode 100644 examples/decoration/CMakeLists.txt create mode 100644 examples/decoration/decoration.qdoc create mode 100644 examples/decoration/main.cpp create mode 100644 examples/demo/CMakeLists.txt create mode 100644 examples/demo/demo.pro create mode 100644 examples/demo/demo.qdoc create mode 100644 examples/demo/demo.qrc create mode 100644 examples/demo/images/down.png create mode 100644 examples/demo/images/left.png create mode 100644 examples/demo/images/right.png create mode 100644 examples/demo/images/up.png create mode 100644 examples/demo/main.cpp create mode 100644 examples/examples.pro create mode 100644 examples/extension/CMakeLists.txt create mode 100644 examples/extension/extension.pro create mode 100644 examples/extension/extension.qdoc create mode 100644 examples/extension/main.cpp create mode 100644 examples/object_controller/CMakeLists.txt create mode 100644 examples/object_controller/main.cpp create mode 100644 examples/object_controller/object_controller.qdoc create mode 100644 examples/object_controller/objectcontroller.cpp create mode 100644 examples/object_controller/objectcontroller.h create mode 100644 examples/simple/CMakeLists.txt create mode 100644 examples/simple/main.cpp create mode 100644 examples/simple/simple.qdoc create mode 100644 src/CMakeLists.txt create mode 100644 src/QtAbstractEditorFactoryBase create mode 100644 src/QtAbstractPropertyBrowser create mode 100644 src/QtAbstractPropertyManager create mode 100644 src/QtBoolPropertyManager create mode 100644 src/QtBrowserItem create mode 100644 src/QtButtonPropertyBrowser create mode 100644 src/QtCharEditorFactory create mode 100644 src/QtCharPropertyManager create mode 100644 src/QtCheckBoxFactory create mode 100644 src/QtColorEditorFactory create mode 100644 src/QtColorPropertyManager create mode 100644 src/QtCursorEditorFactory create mode 100644 src/QtCursorPropertyManager create mode 100644 src/QtDateEditFactory create mode 100644 src/QtDatePropertyManager create mode 100644 src/QtDateTimeEditFactory create mode 100644 src/QtDateTimePropertyManager create mode 100644 src/QtDoublePropertyManager create mode 100644 src/QtDoubleSpinBoxFactory create mode 100644 src/QtEnumEditorFactory create mode 100644 src/QtEnumPropertyManager create mode 100644 src/QtFlagPropertyManager create mode 100644 src/QtFontEditorFactory create mode 100644 src/QtFontPropertyManager create mode 100644 src/QtGroupBoxPropertyBrowser create mode 100644 src/QtGroupPropertyManager create mode 100644 src/QtIntPropertyManager create mode 100644 src/QtKeySequenceEditorFactory create mode 100644 src/QtKeySequencePropertyManager create mode 100644 src/QtLineEditFactory create mode 100644 src/QtLocalePropertyManager create mode 100644 src/QtPointFPropertyManager create mode 100644 src/QtPointPropertyManager create mode 100644 src/QtProperty create mode 100644 src/QtRectFPropertyManager create mode 100644 src/QtRectPropertyManager create mode 100644 src/QtScrollBarFactory create mode 100644 src/QtSizeFPropertyManager create mode 100644 src/QtSizePolicyPropertyManager create mode 100644 src/QtSizePropertyManager create mode 100644 src/QtSliderFactory create mode 100644 src/QtSpinBoxFactory create mode 100644 src/QtStringPropertyManager create mode 100644 src/QtTimeEditFactory create mode 100644 src/QtTimePropertyManager create mode 100644 src/QtTreePropertyBrowser create mode 100644 src/QtVariantEditorFactory create mode 100644 src/QtVariantProperty create mode 100644 src/QtVariantPropertyManager create mode 100644 src/images/cursor-arrow.png create mode 100644 src/images/cursor-busy.png create mode 100644 src/images/cursor-closedhand.png create mode 100644 src/images/cursor-cross.png create mode 100644 src/images/cursor-forbidden.png create mode 100644 src/images/cursor-hand.png create mode 100644 src/images/cursor-hsplit.png create mode 100644 src/images/cursor-ibeam.png create mode 100644 src/images/cursor-openhand.png create mode 100644 src/images/cursor-sizeall.png create mode 100644 src/images/cursor-sizeb.png create mode 100644 src/images/cursor-sizef.png create mode 100644 src/images/cursor-sizeh.png create mode 100644 src/images/cursor-sizev.png create mode 100644 src/images/cursor-uparrow.png create mode 100644 src/images/cursor-vsplit.png create mode 100644 src/images/cursor-wait.png create mode 100644 src/images/cursor-whatsthis.png create mode 100644 src/qtbuttonpropertybrowser.cpp create mode 100644 src/qtbuttonpropertybrowser.h create mode 100644 src/qteditorfactory.cpp create mode 100644 src/qteditorfactory.h create mode 100644 src/qtgroupboxpropertybrowser.cpp create mode 100644 src/qtgroupboxpropertybrowser.h create mode 100644 src/qtpropertybrowser.cpp create mode 100644 src/qtpropertybrowser.h create mode 100644 src/qtpropertybrowser.pri create mode 100644 src/qtpropertybrowser.qrc create mode 100644 src/qtpropertybrowserutils.cpp create mode 100644 src/qtpropertybrowserutils_p.h create mode 100644 src/qtpropertymanager.cpp create mode 100644 src/qtpropertymanager.h create mode 100644 src/qttreepropertybrowser.cpp create mode 100644 src/qttreepropertybrowser.h create mode 100644 src/qtvariantproperty.cpp create mode 100644 src/qtvariantproperty.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b667c89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Ignores 'build' in any sub folder +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2b921b9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) +PROJECT(QtPropertyBrowser) + +########################### Misc. Configs ############################## +# Add custom cmake files folder +SET (PROJECT_CMAKE_DIR ${PROJECT_SOURCE_DIR}/cmake) +SET (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_CMAKE_DIR}") + + + +##################### Look for required libraries ###################### + +# Add QT dependencies +INCLUDE(AddQt) +IF (NOT QT_FOUND) + MESSAGE(FATAL "We Need some QT") +ENDIF() + +#----------------------------------------------------------------------------- +# Subdirectories +# +ADD_SUBDIRECTORY(src) + +OPTION (BUILD_EXAMPLES "Build Examples" ON) +IF(BUILD_EXAMPLES) + ADD_SUBDIRECTORY(examples) +ENDIF() + +#----------------------------------------------------------------------------- +# Generate QtPropertyBrowserConfig file +# +CONFIGURE_FILE(UseQtPropertyBrowser.cmake.in + ${QtPropertyBrowser_BINARY_DIR}/UseQtPropertyBrowser.cmake COPYONLY) + +INCLUDE(GenerateQtPropertyBrowserConfig.cmake) + + +MESSAGE(STATUS "===============================================================") +MESSAGE(STATUS "================ Configuration Summary ======================") +MESSAGE(STATUS "Project Name: ${PROJECT_NAME}") +MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +MESSAGE(STATUS "Build type Flags: ${CMAKE_BUILD_TYPE_FLAGS}") +MESSAGE(STATUS "C++ compile flags: ${CMAKE_CXX_FLAGS}") +MESSAGE(STATUS "Install Path: ${CMAKE_INSTALL_PREFIX}") +MESSAGE(STATUS "Build Examples: ${BUILD_EXAMPLES}") +IF(QT_FOUND ) + MESSAGE(STATUS "QT Version: ${QT_VERSION_STRING}") + MESSAGE(STATUS "QT_TARGETS: ${QT_TARGETS}") + MESSAGE(STATUS "QT_ALL_TARGETS: ${QT_ALL_TARGETS}") + MESSAGE(STATUS "QT_INCLUDE_DIRS: ${QT_INCLUDE_DIRS}") + MESSAGE(STATUS "QT_COMPILE_DEFS: ${QT_COMPILE_DEFS}") +ENDIF() diff --git a/GenerateQtPropertyBrowserConfig.cmake b/GenerateQtPropertyBrowserConfig.cmake new file mode 100644 index 0000000..5f1dd6c --- /dev/null +++ b/GenerateQtPropertyBrowserConfig.cmake @@ -0,0 +1,59 @@ +########################################################################### +# +# Library: CTK +# +# Copyright (c) Kitware Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.commontk.org/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +#----------------------------------------------------------------------------- +# Settings shared between the build tree and install tree. + + +#----------------------------------------------------------------------------- +# Settings specific to the build tree. + +# The "use" file. +SET(QtPropertyBrowser_USE_FILE ${QtPropertyBrowser_BINARY_DIR}/UseQtPropertyBrowser.cmake) + +# Determine the include directories needed. +SET(QtPropertyBrowser_INCLUDE_DIRS_CONFIG + ${QtPropertyBrowser_SOURCE_DIR}/src +) + +# Library directory. +SET(QtPropertyBrowser_LIBRARY_DIRS_CONFIG ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) + +# Runtime library directory. +SET(QtPropertyBrowser_RUNTIME_LIBRARY_DIRS_CONFIG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + +# Build configuration information. +SET(QtPropertyBrowser_CONFIGURATION_TYPES_CONFIG ${CMAKE_CONFIGURATION_TYPES}) +SET(QtPropertyBrowser_BUILD_TYPE_CONFIG ${CMAKE_BUILD_TYPE}) + +#----------------------------------------------------------------------------- +# Configure QtPropertyBrowserConfig.cmake for the build tree. +CONFIGURE_FILE(${QtPropertyBrowser_SOURCE_DIR}/QtPropertyBrowserConfig.cmake.in + ${QtPropertyBrowser_BINARY_DIR}/QtPropertyBrowserConfig.cmake @ONLY IMMEDIATE) + +#----------------------------------------------------------------------------- +# Settings specific to the install tree. + +# TODO + +#----------------------------------------------------------------------------- +# Configure QtPropertyBrowserConfig.cmake for the install tree. + +# TODO diff --git a/QtPropertyBrowserConfig.cmake.in b/QtPropertyBrowserConfig.cmake.in new file mode 100644 index 0000000..ded17b1 --- /dev/null +++ b/QtPropertyBrowserConfig.cmake.in @@ -0,0 +1,60 @@ +########################################################################### +# +# Library: CTK +# +# Copyright (c) Kitware Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.commontk.org/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +# +# QtPropertyBrowserConfig.cmake - QtPropertyBrowser CMake configuration file for external projects. +# +# This file is configured by QtPropertyBrowser and used by the +# UseQtPropertyBrowser.cmake module to load QtPropertyBrowser's settings +# for an external project. + +# The QtPropertyBrowser include file directories. +SET(QtPropertyBrowser_INCLUDE_DIRS "@QtPropertyBrowser_INCLUDE_DIRS_CONFIG@") + +# The QtPropertyBrowser library directories. Note that if +# QtPropertyBrowser_CONFIGURATION_TYPES is set (see below) then these directories +# will be the parent directories under which there will be a directory +# of runtime binaries for each configuration type. +SET(QtPropertyBrowser_LIBRARY_DIRS "@QtPropertyBrowser_LIBRARY_DIRS_CONFIG@") + +# The QtPropertyBrowser runtime library directories. Note that if +# QtPropertyBrowser_CONFIGURATION_TYPES is set (see below) then these directories +# will be the parent directories under which there will be a directory +# of runtime libraries for each configuration type. +SET(QtPropertyBrowser_RUNTIME_LIBRARY_DIRS "@QtPropertyBrowser_RUNTIME_LIBRARY_DIRS_CONFIG@") + +# The location of the UseQtPropertyBrowser.cmake file. +SET(QtPropertyBrowser_USE_FILE "@QtPropertyBrowser_USE_FILE@") + + +# A QtPropertyBrowser install tree always provides one build configuration. +# A QtPropertyBrowser build tree may provide either one or multiple build +# configurations depending on the CMake generator used. +# Since QtPropertyBrowser can be used either from a build tree or an install +# tree it is useful for outside projects to know the configurations available. +# If this QtPropertyBrowserConfig.cmake is in a QtPropertyBrowser install +# tree QtPropertyBrowser_CONFIGURATION_TYPES will be empty and +# QtPropertyBrowser_BUILD_TYPE will be set to the value of +# CMAKE_BUILD_TYPE used to build QtPropertyBrowser. If QtPropertyBrowserConfig.cmake +# is in a QtPropertyBrowser build tree then QtPropertyBrowser_CONFIGURATION_TYPES +# and QtPropertyBrowser_BUILD_TYPE will have values matching CMAKE_CONFIGURATION_TYPES +# and CMAKE_BUILD_TYPE for that build tree (only one will ever be set). +SET(QtPropertyBrowser_CONFIGURATION_TYPES @QtPropertyBrowser_CONFIGURATION_TYPES_CONFIG@) +SET(QtPropertyBrowser_BUILD_TYPE @QtPropertyBrowser_BUILD_TYPE_CONFIG@) diff --git a/README.md b/README.md new file mode 100644 index 0000000..148c47b --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +Qt Solutions Component: Property Browser + +A property browser framework enabling the user to edit a set of +properties. + +The framework provides a browser widget that displays the given +properties with labels and corresponding editing widgets (e.g. +line edits or comboboxes). The various types of editing widgets +are provided by the framework's editor factories: For each +property type, the framework provides a property manager (e.g. +QtIntPropertyManager and QtStringPropertyManager) which can be +associated with the preferred editor factory (e.g. +QtSpinBoxFactory and QtLineEditFactory). The framework also +provides a variant based property type with corresponding variant +manager and factory. Finally, the framework provides three +ready-made implementations of the browser widget: +QtTreePropertyBrowser, QtButtonPropertyBrowser and +QtGroupBoxPropertyBrowser. + +Version history: + +2.1: - QtTreePropertyBrowser - tooltip of property applied to + first column, while second column shows the value text of property + in its tooltip + - QtAbstractPropertyManager - initializeProperty() and + uninitializeProperty() without const modifier now + - QtTreePropertyBrowser and QtGroupBoxPropertyBrowser - internal + margin set to 0 + - QtProperty - setEnabled() and isEnabled() methods added + - QtTreePropertyBrowser - "rootIsDecorated", "indentation" and + "headerVisible" properties added + - QtProperty - hasValue() method added, useful for group + properties + +2.2: - FocusOut event now filtered out in case of + Qt::ActiveWindowFocusReason reason. In that case editor is not + closed when its sub dialog is executed + - Removed bug in color icon generation + - Decimals attribute added to "double" property type + - PointF, SizeF and RectF types supported + - Proper translation calls for tree property browser + - QtProperty - ensure inserted subproperty is different from + "this" property + - QtBrowserItem class introduced, useful for identifying browser's + gui elements + - Possibility to control expanded state of QtTreePropertyBrowser's + items from code + - QtTreePropertyBrowser - "resizeMode" and "splitterPosition" + properties added + - QtGroupBoxPropertyBrowser - fixed crash in case of deleting the + editor factory and then deleting the manager + - "Decoration" example added - it shows how to add new + responsibilities to the existing managers and editor factories + +2.3: - Various bugfixes and improvements + - QtProperty - setModified() and isModified() methods added + - QtTreePropertyBrowser - disabling an item closes its editor + - KeySequence, Char, Locale and Cursor types supported + - Support for icons in enum type added + - Kerning subproperty exposed in Font type + - New property browser class added - QtButtonPropertyBrowser with + drop down button as a grouping element + +2.4: - Fixed memory leak of QtProperty + - QtTreePropertyBrowser - group items are rendered better + - QtTreePropertyBrowser - propertiesWithoutValueMarked and + alternatingRowColors features added + - QtTreePropertyBrowser - possibility of coloring properties added + - QtTreePropertyBrowser - keyboard navigation improved + - New factories providing popup dialogs added: + QtColorEditorFactory and QtFontEditorFactory + - Single step attribute added to: QtIntPropertyManager and + QtDoublePropertyManager + +2.5: - "Object Controller" example added. It implements a similar + widget to the property editor in QDesigner + - Compile with QT_NO_CURSOR + - Expand root item with single click on the '+' icon + - QtRectPropertyManager and QtRectFPropertyManager - by default + constraint is null rect meaning no constraint is applied + +2.6: - QtGroupPropertyBrowser - don't force the layout to show the + whole labels' contents for read only properties, show tooltips for + them in addition. + - QtTreePropertyBrowser - fixed painting of the editor for color + property type when style sheet is used (QTSOLBUG-64). + - Make it possible to change the style of the checkboxes with a + stylesheet (QTSOLBUG-61). + - Change the minimum size of a combobox so that it can show at + least one character and an icon. + - Make it possible to properly style custom embedded editors (e.g. + the color editor provided with the solution). + diff --git a/UseQtPropertyBrowser.cmake.in b/UseQtPropertyBrowser.cmake.in new file mode 100644 index 0000000..330f103 --- /dev/null +++ b/UseQtPropertyBrowser.cmake.in @@ -0,0 +1,36 @@ +########################################################################### +# +# Library: CTK +# +# Copyright (c) Kitware Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.commontk.org/LICENSE +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +########################################################################### + +# +# This module is provided as QtPropertyBrowser_USE_FILE by QtPropertyBrowserConfig.cmake. +# It can be INCLUDED in a project to load the needed compiler and linker +# settings to use QtPropertyBrowser. +# + +IF(NOT QtPropertyBrowser_USE_FILE_INCLUDED) + SET(QtPropertyBrowser_USE_FILE_INCLUDED 1) + + # Add include directories needed to use QtPropertyBrowser. + INCLUDE_DIRECTORIES(${QtPropertyBrowser_INCLUDE_DIRS}) + + # Add link directories needed to use QtPropertyBrowser. + LINK_DIRECTORIES(${QtPropertyBrowser_LIBRARY_DIRS}) + +ENDIF() diff --git a/cmake/AddQt.cmake b/cmake/AddQt.cmake new file mode 100644 index 0000000..599dbd3 --- /dev/null +++ b/cmake/AddQt.cmake @@ -0,0 +1,55 @@ +if(NOT QT_FOUND) +# --- QT5 --- + find_package(Qt5Core QUIET) + if(Qt5Core_FOUND) + find_package(Qt5Gui QUIET) + find_package(Qt5Widgets QUIET) + if(Qt5Gui_FOUND AND Qt5Widgets_FOUND) + set(QT5_FOUND ON) + set(QT_FOUND ON) + endif() + endif() +endif() + +if(NOT QT_FOUND) +# --- QT4 --- + find_package(Qt4 QUIET) + if(QT4_FOUND) + set(QT_FOUND TRUE) + endif() +endif() + + + +if(QT_FOUND) + if(NOT QT5_FOUND) + message ( STATUS "Using Qt4") + set( QT_VERSION_STRING "${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}") + set( QT_TARGETS "Qt4::QtGui") + else() + message ( STATUS "Using Qt5") + set( QT_VERSION_STRING ${Qt5Core_VERSION_STRING}) + set( QT_TARGETS "Qt5::Gui;Qt5::Widgets") + endif() + +else() + message ( STATUS "Qt Not Found") +endif() + + +#include some custom CMake util functions +INCLUDE(CommonCMakeUtils) + +extract_target_properties(QT_TARGETS_DEPENDENCIES QT_TARGETS INTERFACE_LINK_LIBRARIES) +set (QT_ALL_TARGETS ${QT_TARGETS} ${QT_TARGETS_DEPENDENCIES}) +list(REMOVE_DUPLICATES QT_ALL_TARGETS) +extract_target_properties(QT_INCLUDE_DIRS QT_ALL_TARGETS INTERFACE_INCLUDE_DIRECTORIES) +extract_target_properties(QT_COMPILE_DEFS QT_ALL_TARGETS INTERFACE_COMPILE_DEFINITIONS) + +IF(QT_FOUND ) + MESSAGE(STATUS "QT Version: ${QT_VERSION_STRING}") + MESSAGE(STATUS "QT_TARGETS: ${QT_TARGETS}") + MESSAGE(STATUS "QT_ALL_TARGETS: ${QT_ALL_TARGETS}") + MESSAGE(STATUS "QT_INCLUDE_DIRS: ${QT_INCLUDE_DIRS}") + MESSAGE(STATUS "QT_COMPILE_DEFS: ${QT_COMPILE_DEFS}") +ENDIF() \ No newline at end of file diff --git a/cmake/CommonCMakeUtils.cmake b/cmake/CommonCMakeUtils.cmake new file mode 100644 index 0000000..428dde7 --- /dev/null +++ b/cmake/CommonCMakeUtils.cmake @@ -0,0 +1,27 @@ +# extract filename components of all items in src_list in dst_list +# example usage: extract_filename_components(ALL_LIBS_BASENAME ALL_LIBS NAME_WE) +macro(extract_filename_components dst_list src_list component) + set(list_var "${${src_list}}") + #message(STATUS "list_var: ${list_var}") + #message(STATUS "component: ${component}") + foreach(item ${list_var}) + get_filename_component(BASENAME ${item} ${component}) + list( APPEND ${dst_list} ${BASENAME}) + endforeach() + #message(STATUS "dst_list: ${${dst_list}}") +endmacro() + + +# extract target properties of all items in src_list in dst_list +# example usage: extract_target_properties(QT_INCLUDES Qt5::Core INTERFACE_INCLUDE_DIR) +macro(extract_target_properties target_props target_list property) + set(list_var "${${target_list}}") + # message(STATUS "list_var: ${list_var}") + #message(STATUS "property: ${property}") + foreach(item ${list_var}) + get_target_property(value ${item} ${property}) + list( APPEND ${target_props} ${value}) + endforeach() + #message(STATUS "target_props: ${${target_props}}") + list(REMOVE_DUPLICATES ${target_props}) +endmacro() \ No newline at end of file diff --git a/doc/images/canvas_typed.png b/doc/images/canvas_typed.png new file mode 100644 index 0000000000000000000000000000000000000000..888cb6a0e1e2c3081b3c5dd82bbc1ab3ca0c7eb0 GIT binary patch literal 16376 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 literal 0 HcmV?d00001 diff --git a/doc/images/decoration.png b/doc/images/decoration.png new file mode 100644 index 0000000000000000000000000000000000000000..92db0fc60f85e9a9e2862c50367d8788ac7145b5 GIT binary patch literal 6620 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�tNNt{hvpw4vjnQ^nGmzSua%t*RO z1Xq>){g_F2U3v*u0G-oxB30nkc&)=!e}8}R;OdMGET+zwKl}h%7NW=kKAe0L1}eFo<1l)m+DdI z4K!xO->8Zp9@ykf?8I`(7js%m^RmjI&*;m*i8@*1aNIPz20aB##$z#V>M#MIF8O0T z)aM{ErO-7G$F}Oo($W&l{UIXaEqiM6+TlY?rDM-68+RI8hQ&{3=u=Zzrv8_+r9NGv z-90_9&TUWWkgm<2iuc2t4+m;Kjt8%@%Dl{n@ceA+Z|y^uzHxi%_c5JGH)8Q2rHF1_ zCDpYK_4d}Au^i&1XjkL;NY=kyGO$Oj^}7|?97Mp}&A2Z;Zj=mdzH)|CzhunD(}9w5 zuJ#&#R!p-aQ=Z>898aN6C9dbt00L|;7e$#L06hGFg|~R|AQH1trfsX>X#P?!2zCp< zCk39_t6KQ&DENMaG+vg{=SvtiC{DlJ4IWd${63=nJYOSfkEw0dW*y9>{DV97IS@~m z8i&a%-sXweL^aLP3N2rus}8np-fs+d$L8{=<5HU;T^qU?5r>Q3DL*!D?G+fRbQHL&ey*gIO%WEtcucU~oCMLN` z8)$z;g+~12S1&APLm+3gE7@Gd zLYhs_wag^>nK5VVamgzVQPDbF z)4stTv9~gikz(Btus<-(a2OCyTcZaS3iiQL@Dv%A$@y$aWw=p)DvdDOBw2nja(@oe z{D5jOvL8z2>+G+iDX1RMjMcdu6fBH1{iT8u zuJ3B*+)wFPNr5Q)X-X?b74v<^`o*l>v*C|iBTDWWT=#*>U!Um3Mrh@^0sy$^4Ruqo z7r=RKVH+Li&o+tn5CrB3rSn}Bqkx_9#Urjg!Qt&nT02ddLrJs0)1(jpWV4~_5Z>Qq z$f(83fmexv__}M1N^` zk(j!m7fg?>Kz+xRGnzUiSDX|?h+&+NXUr&ag1>}mc2WWW5IY1zOr(FzVuwIAm@yLW z-vZ|IKg7lSf9PR)_*d~S;s4S5)5E_8{|Nt&9)=YEWAHEGf9w76{a=@%r2yo?fxC@4 z2PpwJP9&ipx2}YgRC*Q07?kMmLj%lVf);@eBrFW7p{RKD9)@Z2r7nZah5 literal 0 HcmV?d00001 diff --git a/doc/images/demo.png b/doc/images/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..767e77f66cf09217830b556d211a9029395abd79 GIT binary patch literal 76351 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�YJiQ*E#{%7dHn@ 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 F_a8dJpCkYP literal 0 HcmV?d00001 diff --git a/doc/images/object_controller.png b/doc/images/object_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..e490e6d5371adf0afd0928ce3ff97601f7048024 GIT binary patch literal 39658 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 Yu8XGlt*q6I_7=Rfj_RpDRI?8GKW8Fbv;Y7A literal 0 HcmV?d00001 diff --git a/doc/images/qt-logo.png b/doc/images/qt-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..794162f5af58e7b38beebf472842fc9703ede19b GIT binary patch literal 4075 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 literal 0 HcmV?d00001 diff --git a/doc/images/qtbuttonpropertybrowser.png b/doc/images/qtbuttonpropertybrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..7890fdb04d942505e8198a02b913e76fd75800e8 GIT binary patch literal 7181 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 NQIJ)YDU&ks|1T1HXA1xT literal 0 HcmV?d00001 diff --git a/doc/images/qtgroupboxpropertybrowser.png b/doc/images/qtgroupboxpropertybrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..203c50f06dcdbc38cd4231b4df74879eed1a2fdf GIT binary patch literal 5819 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 literal 0 HcmV?d00001 diff --git a/doc/images/qtpropertybrowser-duplicate.png b/doc/images/qtpropertybrowser-duplicate.png new file mode 100644 index 0000000000000000000000000000000000000000..0a939fc3066d2891fc14f6dc6286c12c29cf84c1 GIT binary patch literal 1620 zcmZvcdoZd$rYM4t#Kcw`KiXuugn8u_~=&>e}OpQT_VP?6#=0{Adq|{Y6 zGG2)uq?E_?N-8QN@@5{)FinhiXvF=w-F5%EXPtHSUVDGnI^Xp^tN`; z74x`bm|}WSB&I3;)2@J8`s*u|gv6BNBkqUop8Qam6wJ8>=I<%8=+a<_N8YupfaMAw zSJv$icD!gVHbrZ_ctoq3wL=fc5aZ#qp<6+x8av>))E~`_VSyFz>man|FuJ=?Ewc{S z{k9G&zNS-pBjg_B5K^N{x15AQJ;3^z{I;sKx-rleRDGuNUDDHN3fUp-WuaC@29 z?^Ria#tmcOa8OaWRh>>}PB0vC(&NWTq#dVg_M$8OG}25C;w*3fvf3QS0_Px)#nrSI zW)<48E{9{BG4QgN1oa@s^61R(8q<3E0PZuxai`ba*QDCPD=e^~L)Ek(mvT9Td*70V zPIruh00qj(Bd^YZxKRTuM15UKgAd<$#8rgVn+I+oyb(F}{HCp6f zQnmB@O$~U(4h)?6*x_TmCTxPF{C`2DNq&K_um1(&d}0z2(ia%9E!IX6to|Z}u%Voq zy*B6L36 z++E2DROgj%eG4A|(shCoR}ic5Czv)_qtNLM1<93n#EIMDDVwCFr()xL?Q|M{2S_{+ zOWXn$G&3+(8F0|_o&|F}@D%37qBUg`mieg-fQv8{!&0v8a%erVZP?xu_l3wE)@o$zeJmyDjAaov&~K-EoyHV%|& zQbeEn>O_7ob2jh1U>p4AgLEO$=Rhg@i0=25R$A2u$&Am6<^Z6?Aop8nTIlKR&Yyg} zpF5Hgeb$we-2vntY!o5cEga+h3(Huk+Y0oA!wD+~p*$+6KjpE)V>R>}|D^|cXHKJk zAzd1lwtWY>SffT-TXm;Zc&dn+UFOzo_u6u|5aOQ(x3Z!&6wMQd8u^ipEwkB#oN9?v zqTK$|7Tt;GhI)ES@J1V8M6eO`VW@cyD=y!{5%ZK*qmJkNYz8B4246JO9klNm>kiOV zG`BfG*5xh#y&rSN6Zct%V~lwA?NWFoc*S(nOpSTe9GrQmC3qxe-Bz=70`MZs)~PBi zZ43i`=LP^5DdFvyVnuU{$%so{<^#L?%3IdDx8kcTavMx!EsGEAj-zf@t<0)-*xYMR z^U50(H(KN-82c8x9o-|(*o_UKY>PNG9`QK&`G=r7b&-Ql`^Q{c}xH(bzV-*AB^y922IX$#+J(es2-X6*{NNL2ozlw^=R8 zc1(2AG2p4`oP+AFb3n`W!#lI_QphWR{{RBv+@W4!-c;&z18f literal 0 HcmV?d00001 diff --git a/doc/images/qtpropertybrowser.png b/doc/images/qtpropertybrowser.png new file mode 100644 index 0000000000000000000000000000000000000000..1f0403af24777f12af953f7da6383420625f0c46 GIT binary patch literal 9677 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 I%yZxW0Z@#nLjV8( literal 0 HcmV?d00001 diff --git a/doc/images/simple.png b/doc/images/simple.png new file mode 100644 index 0000000000000000000000000000000000000000..56048d5dfd2f447805f1fbf7cb8e3e7a764e90e3 GIT binary patch literal 9548 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�$toe<)*el)a`rbqO*$=46wbE%2_BS&tM{ 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 literal 0 HcmV?d00001 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..af43083 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,15 @@ + +INCLUDE_DIRECTORIES( + ${${PROJECT_NAME}_SOURCE_DIR}/src + ) + +# setting a common place to put all executable files +SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + +ADD_SUBDIRECTORY(canvas_typed) +ADD_SUBDIRECTORY(canvas_variant) +ADD_SUBDIRECTORY(decoration) +ADD_SUBDIRECTORY(demo) +ADD_SUBDIRECTORY(extension) +ADD_SUBDIRECTORY(object_controller) +ADD_SUBDIRECTORY(simple) diff --git a/examples/canvas_typed/CMakeLists.txt b/examples/canvas_typed/CMakeLists.txt new file mode 100644 index 0000000..26cd220 --- /dev/null +++ b/examples/canvas_typed/CMakeLists.txt @@ -0,0 +1,19 @@ +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(example_name canvas_typed) + +SET(KIT_SRCS + main.cpp + mainwindow.cpp + mainwindow.h + qtcanvas.cpp + qtcanvas.h + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) diff --git a/examples/canvas_typed/canvas_typed.qdoc b/examples/canvas_typed/canvas_typed.qdoc new file mode 100644 index 0000000..e5b56dc --- /dev/null +++ b/examples/canvas_typed/canvas_typed.qdoc @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-canvas_typed.html + + \title Type Based Canvas Example + + This example demonstrates how to use different + QtAbstractPropertyManager subclasses for different property + types. Using this approach, the developer interfaces with a + type-safe API (since each manager has its own property-type + specific API). + + \image canvas_typed.png + + The example presents a canvas filled up with items of different + types, and a tree property browser which displays the currently + selected item's properties. + + All item types has a few common properties like "Position X", "Position Y" + or "Position Z", but each type also adds its own type-specific + properties (e.g. the text items provide "Text" and "Font" + properties, and the line items provide a "Vector" property). + + The source files can be found in examples/canvas_typed directory + of the package. + + \section1 Third party copyright notice + + The canvas class used in this example contains third party code + with the following copyright notice: + + \legalese + \code + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + \endcode + \endlegalese + +*/ diff --git a/examples/canvas_typed/main.cpp b/examples/canvas_typed/main.cpp new file mode 100644 index 0000000..6127bba --- /dev/null +++ b/examples/canvas_typed/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include "mainwindow.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + MainWindow mw; + mw.show(); + + return app.exec(); +} diff --git a/examples/canvas_typed/mainwindow.cpp b/examples/canvas_typed/mainwindow.cpp new file mode 100644 index 0000000..e6ec196 --- /dev/null +++ b/examples/canvas_typed/mainwindow.cpp @@ -0,0 +1,521 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include "mainwindow.h" +#include "qtpropertymanager.h" +#include "qteditorfactory.h" +#include "qttreepropertybrowser.h" +#include +#include +#include +#include +#include +#include + +void CanvasView::contentsMousePressEvent(QMouseEvent* event) +{ + handleMouseClickEvent(event); +} + +void CanvasView::contentsMouseDoubleClickEvent(QMouseEvent* event) +{ + handleMouseClickEvent(event); +} + +void CanvasView::handleMouseClickEvent(QMouseEvent* event) +{ + QPoint p = inverseWorldMatrix().map(event->pos()); + QtCanvasItemList l = canvas()->collisions(p); + moving = 0; + if (!l.isEmpty()) + moving = l.first(); + moving_start = p; + emit itemClicked(moving); +} + +void CanvasView::contentsMouseMoveEvent(QMouseEvent* event) +{ + if (moving) { + QPoint p = inverseWorldMatrix().map(event->pos()); + moving->moveBy(p.x() - moving_start.x(), p.y() - moving_start.y()); + moving_start = p; + canvas()->update(); + emit itemMoved(moving); + } +} + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + QMenu *editMenu = menuBar()->addMenu(tr("Edit")); + QMenu *newObjectMenu = editMenu->addMenu(tr("New Object")); + + QAction *newRectangleAction = new QAction(tr("Rectangle"), this); + connect(newRectangleAction, SIGNAL(triggered(bool)), this, SLOT(newRectangle())); + newObjectMenu->addAction(newRectangleAction); + + QAction *newLineAction = new QAction(tr("Line"), this); + connect(newLineAction, SIGNAL(triggered(bool)), this, SLOT(newLine())); + newObjectMenu->addAction(newLineAction); + + QAction *newEllipseAction = new QAction(tr("Ellipse"), this); + connect(newEllipseAction, SIGNAL(triggered(bool)), this, SLOT(newEllipse())); + newObjectMenu->addAction(newEllipseAction); + + QAction *newTextAction = new QAction(tr("Text"), this); + connect(newTextAction, SIGNAL(triggered(bool)), this, SLOT(newText())); + newObjectMenu->addAction(newTextAction); + + deleteAction = new QAction(tr("Delete Object"), this); + connect(deleteAction, SIGNAL(triggered(bool)), this, SLOT(deleteObject())); + editMenu->addAction(deleteAction); + + QAction *clearAction = new QAction(tr("Clear All"), this); + connect(clearAction, SIGNAL(triggered(bool)), this, SLOT(clearAll())); + editMenu->addAction(clearAction); + + QAction *fillAction = new QAction(tr("Fill View"), this); + connect(fillAction, SIGNAL(triggered(bool)), this, SLOT(fillView())); + editMenu->addAction(fillAction); + + doubleManager = new QtDoublePropertyManager(this); + stringManager = new QtStringPropertyManager(this); + colorManager = new QtColorPropertyManager(this); + fontManager = new QtFontPropertyManager(this); + pointManager = new QtPointPropertyManager(this); + sizeManager = new QtSizePropertyManager(this); + + connect(doubleManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(valueChanged(QtProperty *, double))); + connect(stringManager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(valueChanged(QtProperty *, const QString &))); + connect(colorManager, SIGNAL(valueChanged(QtProperty *, const QColor &)), + this, SLOT(valueChanged(QtProperty *, const QColor &))); + connect(fontManager, SIGNAL(valueChanged(QtProperty *, const QFont &)), + this, SLOT(valueChanged(QtProperty *, const QFont &))); + connect(pointManager, SIGNAL(valueChanged(QtProperty *, const QPoint &)), + this, SLOT(valueChanged(QtProperty *, const QPoint &))); + connect(sizeManager, SIGNAL(valueChanged(QtProperty *, const QSize &)), + this, SLOT(valueChanged(QtProperty *, const QSize &))); + + QtDoubleSpinBoxFactory *doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); + QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory(this); + QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(this); + QtLineEditFactory *lineEditFactory = new QtLineEditFactory(this); + QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory(this); + + canvas = new QtCanvas(800, 600); + canvasView = new CanvasView(canvas, this); + setCentralWidget(canvasView); + + QDockWidget *dock = new QDockWidget(this); + addDockWidget(Qt::RightDockWidgetArea, dock); + + propertyEditor = new QtTreePropertyBrowser(dock); + propertyEditor->setFactoryForManager(doubleManager, doubleSpinBoxFactory); + propertyEditor->setFactoryForManager(stringManager, lineEditFactory); + propertyEditor->setFactoryForManager(colorManager->subIntPropertyManager(), spinBoxFactory); + propertyEditor->setFactoryForManager(fontManager->subIntPropertyManager(), spinBoxFactory); + propertyEditor->setFactoryForManager(fontManager->subBoolPropertyManager(), checkBoxFactory); + propertyEditor->setFactoryForManager(fontManager->subEnumPropertyManager(), comboBoxFactory); + propertyEditor->setFactoryForManager(pointManager->subIntPropertyManager(), spinBoxFactory); + propertyEditor->setFactoryForManager(sizeManager->subIntPropertyManager(), spinBoxFactory); + dock->setWidget(propertyEditor); + + currentItem = 0; + + connect(canvasView, SIGNAL(itemClicked(QtCanvasItem *)), + this, SLOT(itemClicked(QtCanvasItem *))); + connect(canvasView, SIGNAL(itemMoved(QtCanvasItem *)), + this, SLOT(itemMoved(QtCanvasItem *))); + + fillView(); + itemClicked(0); +} + +void MainWindow::newRectangle() +{ + QtCanvasItem *item = addRectangle(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newEllipse() +{ + QtCanvasItem *item = addEllipse(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newLine() +{ + QtCanvasItem *item = addLine(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newText() +{ + QtCanvasItem *item = addText(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::deleteObject() +{ + if (!currentItem) + return; + + delete currentItem; + itemClicked(0); + canvas->update(); +} + +void MainWindow::clearAll() +{ + QtCanvasItemList list = canvas->allItems(); + qDeleteAll(list); + itemClicked(0); + canvas->update(); +} + +void MainWindow::fillView() +{ + for (int i = 0; i < 10; i++) { + addRectangle(); + addEllipse(); + addLine(); + addText(); + } + canvas->update(); +} + +QtCanvasItem *MainWindow::addRectangle() +{ + QtCanvasPolygonalItem *item = new QtCanvasRectangle(rand() % canvas->width(), + rand() % canvas->height(), 50, 50, canvas); + int z = rand() % 256; + item->setBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + item->setPen(QPen(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8), 4)); + item->setZ(z); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addEllipse() +{ + QtCanvasPolygonalItem *item = new QtCanvasEllipse(50, 50, canvas); + item->setBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setZ(rand() % 256); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addLine() +{ + QtCanvasLine *item = new QtCanvasLine(canvas); + item->setPoints(0, 0, rand() % canvas->width() - canvas->width() / 2, + rand() % canvas->height() - canvas->height() / 2); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setPen(QPen(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8), 6)); + item->setZ(rand() % 256); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addText() +{ + QtCanvasText *item = new QtCanvasText(canvas); + item->setText(tr("Text")); + item->setColor(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8)); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setZ(rand() % 256); + item->show(); + return item; +} + +void MainWindow::itemMoved(QtCanvasItem *item) +{ + if (item != currentItem) + return; + + doubleManager->setValue(idToProperty[QLatin1String("xpos")], item->x()); + doubleManager->setValue(idToProperty[QLatin1String("ypos")], item->y()); + doubleManager->setValue(idToProperty[QLatin1String("zpos")], item->z()); +} + +void MainWindow::updateExpandState() +{ + QList list = propertyEditor->topLevelItems(); + QListIterator it(list); + while (it.hasNext()) { + QtBrowserItem *item = it.next(); + QtProperty *prop = item->property(); + idToExpanded[propertyToId[prop]] = propertyEditor->isExpanded(item); + } +} + +void MainWindow::itemClicked(QtCanvasItem *item) +{ + updateExpandState(); + + QMap::ConstIterator itProp = propertyToId.constBegin(); + while (itProp != propertyToId.constEnd()) { + delete itProp.key(); + itProp++; + } + propertyToId.clear(); + idToProperty.clear(); + + currentItem = item; + if (!currentItem) { + deleteAction->setEnabled(false); + return; + } + + deleteAction->setEnabled(true); + + QtProperty *property; + + property = doubleManager->addProperty(tr("Position X")); + doubleManager->setRange(property, 0, canvas->width()); + doubleManager->setValue(property, item->x()); + addProperty(property, QLatin1String("xpos")); + + property = doubleManager->addProperty(tr("Position Y")); + doubleManager->setRange(property, 0, canvas->height()); + doubleManager->setValue(property, item->y()); + addProperty(property, QLatin1String("ypos")); + + property = doubleManager->addProperty(tr("Position Z")); + doubleManager->setRange(property, 0, 256); + doubleManager->setValue(property, item->z()); + addProperty(property, QLatin1String("zpos")); + + if (item->rtti() == QtCanvasItem::Rtti_Rectangle) { + QtCanvasRectangle *i = (QtCanvasRectangle *)item; + + property = colorManager->addProperty(tr("Brush Color")); + colorManager->setValue(property, i->brush().color()); + addProperty(property, QLatin1String("brush")); + + property = colorManager->addProperty(tr("Pen Color")); + colorManager->setValue(property, i->pen().color()); + addProperty(property, QLatin1String("pen")); + + property = sizeManager->addProperty(tr("Size")); + sizeManager->setValue(property, i->size()); + addProperty(property, QLatin1String("size")); + } else if (item->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasLine *i = (QtCanvasLine *)item; + + property = colorManager->addProperty(tr("Pen Color")); + colorManager->setValue(property, i->pen().color()); + addProperty(property, QLatin1String("pen")); + + property = pointManager->addProperty(tr("Vector")); + pointManager->setValue(property, i->endPoint()); + addProperty(property, QLatin1String("endpoint")); + } else if (item->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasEllipse *i = (QtCanvasEllipse *)item; + + property = colorManager->addProperty(tr("Brush Color")); + colorManager->setValue(property, i->brush().color()); + addProperty(property, QLatin1String("brush")); + + property = sizeManager->addProperty(tr("Size")); + sizeManager->setValue(property, QSize(i->width(), i->height())); + sizeManager->setRange(property, QSize(0, 0), QSize(1000, 1000)); + addProperty(property, QLatin1String("size")); + } else if (item->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)item; + + property = colorManager->addProperty(tr("Color")); + colorManager->setValue(property, i->color()); + addProperty(property, QLatin1String("color")); + + property = stringManager->addProperty(tr("Text")); + stringManager->setValue(property, i->text()); + addProperty(property, QLatin1String("text")); + + property = fontManager->addProperty(tr("Font")); + fontManager->setValue(property, i->font()); + addProperty(property, QLatin1String("font")); + } +} + +void MainWindow::addProperty(QtProperty *property, const QString &id) +{ + propertyToId[property] = id; + idToProperty[id] = property; + QtBrowserItem *item = propertyEditor->addProperty(property); + if (idToExpanded.contains(id)) + propertyEditor->setExpanded(item, idToExpanded[id]); +} + +void MainWindow::valueChanged(QtProperty *property, double value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("xpos")) { + currentItem->setX(value); + } else if (id == QLatin1String("ypos")) { + currentItem->setY(value); + } else if (id == QLatin1String("zpos")) { + currentItem->setZ(value); + } + canvas->update(); +} + +void MainWindow::valueChanged(QtProperty *property, const QString &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("text")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setText(value); + } + } + canvas->update(); +} + +void MainWindow::valueChanged(QtProperty *property, const QColor &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("color")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setColor(value); + } + } else if (id == QLatin1String("brush")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle || + currentItem->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasPolygonalItem *i = (QtCanvasPolygonalItem *)currentItem; + QBrush b = i->brush(); + b.setColor(value); + i->setBrush(b); + } + } else if (id == QLatin1String("pen")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle || + currentItem->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasPolygonalItem *i = (QtCanvasPolygonalItem *)currentItem; + QPen p = i->pen(); + p.setColor(value); + i->setPen(p); + } + } + canvas->update(); +} + +void MainWindow::valueChanged(QtProperty *property, const QFont &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("font")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setFont(value); + } + } + canvas->update(); +} + +void MainWindow::valueChanged(QtProperty *property, const QPoint &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (currentItem->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasLine *i = (QtCanvasLine *)currentItem; + if (id == QLatin1String("endpoint")) { + i->setPoints(i->startPoint().x(), i->startPoint().y(), value.x(), value.y()); + } + } + canvas->update(); +} + +void MainWindow::valueChanged(QtProperty *property, const QSize &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("size")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle) { + QtCanvasRectangle *i = (QtCanvasRectangle *)currentItem; + i->setSize(value.width(), value.height()); + } else if (currentItem->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasEllipse *i = (QtCanvasEllipse *)currentItem; + i->setSize(value.width(), value.height()); + } + } + canvas->update(); +} + diff --git a/examples/canvas_typed/mainwindow.h b/examples/canvas_typed/mainwindow.h new file mode 100644 index 0000000..64c46ca --- /dev/null +++ b/examples/canvas_typed/mainwindow.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "qtcanvas.h" + +class QtProperty; + +class CanvasView : public QtCanvasView +{ + Q_OBJECT +public: + CanvasView(QWidget *parent = 0) + : QtCanvasView(parent), moving(0) { } + CanvasView(QtCanvas *canvas, QWidget *parent = 0) + : QtCanvasView(canvas, parent), moving(0) { } +signals: + void itemClicked(QtCanvasItem *item); + void itemMoved(QtCanvasItem *item); +protected: + void contentsMousePressEvent(QMouseEvent *event); + void contentsMouseDoubleClickEvent(QMouseEvent *event); + void contentsMouseMoveEvent(QMouseEvent* event); +private: + void handleMouseClickEvent(QMouseEvent *event); + QPoint moving_start; + QtCanvasItem *moving; +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget *parent = 0); + +private slots: + void newRectangle(); + void newEllipse(); + void newLine(); + void newText(); + void deleteObject(); + void clearAll(); + void fillView(); + + void itemClicked(QtCanvasItem *item); + void itemMoved(QtCanvasItem *item); + void valueChanged(QtProperty *property, double value); + void valueChanged(QtProperty *property, const QString &value); + void valueChanged(QtProperty *property, const QColor &value); + void valueChanged(QtProperty *property, const QFont &value); + void valueChanged(QtProperty *property, const QPoint &value); + void valueChanged(QtProperty *property, const QSize &value); +private: + + QtCanvasItem *addRectangle(); + QtCanvasItem *addEllipse(); + QtCanvasItem *addLine(); + QtCanvasItem *addText(); + void addProperty(QtProperty *property, const QString &id); + void updateExpandState(); + + QAction *deleteAction; + + class QtDoublePropertyManager *doubleManager; + class QtStringPropertyManager *stringManager; + class QtColorPropertyManager *colorManager; + class QtFontPropertyManager *fontManager; + class QtPointPropertyManager *pointManager; + class QtSizePropertyManager *sizeManager; + + class QtTreePropertyBrowser *propertyEditor; + CanvasView *canvasView; + QtCanvas *canvas; + QtCanvasItem *currentItem; + QMap propertyToId; + QMap idToProperty; + QMap idToExpanded; +}; + +#endif diff --git a/examples/canvas_typed/qtcanvas.cpp b/examples/canvas_typed/qtcanvas.cpp new file mode 100644 index 0000000..1e297c5 --- /dev/null +++ b/examples/canvas_typed/qtcanvas.cpp @@ -0,0 +1,5906 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include "qtcanvas.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace Qt; + +class QtCanvasData { +public: + QtCanvasData() + { + } + + QList viewList; + QSet itemDict; + QSet animDict; +}; + +class QtCanvasViewData { +public: + QtCanvasViewData() {} + QMatrix xform; + QMatrix ixform; + bool highQuality; +}; + +// clusterizer + +class QtCanvasClusterizer { +public: + QtCanvasClusterizer(int maxclusters); + ~QtCanvasClusterizer(); + + void add(int x, int y); // 1x1 rectangle (point) + void add(int x, int y, int w, int h); + void add(const QRect& rect); + + void clear(); + int clusters() const { return count; } + const QRect& operator[](int i) const; + +private: + QRect* cluster; + int count; + const int maxcl; +}; + +static +void include(QRect& r, const QRect& rect) +{ + if (rect.left() < r.left()) { + r.setLeft(rect.left()); + } + if (rect.right()>r.right()) { + r.setRight(rect.right()); + } + if (rect.top() < r.top()) { + r.setTop(rect.top()); + } + if (rect.bottom()>r.bottom()) { + r.setBottom(rect.bottom()); + } +} + +/* +A QtCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles +by a merging heuristic. +*/ +QtCanvasClusterizer::QtCanvasClusterizer(int maxclusters) : + cluster(new QRect[maxclusters]), + count(0), + maxcl(maxclusters) +{ } + +QtCanvasClusterizer::~QtCanvasClusterizer() +{ + delete [] cluster; +} + +void QtCanvasClusterizer::clear() +{ + count = 0; +} + +void QtCanvasClusterizer::add(int x, int y) +{ + add(QRect(x, y, 1, 1)); +} + +void QtCanvasClusterizer::add(int x, int y, int w, int h) +{ + add(QRect(x, y, w, h)); +} + +void QtCanvasClusterizer::add(const QRect& rect) +{ + QRect biggerrect(rect.x()-1, rect.y()-1, rect.width()+2, rect.height()+2); + + //assert(rect.width()>0 && rect.height()>0); + + int cursor; + + for (cursor = 0; cursor < count; cursor++) { + if (cluster[cursor].contains(rect)) { + // Wholly contained already. + return; + } + } + + int lowestcost = 9999999; + int cheapest = -1; + cursor = 0; + while(cursor < count) { + if (cluster[cursor].intersects(biggerrect)) { + QRect larger = cluster[cursor]; + include(larger, rect); + int cost = larger.width()*larger.height() - + cluster[cursor].width()*cluster[cursor].height(); + + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapest = cursor; + lowestcost = cost; + } + } + } + cursor++; + } + + if (cheapest>= 0) { + include(cluster[cheapest], rect); + return; + } + + if (count < maxcl) { + cluster[count++] = rect; + return; + } + + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + + lowestcost = 9999999; + cheapest = -1; + cursor = 0; + while(cursor < count) { + QRect larger = cluster[cursor]; + include(larger, rect); + int cost = larger.width()*larger.height() + - cluster[cursor].width()*cluster[cursor].height(); + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapest = cursor; + lowestcost = cost; + } + } + cursor++; + } + + // ### + // could make an heuristic guess as to whether we need to bother + // looking for a cheap merge. + + int cheapestmerge1 = -1; + int cheapestmerge2 = -1; + + int merge1 = 0; + while(merge1 < count) { + int merge2 = 0; + while(merge2 < count) { + if(merge1!= merge2) { + QRect larger = cluster[merge1]; + include(larger, cluster[merge2]); + int cost = larger.width()*larger.height() + - cluster[merge1].width()*cluster[merge1].height() + - cluster[merge2].width()*cluster[merge2].height(); + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapestmerge1 = merge1; + cheapestmerge2 = merge2; + lowestcost = cost; + } + } + } + merge2++; + } + merge1++; + } + + if (cheapestmerge1>= 0) { + include(cluster[cheapestmerge1], cluster[cheapestmerge2]); + cluster[cheapestmerge2] = cluster[count--]; + } else { + // if (!cheapest) debugRectangles(rect); + include(cluster[cheapest], rect); + } + + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x, y) are ordered topleft + // to bottomright. + + // ### + // + // add explicit x/y ordering to that comment, move it to the top + // and rephrase it as pre-/post-conditions. +} + +const QRect& QtCanvasClusterizer::operator[](int i) const +{ + return cluster[i]; +} + +// end of clusterizer + + +class QtCanvasItemLess +{ +public: + inline bool operator()(const QtCanvasItem *i1, const QtCanvasItem *i2) const + { + if (i1->z() == i2->z()) + return i1 > i2; + return (i1->z() > i2->z()); + } +}; + + +class QtCanvasChunk { +public: + QtCanvasChunk() : changed(true) { } + // Other code assumes lists are not deleted. Assignment is also + // done on ChunkRecs. So don't add that sort of thing here. + + void sort() + { + qSort(m_list.begin(), m_list.end(), QtCanvasItemLess()); + } + + const QtCanvasItemList &list() const + { + return m_list; + } + + void add(QtCanvasItem* item) + { + m_list.prepend(item); + changed = true; + } + + void remove(QtCanvasItem* item) + { + m_list.removeAll(item); + changed = true; + } + + void change() + { + changed = true; + } + + bool hasChanged() const + { + return changed; + } + + bool takeChange() + { + bool y = changed; + changed = false; + return y; + } + +private: + QtCanvasItemList m_list; + bool changed; +}; + + +static int gcd(int a, int b) +{ + int r; + while ((r = a%b)) { + a = b; + b = r; + } + return b; +} + +static int scm(int a, int b) +{ + int g = gcd(a, b); + return a/g*b; +} + + + +/* + \class QtCanvas qtcanvas.h + \brief The QtCanvas class provides a 2D area that can contain QtCanvasItem objects. + + The QtCanvas class manages its 2D graphic area and all the canvas + items the area contains. The canvas has no visual appearance of + its own. Instead, it is displayed on screen using a QtCanvasView. + Multiple QtCanvasView widgets may be associated with a canvas to + provide multiple views of the same canvas. + + The canvas is optimized for large numbers of items, particularly + where only a small percentage of the items change at any + one time. If the entire display changes very frequently, you should + consider using your own custom QtScrollView subclass. + + Qt provides a rich + set of canvas item classes, e.g. QtCanvasEllipse, QtCanvasLine, + QtCanvasPolygon, QtCanvasPolygonalItem, QtCanvasRectangle, QtCanvasSpline, + QtCanvasSprite and QtCanvasText. You can subclass to create your own + canvas items; QtCanvasPolygonalItem is the most common base class used + for this purpose. + + Items appear on the canvas after their \link QtCanvasItem::show() + show()\endlink function has been called (or \link + QtCanvasItem::setVisible() setVisible(true)\endlink), and \e after + update() has been called. The canvas only shows items that are + \link QtCanvasItem::setVisible() visible\endlink, and then only if + \l update() is called. (By default the canvas is white and so are + canvas items, so if nothing appears try changing colors.) + + If you created the canvas without passing a width and height to + the constructor you must also call resize(). + + Although a canvas may appear to be similar to a widget with child + widgets, there are several notable differences: + + \list + \i Canvas items are usually much faster to manipulate and redraw than + child widgets, with the speed advantage becoming especially great when + there are \e many canvas items and non-rectangular items. In most + situations canvas items are also a lot more memory efficient than child + widgets. + + \i It's easy to detect overlapping items (collision detection). + + \i The canvas can be larger than a widget. A million-by-million canvas + is perfectly possible. At such a size a widget might be very + inefficient, and some window systems might not support it at all, + whereas QtCanvas scales well. Even with a billion pixels and a million + items, finding a particular canvas item, detecting collisions, etc., + is still fast (though the memory consumption may be prohibitive + at such extremes). + + \i Two or more QtCanvasView objects can view the same canvas. + + \i An arbitrary transformation matrix can be set on each QtCanvasView + which makes it easy to zoom, rotate or shear the viewed canvas. + + \i Widgets provide a lot more functionality, such as input (QKeyEvent, + QMouseEvent etc.) and layout management (QGridLayout etc.). + + \endlist + + A canvas consists of a background, a number of canvas items organized by + x, y and z coordinates, and a foreground. A canvas item's z coordinate + can be treated as a layer number -- canvas items with a higher z + coordinate appear in front of canvas items with a lower z coordinate. + + The background is white by default, but can be set to a different color + using setBackgroundColor(), or to a repeated pixmap using + setBackgroundPixmap() or to a mosaic of smaller pixmaps using + setTiles(). Individual tiles can be set with setTile(). There + are corresponding get functions, e.g. backgroundColor() and + backgroundPixmap(). + + Note that QtCanvas does not inherit from QWidget, even though it has some + functions which provide the same functionality as those in QWidget. One + of these is setBackgroundPixmap(); some others are resize(), size(), + width() and height(). \l QtCanvasView is the widget used to display a + canvas on the screen. + + Canvas items are added to a canvas by constructing them and passing the + canvas to the canvas item's constructor. An item can be moved to a + different canvas using QtCanvasItem::setCanvas(). + + Canvas items are movable (and in the case of QtCanvasSprites, animated) + objects that inherit QtCanvasItem. Each canvas item has a position on the + canvas (x, y coordinates) and a height (z coordinate), all of which are + held as floating-point numbers. Moving canvas items also have x and y + velocities. It's possible for a canvas item to be outside the canvas + (for example QtCanvasItem::x() is greater than width()). When a canvas + item is off the canvas, onCanvas() returns false and the canvas + disregards the item. (Canvas items off the canvas do not slow down any + of the common operations on the canvas.) + + Canvas items can be moved with QtCanvasItem::move(). The advance() + function moves all QtCanvasItem::animated() canvas items and + setAdvancePeriod() makes QtCanvas move them automatically on a periodic + basis. In the context of the QtCanvas classes, to `animate' a canvas item + is to set it in motion, i.e. using QtCanvasItem::setVelocity(). Animation + of a canvas item itself, i.e. items which change over time, is enabled + by calling QtCanvasSprite::setFrameAnimation(), or more generally by + subclassing and reimplementing QtCanvasItem::advance(). To detect collisions + use one of the QtCanvasItem::collisions() functions. + + The changed parts of the canvas are redrawn (if they are visible in a + canvas view) whenever update() is called. You can either call update() + manually after having changed the contents of the canvas, or force + periodic updates using setUpdatePeriod(). If you have moving objects on + the canvas, you must call advance() every time the objects should + move one step further. Periodic calls to advance() can be forced using + setAdvancePeriod(). The advance() function will call + QtCanvasItem::advance() on every item that is \link + QtCanvasItem::animated() animated\endlink and trigger an update of the + affected areas afterwards. (A canvas item that is `animated' is simply + a canvas item that is in motion.) + + QtCanvas organizes its canvas items into \e chunks; these are areas on + the canvas that are used to speed up most operations. Many operations + start by eliminating most chunks (i.e. those which haven't changed) + and then process only the canvas items that are in the few interesting + (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on + the canvas. + + The chunk size is a key factor to QtCanvas's speed: if there are too many + chunks, the speed benefit of grouping canvas items into chunks is + reduced. If the chunks are too large, it takes too long to process each + one. The QtCanvas constructor tries to pick a suitable size, but you + can call retune() to change it at any time. The chunkSize() function + returns the current chunk size. The canvas items always make sure + they're in the right chunks; all you need to make sure of is that + the canvas uses the right chunk size. A good rule of thumb is that + the size should be a bit smaller than the average canvas item + size. If you have moving objects, the chunk size should be a bit + smaller than the average size of the moving items. + + The foreground is normally nothing, but if you reimplement + drawForeground(), you can draw things in front of all the canvas + items. + + Areas can be set as changed with setChanged() and set unchanged with + setUnchanged(). The entire canvas can be set as changed with + setAllChanged(). A list of all the items on the canvas is returned by + allItems(). + + An area can be copied (painted) to a QPainter with drawArea(). + + If the canvas is resized it emits the resized() signal. + + The examples/canvas application and the 2D graphics page of the + examples/demo application demonstrate many of QtCanvas's facilities. + + \sa QtCanvasView QtCanvasItem +*/ +void QtCanvas::init(int w, int h, int chunksze, int mxclusters) +{ + d = new QtCanvasData; + awidth = w; + aheight = h; + chunksize = chunksze; + maxclusters = mxclusters; + chwidth = (w+chunksize-1)/chunksize; + chheight = (h+chunksize-1)/chunksize; + chunks = new QtCanvasChunk[chwidth*chheight]; + update_timer = 0; + bgcolor = white; + grid = 0; + htiles = 0; + vtiles = 0; + debug_redraw_areas = false; +} + +/* + Create a QtCanvas with no size. \a parent is passed to the QObject + superclass. + + \warning You \e must call resize() at some time after creation to + be able to use the canvas. +*/ +QtCanvas::QtCanvas(QObject* parent) + : QObject(parent) +{ + init(0, 0); +} + +/* + Constructs a QtCanvas that is \a w pixels wide and \a h pixels high. +*/ +QtCanvas::QtCanvas(int w, int h) +{ + init(w, h); +} + +/* + Constructs a QtCanvas which will be composed of \a h tiles + horizontally and \a v tiles vertically. Each tile will be an image + \a tilewidth by \a tileheight pixels taken from pixmap \a p. + + The pixmap \a p is a list of tiles, arranged left to right, (and + in the case of pixmaps that have multiple rows of tiles, top to + bottom), with tile 0 in the top-left corner, tile 1 next to the + right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + The QtCanvas is initially sized to show exactly the given number of + tiles horizontally and vertically. If it is resized to be larger, + the entire matrix of tiles will be repeated as often as necessary + to cover the area. If it is smaller, tiles to the right and bottom + will not be visible. + + \sa setTiles() +*/ +QtCanvas::QtCanvas(QPixmap p, + int h, int v, int tilewidth, int tileheight) +{ + init(h*tilewidth, v*tileheight, scm(tilewidth, tileheight)); + setTiles(p, h, v, tilewidth, tileheight); +} + +/* + Destroys the canvas and all the canvas's canvas items. +*/ +QtCanvas::~QtCanvas() +{ + for (int i = 0; i < d->viewList.size(); ++i) + d->viewList[i]->viewing = 0; + QtCanvasItemList all = allItems(); + for (QtCanvasItemList::Iterator it = all.begin(); it!= all.end(); ++it) + delete *it; + delete [] chunks; + delete [] grid; + delete d; +} + +/* +\internal +Returns the chunk at a chunk position \a i, \a j. +*/ +QtCanvasChunk& QtCanvas::chunk(int i, int j) const +{ + return chunks[i+chwidth*j]; +} + +/* +\internal +Returns the chunk at a pixel position \a x, \a y. +*/ +QtCanvasChunk& QtCanvas::chunkContaining(int x, int y) const +{ + return chunk(x/chunksize, y/chunksize); +} + +/* + Returns a list of all the items in the canvas. +*/ +QtCanvasItemList QtCanvas::allItems() +{ + return d->itemDict.toList(); +} + + +/* + Changes the size of the canvas to have a width of \a w and a + height of \a h. This is a slow operation. +*/ +void QtCanvas::resize(int w, int h) +{ + if (awidth == w && aheight == h) + return; + + QList hidden; + for (QSet::const_iterator it = d->itemDict.begin(); it != d->itemDict.end(); ++it) { + if ((*it)->isVisible()) { + (*it)->hide(); + hidden.append(*it); + } + } + + int nchwidth = (w+chunksize-1)/chunksize; + int nchheight = (h+chunksize-1)/chunksize; + + QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth*nchheight]; + + // Commit the new values. + // + awidth = w; + aheight = h; + chwidth = nchwidth; + chheight = nchheight; + delete [] chunks; + chunks = newchunks; + + for (int i = 0; i < hidden.size(); ++i) + hidden.at(i)->show(); + + setAllChanged(); + + emit resized(); +} + +/* + \fn void QtCanvas::resized() + + This signal is emitted whenever the canvas is resized. Each + QtCanvasView connects to this signal to keep the scrollview's size + correct. +*/ + +/* + Change the efficiency tuning parameters to \a mxclusters clusters, + each of size \a chunksze. This is a slow operation if there are + many objects on the canvas. + + The canvas is divided into chunks which are rectangular areas \a + chunksze wide by \a chunksze high. Use a chunk size which is about + the average size of the canvas items. If you choose a chunk size + which is too small it will increase the amount of calculation + required when drawing since each change will affect many chunks. + If you choose a chunk size which is too large the amount of + drawing required will increase because for each change, a lot of + drawing will be required since there will be many (unchanged) + canvas items which are in the same chunk as the changed canvas + items. + + Internally, a canvas uses a low-resolution "chunk matrix" to keep + track of all the items in the canvas. A 64x64 chunk matrix is the + default for a 1024x1024 pixel canvas, where each chunk collects + canvas items in a 16x16 pixel square. This default is also + affected by setTiles(). You can tune this default using this + function. For example if you have a very large canvas and want to + trade off speed for memory then you might set the chunk size to 32 + or 64. + + The \a mxclusters argument is the number of rectangular groups of + chunks that will be separately drawn. If the canvas has a large + number of small, dispersed items, this should be about that + number. Our testing suggests that a large number of clusters is + almost always best. + +*/ +void QtCanvas::retune(int chunksze, int mxclusters) +{ + maxclusters = mxclusters; + + if (chunksize!= chunksze) { + QList hidden; + for (QSet::const_iterator it = d->itemDict.begin(); it != d->itemDict.end(); ++it) { + if ((*it)->isVisible()) { + (*it)->hide(); + hidden.append(*it); + } + } + + chunksize = chunksze; + + int nchwidth = (awidth+chunksize-1)/chunksize; + int nchheight = (aheight+chunksize-1)/chunksize; + + QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth*nchheight]; + + // Commit the new values. + // + chwidth = nchwidth; + chheight = nchheight; + delete [] chunks; + chunks = newchunks; + + for (int i = 0; i < hidden.size(); ++i) + hidden.at(i)->show(); + } +} + +/* + \fn int QtCanvas::width() const + + Returns the width of the canvas, in pixels. +*/ + +/* + \fn int QtCanvas::height() const + + Returns the height of the canvas, in pixels. +*/ + +/* + \fn QSize QtCanvas::size() const + + Returns the size of the canvas, in pixels. +*/ + +/* + \fn QRect QtCanvas::rect() const + + Returns a rectangle the size of the canvas. +*/ + + +/* + \fn bool QtCanvas::onCanvas(int x, int y) const + + Returns true if the pixel position (\a x, \a y) is on the canvas; + otherwise returns false. + + \sa validChunk() +*/ + +/* + \fn bool QtCanvas::onCanvas(const QPoint& p) const + \overload + + Returns true if the pixel position \a p is on the canvas; + otherwise returns false. + + \sa validChunk() +*/ + +/* + \fn bool QtCanvas::validChunk(int x, int y) const + + Returns true if the chunk position (\a x, \a y) is on the canvas; + otherwise returns false. + + \sa onCanvas() +*/ + +/* + \fn bool QtCanvas::validChunk(const QPoint& p) const + \overload + + Returns true if the chunk position \a p is on the canvas; otherwise + returns false. + + \sa onCanvas() +*/ + +/* + \fn int QtCanvas::chunkSize() const + + Returns the chunk size of the canvas. + + \sa retune() +*/ + +/* +\fn bool QtCanvas::sameChunk(int x1, int y1, int x2, int y2) const +\internal +Tells if the points (\a x1, \a y1) and (\a x2, \a y2) are within the same chunk. +*/ + +/* +\internal +This method adds an the item \a item to the list of QtCanvasItem objects +in the QtCanvas. The QtCanvasItem class calls this. +*/ +void QtCanvas::addItem(QtCanvasItem* item) +{ + d->itemDict.insert(item); +} + +/* +\internal +This method adds the item \a item to the list of QtCanvasItem objects +to be moved. The QtCanvasItem class calls this. +*/ +void QtCanvas::addAnimation(QtCanvasItem* item) +{ + d->animDict.insert(item); +} + +/* +\internal +This method adds the item \a item to the list of QtCanvasItem objects +which are no longer to be moved. The QtCanvasItem class calls this. +*/ +void QtCanvas::removeAnimation(QtCanvasItem* item) +{ + d->animDict.remove(item); +} + +/* +\internal +This method removes the item \a item from the list of QtCanvasItem objects +in this QtCanvas. The QtCanvasItem class calls this. +*/ +void QtCanvas::removeItem(QtCanvasItem* item) +{ + d->itemDict.remove(item); +} + +/* +\internal +This method adds the view \a view to the list of QtCanvasView objects +viewing this QtCanvas. The QtCanvasView class calls this. +*/ +void QtCanvas::addView(QtCanvasView* view) +{ + d->viewList.append(view); + if (htiles>1 || vtiles>1 || pm.isNull()) { + QPalette::ColorRole role = view->widget()->backgroundRole(); + QPalette viewPalette = view->widget()->palette(); + viewPalette.setColor(role, backgroundColor()); + view->widget()->setPalette(viewPalette); + } +} + +/* +\internal +This method removes the view \a view from the list of QtCanvasView objects +viewing this QtCanvas. The QtCanvasView class calls this. +*/ +void QtCanvas::removeView(QtCanvasView* view) +{ + d->viewList.removeAll(view); +} + +/* + Sets the canvas to call advance() every \a ms milliseconds. Any + previous setting by setAdvancePeriod() or setUpdatePeriod() is + overridden. + + If \a ms is less than 0 advancing will be stopped. +*/ +void QtCanvas::setAdvancePeriod(int ms) +{ + if (ms < 0) { + if (update_timer) + update_timer->stop(); + } else { + if (update_timer) + delete update_timer; + update_timer = new QTimer(this); + connect(update_timer, SIGNAL(timeout()), this, SLOT(advance())); + update_timer->start(ms); + } +} + +/* + Sets the canvas to call update() every \a ms milliseconds. Any + previous setting by setAdvancePeriod() or setUpdatePeriod() is + overridden. + + If \a ms is less than 0 automatic updating will be stopped. +*/ +void QtCanvas::setUpdatePeriod(int ms) +{ + if (ms < 0) { + if (update_timer) + update_timer->stop(); + } else { + if (update_timer) + delete update_timer; + update_timer = new QTimer(this); + connect(update_timer, SIGNAL(timeout()), this, SLOT(update())); + update_timer->start(ms); + } +} + +/* + Moves all QtCanvasItem::animated() canvas items on the canvas and + refreshes all changes to all views of the canvas. (An `animated' + item is an item that is in motion; see setVelocity().) + + The advance takes place in two phases. In phase 0, the + QtCanvasItem::advance() function of each QtCanvasItem::animated() + canvas item is called with paramater 0. Then all these canvas + items are called again, with parameter 1. In phase 0, the canvas + items should not change position, merely examine other items on + the canvas for which special processing is required, such as + collisions between items. In phase 1, all canvas items should + change positions, ignoring any other items on the canvas. This + two-phase approach allows for considerations of "fairness", + although no QtCanvasItem subclasses supplied with Qt do anything + interesting in phase 0. + + The canvas can be configured to call this function periodically + with setAdvancePeriod(). + + \sa update() +*/ +void QtCanvas::advance() +{ + QSetIterator it = d->animDict; + while (it.hasNext()) { + QtCanvasItem *i = it.next(); + if (i) + i->advance(0); + } + // we expect the dict contains the exact same items as in the + // first pass. + it.toFront(); + while (it.hasNext()) { + QtCanvasItem* i = it.next(); + if (i) + i->advance(1); + } + update(); +} + +// Don't call this unless you know what you're doing. +// p is in the content's co-ordinate example. +/* + \internal +*/ +void QtCanvas::drawViewArea(QtCanvasView* view, QPainter* p, const QRect& vr, bool) +{ + QMatrix wm = view->worldMatrix(); + QMatrix iwm = wm.inverted(); + // ivr = covers all chunks in vr + QRect ivr = iwm.mapRect(vr); + + p->setMatrix(wm); + drawCanvasArea(ivr, p, false); +} + +/* + Repaints changed areas in all views of the canvas. + + \sa advance() +*/ +void QtCanvas::update() +{ + QRect r = changeBounds(); + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView* view = d->viewList.at(i); + if (!r.isEmpty()) { + QRect tr = view->worldMatrix().mapRect(r); + view->widget()->update(tr); + } + } + setUnchanged(r); +} + + +/* + Marks the whole canvas as changed. + All views of the canvas will be entirely redrawn when + update() is called next. +*/ +void QtCanvas::setAllChanged() +{ + setChanged(QRect(0, 0, width(), height())); +} + +/* + Marks \a area as changed. This \a area will be redrawn in all + views that are showing it when update() is called next. +*/ +void QtCanvas::setChanged(const QRect& area) +{ + QRect thearea = area.intersected(QRect(0, 0, width(), height())); + + int mx = (thearea.x()+thearea.width()+chunksize)/chunksize; + int my = (thearea.y()+thearea.height()+chunksize)/chunksize; + if (mx>chwidth) + mx = chwidth; + if (my>chheight) + my = chheight; + + int x = thearea.x()/chunksize; + while(x < mx) { + int y = thearea.y()/chunksize; + while(y < my) { + chunk(x, y).change(); + y++; + } + x++; + } +} + +/* + Marks \a area as \e unchanged. The area will \e not be redrawn in + the views for the next update(), unless it is marked or changed + again before the next call to update(). +*/ +void QtCanvas::setUnchanged(const QRect& area) +{ + QRect thearea = area.intersected(QRect(0, 0, width(), height())); + + int mx = (thearea.x()+thearea.width()+chunksize)/chunksize; + int my = (thearea.y()+thearea.height()+chunksize)/chunksize; + if (mx>chwidth) + mx = chwidth; + if (my>chheight) + my = chheight; + + int x = thearea.x()/chunksize; + while(x < mx) { + int y = thearea.y()/chunksize; + while(y < my) { + chunk(x, y).takeChange(); + y++; + } + x++; + } +} + + +/* + \internal +*/ +QRect QtCanvas::changeBounds() +{ + QRect area = QRect(0, 0, width(), height()); + + int mx = (area.x()+area.width()+chunksize)/chunksize; + int my = (area.y()+area.height()+chunksize)/chunksize; + if (mx > chwidth) + mx = chwidth; + if (my > chheight) + my = chheight; + + QRect result; + + int x = area.x()/chunksize; + while(x < mx) { + int y = area.y()/chunksize; + while(y < my) { + QtCanvasChunk& ch = chunk(x, y); + if (ch.hasChanged()) + result |= QRect(x*chunksize, y*chunksize, chunksize + 1, chunksize + 1); + y++; + } + x++; + } + + return result; +} + +/* + Paints all canvas items that are in the area \a clip to \a + painter, using double-buffering if \a dbuf is true. + + e.g. to print the canvas to a printer: + \code + QPrinter pr; + if (pr.setup()) { + QPainter p(&pr); + canvas.drawArea(canvas.rect(), &p); + } + \endcode +*/ +void QtCanvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf) +{ + if (painter) + drawCanvasArea(clip, painter, dbuf); +} + +#include +/* + \internal +*/ +void QtCanvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool /*double_buffer*/) +{ + QRect area = inarea.intersected(QRect(0, 0, width(), height())); + + if (!p) return; // Nothing to do. + + int lx = area.x()/chunksize; + int ly = area.y()/chunksize; + int mx = area.right()/chunksize; + int my = area.bottom()/chunksize; + if (mx>= chwidth) + mx = chwidth-1; + if (my>= chheight) + my = chheight-1; + + QtCanvasItemList allvisible; + + // Stores the region within area that need to be drawn. It is relative + // to area.topLeft() (so as to keep within bounds of 16-bit XRegions) + QRegion rgn; + + for (int x = lx; x <= mx; x++) { + for (int y = ly; y <= my; y++) { + // Only reset change if all views updating, and + // wholy within area. (conservative: ignore entire boundary) + // + // Disable this to help debugging. + // + if (!p) { + if (chunk(x, y).takeChange()) { + // ### should at least make bands + rgn |= QRegion(x*chunksize-area.x(), y*chunksize-area.y(), + chunksize, chunksize); + allvisible += chunk(x, y).list(); + } + } else { + allvisible += chunk(x, y).list(); + } + } + } + qSort(allvisible.begin(), allvisible.end(), QtCanvasItemLess()); + + drawBackground(*p, area); + if (!allvisible.isEmpty()) { + QtCanvasItem* prev = 0; + for (int i = allvisible.size() - 1; i >= 0; --i) { + QtCanvasItem *g = allvisible[i]; + if (g != prev) { + g->draw(*p); + prev = g; + } + } + } + + drawForeground(*p, area); +} + +/* +\internal +This method to informs the QtCanvas that a given chunk is +`dirty' and needs to be redrawn in the next Update. + +(\a x, \a y) is a chunk location. + +The sprite classes call this. Any new derived class of QtCanvasItem +must do so too. SetChangedChunkContaining can be used instead. +*/ +void QtCanvas::setChangedChunk(int x, int y) +{ + if (validChunk(x, y)) { + QtCanvasChunk& ch = chunk(x, y); + ch.change(); + } +} + +/* +\internal +This method to informs the QtCanvas that the chunk containing a given +pixel is `dirty' and needs to be redrawn in the next Update. + +(\a x, \a y) is a pixel location. + +The item classes call this. Any new derived class of QtCanvasItem must +do so too. SetChangedChunk can be used instead. +*/ +void QtCanvas::setChangedChunkContaining(int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + QtCanvasChunk& chunk = chunkContaining(x, y); + chunk.change(); + } +} + +/* +\internal +This method adds the QtCanvasItem \a g to the list of those which need to be +drawn if the given chunk at location (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the +chunk as `dirty'. +*/ +void QtCanvas::addItemToChunk(QtCanvasItem* g, int x, int y) +{ + if (validChunk(x, y)) { + chunk(x, y).add(g); + } +} + +/* +\internal +This method removes the QtCanvasItem \a g from the list of those which need to +be drawn if the given chunk at location (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the chunk +as `dirty'. +*/ +void QtCanvas::removeItemFromChunk(QtCanvasItem* g, int x, int y) +{ + if (validChunk(x, y)) { + chunk(x, y).remove(g); + } +} + + +/* +\internal +This method adds the QtCanvasItem \a g to the list of those which need to be +drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the +chunk as `dirty'. +*/ +void QtCanvas::addItemToChunkContaining(QtCanvasItem* g, int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + chunkContaining(x, y).add(g); + } +} + +/* +\internal +This method removes the QtCanvasItem \a g from the list of those which need to +be drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. +Like SetChangedChunk and SetChangedChunkContaining, this method +marks the chunk as `dirty'. +*/ +void QtCanvas::removeItemFromChunkContaining(QtCanvasItem* g, int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + chunkContaining(x, y).remove(g); + } +} + +/* + Returns the color set by setBackgroundColor(). By default, this is + white. + + This function is not a reimplementation of + QWidget::backgroundColor() (QtCanvas is not a subclass of QWidget), + but all QtCanvasViews that are viewing the canvas will set their + backgrounds to this color. + + \sa setBackgroundColor(), backgroundPixmap() +*/ +QColor QtCanvas::backgroundColor() const +{ + return bgcolor; +} + +/* + Sets the solid background to be the color \a c. + + \sa backgroundColor(), setBackgroundPixmap(), setTiles() +*/ +void QtCanvas::setBackgroundColor(const QColor& c) +{ + if (bgcolor != c) { + bgcolor = c; + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView *view = d->viewList.at(i); + QPalette::ColorRole role = view->widget()->backgroundRole(); + QPalette viewPalette = view->widget()->palette(); + viewPalette.setColor(role, bgcolor); + view->widget()->setPalette(viewPalette); + } + setAllChanged(); + } +} + +/* + Returns the pixmap set by setBackgroundPixmap(). By default, + this is a null pixmap. + + \sa setBackgroundPixmap(), backgroundColor() +*/ +QPixmap QtCanvas::backgroundPixmap() const +{ + return pm; +} + +/* + Sets the solid background to be the pixmap \a p repeated as + necessary to cover the entire canvas. + + \sa backgroundPixmap(), setBackgroundColor(), setTiles() +*/ +void QtCanvas::setBackgroundPixmap(const QPixmap& p) +{ + setTiles(p, 1, 1, p.width(), p.height()); + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView* view = d->viewList.at(i); + view->widget()->update(); + } +} + +/* + This virtual function is called for all updates of the canvas. It + renders any background graphics using the painter \a painter, in + the area \a clip. If the canvas has a background pixmap or a tiled + background, that graphic is used, otherwise the canvas is cleared + using the background color. + + If the graphics for an area change, you must explicitly call + setChanged(const QRect&) for the result to be visible when + update() is next called. + + \sa setBackgroundColor(), setBackgroundPixmap(), setTiles() +*/ +void QtCanvas::drawBackground(QPainter& painter, const QRect& clip) +{ + if (pm.isNull()) { + painter.fillRect(clip, bgcolor); + } else if (!grid) { + for (int x = clip.x()/pm.width(); + x < (clip.x()+clip.width()+pm.width()-1)/pm.width(); x++) + { + for (int y = clip.y()/pm.height(); + y < (clip.y()+clip.height()+pm.height()-1)/pm.height(); y++) + { + painter.drawPixmap(x*pm.width(), y*pm.height(), pm); + } + } + } else { + const int x1 = clip.left()/tilew; + int x2 = clip.right()/tilew; + const int y1 = clip.top()/tileh; + int y2 = clip.bottom()/tileh; + + const int roww = pm.width()/tilew; + + for (int j = y1; j <= y2; j++) { + int jj = j%tilesVertically(); + for (int i = x1; i <= x2; i++) { + int t = tile(i%tilesHorizontally(), jj); + int tx = t % roww; + int ty = t / roww; + painter.drawPixmap(i*tilew, j*tileh, pm, + tx*tilew, ty*tileh, tilew, tileh); + } + } + } +} + +/* + This virtual function is called for all updates of the canvas. It + renders any foreground graphics using the painter \a painter, in + the area \a clip. + + If the graphics for an area change, you must explicitly call + setChanged(const QRect&) for the result to be visible when + update() is next called. + + The default is to draw nothing. +*/ +void QtCanvas::drawForeground(QPainter& painter, const QRect& clip) +{ + if (debug_redraw_areas) { + painter.setPen(red); + painter.setBrush(NoBrush); + painter.drawRect(clip); + } +} + +/* + Sets the QtCanvas to be composed of \a h tiles horizontally and \a + v tiles vertically. Each tile will be an image \a tilewidth by \a + tileheight pixels from pixmap \a p. + + The pixmap \a p is a list of tiles, arranged left to right, (and + in the case of pixmaps that have multiple rows of tiles, top to + bottom), with tile 0 in the top-left corner, tile 1 next to the + right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + If the canvas is larger than the matrix of tiles, the entire + matrix is repeated as necessary to cover the whole canvas. If it + is smaller, tiles to the right and bottom are not visible. + + The width and height of \a p must be a multiple of \a tilewidth + and \a tileheight. If they are not the function will do nothing. + + If you want to unset any tiling set, then just pass in a null + pixmap and 0 for \a h, \a v, \a tilewidth, and + \a tileheight. +*/ +void QtCanvas::setTiles(QPixmap p, + int h, int v, int tilewidth, int tileheight) +{ + if (!p.isNull() && (!tilewidth || !tileheight || + p.width() % tilewidth != 0 || p.height() % tileheight != 0)) + return; + + htiles = h; + vtiles = v; + delete[] grid; + pm = p; + if (h && v && !p.isNull()) { + grid = new ushort[h*v]; + memset(grid, 0, h*v*sizeof(ushort)); + tilew = tilewidth; + tileh = tileheight; + } else { + grid = 0; + } + if (h + v > 10) { + int s = scm(tilewidth, tileheight); + retune(s < 128 ? s : qMax(tilewidth, tileheight)); + } + setAllChanged(); +} + +/* + \fn int QtCanvas::tile(int x, int y) const + + Returns the tile at position (\a x, \a y). Initially, all tiles + are 0. + + The parameters must be within range, i.e. + 0 \< \a x \< tilesHorizontally() and + 0 \< \a y \< tilesVertically(). + + \sa setTile() +*/ + +/* + \fn int QtCanvas::tilesHorizontally() const + + Returns the number of tiles horizontally. +*/ + +/* + \fn int QtCanvas::tilesVertically() const + + Returns the number of tiles vertically. +*/ + +/* + \fn int QtCanvas::tileWidth() const + + Returns the width of each tile. +*/ + +/* + \fn int QtCanvas::tileHeight() const + + Returns the height of each tile. +*/ + + +/* + Sets the tile at (\a x, \a y) to use tile number \a tilenum, which + is an index into the tile pixmaps. The canvas will update + appropriately when update() is next called. + + The images are taken from the pixmap set by setTiles() and are + arranged left to right, (and in the case of pixmaps that have + multiple rows of tiles, top to bottom), with tile 0 in the + top-left corner, tile 1 next to the right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + \sa tile() setTiles() +*/ +void QtCanvas::setTile(int x, int y, int tilenum) +{ + ushort& t = grid[x+y*htiles]; + if (t != tilenum) { + t = tilenum; + if (tilew == tileh && tilew == chunksize) + setChangedChunk(x, y); // common case + else + setChanged(QRect(x*tilew, y*tileh, tilew, tileh)); + } +} + + +// lesser-used data in canvas item, plus room for extension. +// Be careful adding to this - check all usages. +class QtCanvasItemExtra { + QtCanvasItemExtra() : vx(0.0), vy(0.0) { } + double vx, vy; + friend class QtCanvasItem; +}; + + +/* + \class QtCanvasItem qtcanvas.h + \brief The QtCanvasItem class provides an abstract graphic object on a QtCanvas. + + A variety of QtCanvasItem subclasses provide immediately usable + behaviour. This class is a pure abstract superclass providing the + behaviour that is shared among all the concrete canvas item classes. + QtCanvasItem is not intended for direct subclassing. It is much easier + to subclass one of its subclasses, e.g. QtCanvasPolygonalItem (the + commonest base class), QtCanvasRectangle, QtCanvasSprite, QtCanvasEllipse + or QtCanvasText. + + Canvas items are added to a canvas by constructing them and passing the + canvas to the canvas item's constructor. An item can be moved to a + different canvas using setCanvas(). + + Items appear on the canvas after their \link show() show()\endlink + function has been called (or \link setVisible() + setVisible(true)\endlink), and \e after update() has been called. The + canvas only shows items that are \link setVisible() visible\endlink, + and then only if \l update() is called. If you created the canvas + without passing a width and height to the constructor you'll also need + to call \link QtCanvas::resize() resize()\endlink. Since the canvas + background defaults to white and canvas items default to white, + you may need to change colors to see your items. + + A QtCanvasItem object can be moved in the x(), y() and z() dimensions + using functions such as move(), moveBy(), setX(), setY() and setZ(). A + canvas item can be set in motion, `animated', using setAnimated() and + given a velocity in the x and y directions with setXVelocity() and + setYVelocity() -- the same effect can be achieved by calling + setVelocity(). Use the collidesWith() function to see if the canvas item + will collide on the \e next advance(1) and use collisions() to see what + collisions have occurred. + + Use QtCanvasSprite or your own subclass of QtCanvasSprite to create canvas + items which are animated, i.e. which change over time. + + The size of a canvas item is given by boundingRect(). Use + boundingRectAdvanced() to see what the size of the canvas item will be + \e after the next advance(1) call. + + The rtti() function is used for identifying subclasses of QtCanvasItem. + The canvas() function returns a pointer to the canvas which contains the + canvas item. + + QtCanvasItem provides the show() and isVisible() functions like those in + QWidget. + + QtCanvasItem also provides the setEnabled(), setActive() and + setSelected() functions; these functions set the relevant boolean and + cause a repaint but the boolean values they set are not used in + QtCanvasItem itself. You can make use of these booleans in your subclasses. + + By default, canvas items have no velocity, no size, and are not in + motion. The subclasses provided in Qt do not change these defaults + except where noted. + +*/ + +/* + \enum QtCanvasItem::RttiValues + + This enum is used to name the different types of canvas item. + + \value Rtti_Item Canvas item abstract base class + \value Rtti_Ellipse + \value Rtti_Line + \value Rtti_Polygon + \value Rtti_PolygonalItem + \value Rtti_Rectangle + \value Rtti_Spline + \value Rtti_Sprite + \value Rtti_Text + +*/ + +/* + \fn void QtCanvasItem::update() + + Call this function to repaint the canvas's changed chunks. +*/ + +/* + Constructs a QtCanvasItem on canvas \a canvas. + + \sa setCanvas() +*/ +QtCanvasItem::QtCanvasItem(QtCanvas* canvas) : + cnv(canvas), + myx(0), myy(0), myz(0) +{ + ani = 0; + vis = 0; + val = 0; + sel = 0; + ena = 0; + act = 0; + + ext = 0; + if (cnv) cnv->addItem(this); +} + +/* + Destroys the QtCanvasItem and removes it from its canvas. +*/ +QtCanvasItem::~QtCanvasItem() +{ + if (cnv) { + cnv->removeItem(this); + cnv->removeAnimation(this); + } + delete ext; +} + +QtCanvasItemExtra& QtCanvasItem::extra() +{ + if (!ext) + ext = new QtCanvasItemExtra; + return *ext; +} + +/* + \fn double QtCanvasItem::x() const + + Returns the horizontal position of the canvas item. Note that + subclasses often have an origin other than the top-left corner. +*/ + +/* + \fn double QtCanvasItem::y() const + + Returns the vertical position of the canvas item. Note that + subclasses often have an origin other than the top-left corner. +*/ + +/* + \fn double QtCanvasItem::z() const + + Returns the z index of the canvas item, which is used for visual + order: higher-z items obscure (are in front of) lower-z items. +*/ + +/* + \fn void QtCanvasItem::setX(double x) + + Moves the canvas item so that its x-position is \a x. + + \sa x(), move() +*/ + +/* + \fn void QtCanvasItem::setY(double y) + + Moves the canvas item so that its y-position is \a y. + + \sa y(), move() +*/ + +/* + \fn void QtCanvasItem::setZ(double z) + + Sets the z index of the canvas item to \a z. Higher-z items + obscure (are in front of) lower-z items. + + \sa z(), move() +*/ + + +/* + Moves the canvas item relative to its current position by (\a dx, + \a dy). +*/ +void QtCanvasItem::moveBy(double dx, double dy) +{ + if (dx || dy) { + removeFromChunks(); + myx += dx; + myy += dy; + addToChunks(); + } +} + + +/* + Moves the canvas item to the absolute position (\a x, \a y). +*/ +void QtCanvasItem::move(double x, double y) +{ + moveBy(x-myx, y-myy); +} + + +/* + Returns true if the canvas item is in motion; otherwise returns + false. + + \sa setVelocity(), setAnimated() +*/ +bool QtCanvasItem::animated() const +{ + return (bool)ani; +} + +/* + Sets the canvas item to be in motion if \a y is true, or not if \a + y is false. The speed and direction of the motion is set with + setVelocity(), or with setXVelocity() and setYVelocity(). + + \sa advance(), QtCanvas::advance() +*/ +void QtCanvasItem::setAnimated(bool y) +{ + if (y != (bool)ani) { + ani = (uint)y; + if (y) { + cnv->addAnimation(this); + } else { + cnv->removeAnimation(this); + } + } +} + +/* + \fn void QtCanvasItem::setXVelocity(double vx) + + Sets the horizontal component of the canvas item's velocity to \a vx. + + \sa setYVelocity() setVelocity() +*/ + +/* + \fn void QtCanvasItem::setYVelocity(double vy) + + Sets the vertical component of the canvas item's velocity to \a vy. + + \sa setXVelocity() setVelocity() +*/ + +/* + Sets the canvas item to be in motion, moving by \a vx and \a vy + pixels in the horizontal and vertical directions respectively. + + \sa advance() setXVelocity() setYVelocity() +*/ +void QtCanvasItem::setVelocity(double vx, double vy) +{ + if (ext || vx!= 0.0 || vy!= 0.0) { + if (!ani) + setAnimated(true); + extra().vx = vx; + extra().vy = vy; + } +} + +/* + Returns the horizontal velocity component of the canvas item. +*/ +double QtCanvasItem::xVelocity() const +{ + return ext ? ext->vx : 0; +} + +/* + Returns the vertical velocity component of the canvas item. +*/ +double QtCanvasItem::yVelocity() const +{ + return ext ? ext->vy : 0; +} + +/* + The default implementation moves the canvas item, if it is + animated(), by the preset velocity if \a phase is 1, and does + nothing if \a phase is 0. + + Note that if you reimplement this function, the reimplementation + must not change the canvas in any way, for example it must not add + or remove items. + + \sa QtCanvas::advance() setVelocity() +*/ +void QtCanvasItem::advance(int phase) +{ + if (ext && phase == 1) + moveBy(ext->vx, ext->vy); +} + +/* + \fn void QtCanvasItem::draw(QPainter& painter) + + This abstract virtual function draws the canvas item using \a painter. +*/ + +/* + Sets the QtCanvas upon which the canvas item is to be drawn to \a c. + + \sa canvas() +*/ +void QtCanvasItem::setCanvas(QtCanvas* c) +{ + bool v = isVisible(); + setVisible(false); + if (cnv) { + if (ext) + cnv->removeAnimation(this); + cnv->removeItem(this); + } + cnv = c; + if (cnv) { + cnv->addItem(this); + if (ext) + cnv->addAnimation(this); + } + setVisible(v); +} + +/* + \fn QtCanvas* QtCanvasItem::canvas() const + + Returns the canvas containing the canvas item. +*/ + +/* Shorthand for setVisible(true). */ +void QtCanvasItem::show() +{ + setVisible(true); +} + +/* Shorthand for setVisible(false). */ +void QtCanvasItem::hide() +{ + setVisible(false); +} + +/* + Makes the canvas item visible if \a yes is true, or invisible if + \a yes is false. The change takes effect when QtCanvas::update() is + next called. +*/ +void QtCanvasItem::setVisible(bool yes) +{ + if ((bool)vis!= yes) { + if (yes) { + vis = (uint)yes; + addToChunks(); + } else { + removeFromChunks(); + vis = (uint)yes; + } + } +} +/* + \obsolete + \fn bool QtCanvasItem::visible() const + Use isVisible() instead. +*/ + +/* + \fn bool QtCanvasItem::isVisible() const + + Returns true if the canvas item is visible; otherwise returns + false. + + Note that in this context true does \e not mean that the canvas + item is currently in a view, merely that if a view is showing the + area where the canvas item is positioned, and the item is not + obscured by items with higher z values, and the view is not + obscured by overlaying windows, it would be visible. + + \sa setVisible(), z() +*/ + +/* + \obsolete + \fn bool QtCanvasItem::selected() const + Use isSelected() instead. +*/ + +/* + \fn bool QtCanvasItem::isSelected() const + + Returns true if the canvas item is selected; otherwise returns false. +*/ + +/* + Sets the selected flag of the item to \a yes. If this changes the + item's selected state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setSelected() + function is supplied because many applications need it, but it is + up to you how you use the isSelected() value. +*/ +void QtCanvasItem::setSelected(bool yes) +{ + if ((bool)sel!= yes) { + sel = (uint)yes; + changeChunks(); + } +} + +/* + \obsolete + \fn bool QtCanvasItem::enabled() const + Use isEnabled() instead. +*/ + +/* + \fn bool QtCanvasItem::isEnabled() const + + Returns true if the QtCanvasItem is enabled; otherwise returns false. +*/ + +/* + Sets the enabled flag of the item to \a yes. If this changes the + item's enabled state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setEnabled() + function is supplied because many applications need it, but it is + up to you how you use the isEnabled() value. +*/ +void QtCanvasItem::setEnabled(bool yes) +{ + if (ena!= (uint)yes) { + ena = (uint)yes; + changeChunks(); + } +} + +/* + \obsolete + \fn bool QtCanvasItem::active() const + Use isActive() instead. +*/ + +/* + \fn bool QtCanvasItem::isActive() const + + Returns true if the QtCanvasItem is active; otherwise returns false. +*/ + +/* + Sets the active flag of the item to \a yes. If this changes the + item's active state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setActive() function + is supplied because many applications need it, but it is up to you + how you use the isActive() value. +*/ +void QtCanvasItem::setActive(bool yes) +{ + if (act!= (uint)yes) { + act = (uint)yes; + changeChunks(); + } +} + +bool qt_testCollision(const QtCanvasSprite* s1, const QtCanvasSprite* s2) +{ + const QImage* s2image = s2->imageAdvanced()->collision_mask; + QRect s2area = s2->boundingRectAdvanced(); + + QRect cyourarea(s2area.x(), s2area.y(), + s2area.width(), s2area.height()); + + QImage* s1image = s1->imageAdvanced()->collision_mask; + + QRect s1area = s1->boundingRectAdvanced(); + + QRect ourarea = s1area.intersected(cyourarea); + + if (ourarea.isEmpty()) + return false; + + int x2 = ourarea.x()-cyourarea.x(); + int y2 = ourarea.y()-cyourarea.y(); + int x1 = ourarea.x()-s1area.x(); + int y1 = ourarea.y()-s1area.y(); + int w = ourarea.width(); + int h = ourarea.height(); + + if (!s2image) { + if (!s1image) + return w>0 && h>0; + // swap everything around + int t; + t = x1; x1 = x2; x2 = t; + t = y1; x1 = y2; y2 = t; + s2image = s1image; + s1image = 0; + } + + // s2image != 0 + + // A non-linear search may be more efficient. + // Perhaps spiralling out from the center, or a simpler + // vertical expansion from the centreline. + + // We assume that sprite masks don't have + // different bit orders. + // + // Q_ASSERT(s1image->bitOrder() == s2image->bitOrder()); + + if (s1image) { + if (s1image->format() == QImage::Format_MonoLSB) { + for (int j = 0; j < h; j++) { + uchar* ml = s1image->scanLine(y1+j); + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)) + && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7))) + { + return true; + } + } + } + } else { + for (int j = 0; j < h; j++) { + uchar* ml = s1image->scanLine(y1+j); + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))) + && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7)))) + { + return true; + } + } + } + } + } else { + if (s2image->format() == QImage::Format_MonoLSB) { + for (int j = 0; j < h; j++) { + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))) + { + return true; + } + } + } + } else { + for (int j = 0; j< h; j++) { + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))) + { + return true; + } + } + } + } + } + + return false; +} + +static bool collision_double_dispatch(const QtCanvasSprite* s1, + const QtCanvasPolygonalItem* p1, + const QtCanvasRectangle* r1, + const QtCanvasEllipse* e1, + const QtCanvasText* t1, + const QtCanvasSprite* s2, + const QtCanvasPolygonalItem* p2, + const QtCanvasRectangle* r2, + const QtCanvasEllipse* e2, + const QtCanvasText* t2) +{ + const QtCanvasItem* i1 = s1 ? + (const QtCanvasItem*)s1 : p1 ? + (const QtCanvasItem*)p1 : r1 ? + (const QtCanvasItem*)r1 : e1 ? + (const QtCanvasItem*)e1 : (const QtCanvasItem*)t1; + const QtCanvasItem* i2 = s2 ? + (const QtCanvasItem*)s2 : p2 ? + (const QtCanvasItem*)p2 : r2 ? + (const QtCanvasItem*)r2 : e2 ? + (const QtCanvasItem*)e2 : (const QtCanvasItem*)t2; + + if (s1 && s2) { + // a + return qt_testCollision(s1, s2); + } else if ((r1 || t1 || s1) && (r2 || t2 || s2)) { + // b + QRect rc1 = i1->boundingRectAdvanced(); + QRect rc2 = i2->boundingRectAdvanced(); + return rc1.intersects(rc2); + } else if (e1 && e2 + && e1->angleLength()>= 360*16 && e2->angleLength()>= 360*16 + && e1->width() == e1->height() + && e2->width() == e2->height()) { + // c + double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity()); + double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity()); + double rd = (e1->width()+e2->width())/2; + return xd*xd+yd*yd <= rd*rd; + } else if (p1 && (p2 || s2 || t2)) { + // d + QPolygon pa1 = p1->areaPointsAdvanced(); + QPolygon pa2 = p2 ? p2->areaPointsAdvanced() + : QPolygon(i2->boundingRectAdvanced()); + bool col = !(QRegion(pa1) & QRegion(pa2, Qt::WindingFill)).isEmpty(); + + return col; + } else { + return collision_double_dispatch(s2, p2, r2, e2, t2, + s1, p1, r1, e1, t1); + } +} + +/* + \fn bool QtCanvasItem::collidesWith(const QtCanvasItem* other) const + + Returns true if the canvas item will collide with the \a other + item \e after they have moved by their current velocities; + otherwise returns false. + + \sa collisions() +*/ + + +/* + \class QtCanvasSprite qtcanvas.h + \brief The QtCanvasSprite class provides an animated canvas item on a QtCanvas. + + A canvas sprite is an object which can contain any number of images + (referred to as frames), only one of which is current, i.e. + displayed, at any one time. The images can be passed in the + constructor or set or changed later with setSequence(). If you + subclass QtCanvasSprite you can change the frame that is displayed + periodically, e.g. whenever QtCanvasItem::advance(1) is called to + create the effect of animation. + + The current frame can be set with setFrame() or with move(). The + number of frames available is given by frameCount(). The bounding + rectangle of the current frame is returned by boundingRect(). + + The current frame's image can be retrieved with image(); use + imageAdvanced() to retrieve the image for the frame that will be + shown after advance(1) is called. Use the image() overload passing + it an integer index to retrieve a particular image from the list of + frames. + + Use width() and height() to retrieve the dimensions of the current + frame. + + Use leftEdge() and rightEdge() to retrieve the current frame's + left-hand and right-hand x-coordinates respectively. Use + bottomEdge() and topEdge() to retrieve the current frame's bottom + and top y-coordinates respectively. These functions have an overload + which will accept an integer frame number to retrieve the + coordinates of a particular frame. + + QtCanvasSprite draws very quickly, at the expense of memory. + + The current frame's image can be drawn on a painter with draw(). + + Like any other canvas item, canvas sprites can be moved with + move() which sets the x and y coordinates and the frame number, as + well as with QtCanvasItem::move() and QtCanvasItem::moveBy(), or by + setting coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() + and QtCanvasItem::setZ(). + +*/ + + +/* + \reimp +*/ +bool QtCanvasSprite::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(this, 0, 0, 0, 0); +} + +/* + Returns true if the canvas item collides with any of the given + items; otherwise returns false. The parameters, \a s, \a p, \a r, + \a e and \a t, are all the same object, this is just a type + resolution trick. +*/ +bool QtCanvasSprite::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, this, 0, 0, 0, 0); +} + +/* + \reimp +*/ +bool QtCanvasPolygonalItem::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, this, 0, 0, 0); +} + +bool QtCanvasPolygonalItem::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, 0, 0, 0); +} + +/* + \reimp +*/ +bool QtCanvasRectangle::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, this, this, 0, 0); +} + +bool QtCanvasRectangle::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, this, 0, 0); +} + + +/* + \reimp +*/ +bool QtCanvasEllipse::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0,this, 0, this, 0); +} + +bool QtCanvasEllipse::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, 0, this, 0); +} + +/* + \reimp +*/ +bool QtCanvasText::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, 0, 0, 0, this); +} + +bool QtCanvasText::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, 0, 0, 0, this); +} + +/* + Returns the list of canvas items that this canvas item has + collided with. + + A collision is generally defined as occurring when the pixels of + one item draw on the pixels of another item, but not all + subclasses are so precise. Also, since pixel-wise collision + detection can be slow, this function works in either exact or + inexact mode, according to the \a exact parameter. + + If \a exact is true, the canvas items returned have been + accurately tested for collision with the canvas item. + + If \a exact is false, the canvas items returned are \e near the + canvas item. You can test the canvas items returned using + collidesWith() if any are interesting collision candidates. By + using this approach, you can ignore some canvas items for which + collisions are not relevant. + + The returned list is a list of QtCanvasItems, but often you will + need to cast the items to their subclass types. The safe way to do + this is to use rtti() before casting. This provides some of the + functionality of the standard C++ dynamic cast operation even on + compilers where dynamic casts are not available. + + Note that a canvas item may be `on' a canvas, e.g. it was created + with the canvas as parameter, even though its coordinates place it + beyond the edge of the canvas's area. Collision detection only + works for canvas items which are wholly or partly within the + canvas's area. + + Note that if items have a velocity (see \l setVelocity()), then + collision testing is done based on where the item \e will be when + it moves, not its current location. For example, a "ball" item + doesn't need to actually embed into a "wall" item before a + collision is detected. For items without velocity, plain + intersection is used. +*/ +QtCanvasItemList QtCanvasItem::collisions(bool exact) const +{ + return canvas()->collisions(chunks(), this, exact); +} + +/* + Returns a list of canvas items that collide with the point \a p. + The list is ordered by z coordinates, from highest z coordinate + (front-most item) to lowest z coordinate (rear-most item). +*/ +QtCanvasItemList QtCanvas::collisions(const QPoint& p) const +{ + return collisions(QRect(p, QSize(1, 1))); +} + +/* + \overload + + Returns a list of items which collide with the rectangle \a r. The + list is ordered by z coordinates, from highest z coordinate + (front-most item) to lowest z coordinate (rear-most item). +*/ +QtCanvasItemList QtCanvas::collisions(const QRect& r) const +{ + QtCanvasRectangle i(r, (QtCanvas*)this); + i.setPen(NoPen); + i.show(); // doesn't actually show, since we destroy it + QtCanvasItemList l = i.collisions(true); + qSort(l.begin(), l.end(), QtCanvasItemLess()); + return l; +} + +/* + \overload + + Returns a list of canvas items which intersect with the chunks + listed in \a chunklist, excluding \a item. If \a exact is true, + only those which actually \link QtCanvasItem::collidesWith() + collide with\endlink \a item are returned; otherwise canvas items + are included just for being in the chunks. + + This is a utility function mainly used to implement the simpler + QtCanvasItem::collisions() function. +*/ +QtCanvasItemList QtCanvas::collisions(const QPolygon& chunklist, + const QtCanvasItem* item, bool exact) const +{ + QSet seen; + QtCanvasItemList result; + for (int i = 0; i <(int)chunklist.count(); i++) { + int x = chunklist[i].x(); + int y = chunklist[i].y(); + if (validChunk(x, y)) { + const QtCanvasItemList &l = chunk(x, y).list(); + for (int i = 0; i < l.size(); ++i) { + QtCanvasItem *g = l.at(i); + if (g != item) { + if (!seen.contains(g)) { + seen.insert(g); + if (!exact || item->collidesWith(g)) + result.append(g); + } + } + } + } + } + return result; +} + +/* + \internal + Adds the item to all the chunks it covers. +*/ +void QtCanvasItem::addToChunks() +{ + if (isVisible() && canvas()) { + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->addItemToChunk(this, pa[i].x(), pa[i].y()); + val = (uint)true; + } +} + +/* + \internal + Removes the item from all the chunks it covers. +*/ +void QtCanvasItem::removeFromChunks() +{ + if (isVisible() && canvas()) { + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->removeItemFromChunk(this, pa[i].x(), pa[i].y()); + } +} + +/* + \internal + Sets all the chunks covered by the item to be refreshed with QtCanvas::update() + is next called. +*/ +void QtCanvasItem::changeChunks() +{ + if (isVisible() && canvas()) { + if (!val) + addToChunks(); + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->setChangedChunk(pa[i].x(), pa[i].y()); + } +} + +/* + \fn QRect QtCanvasItem::boundingRect() const + + Returns the bounding rectangle in pixels that the canvas item covers. + + \sa boundingRectAdvanced() +*/ + +/* + Returns the bounding rectangle of pixels that the canvas item \e + will cover after advance(1) is called. + + \sa boundingRect() +*/ +QRect QtCanvasItem::boundingRectAdvanced() const +{ + int dx = int(x()+xVelocity())-int(x()); + int dy = int(y()+yVelocity())-int(y()); + QRect r = boundingRect(); + r.translate(dx, dy); + return r; +} + +/* + \class QtCanvasPixmap qtcanvas.h + \brief The QtCanvasPixmap class provides pixmaps for QtCanvasSprites. + + If you want to show a single pixmap on a QtCanvas use a + QtCanvasSprite with just one pixmap. + + When pixmaps are inserted into a QtCanvasPixmapArray they are held + as QtCanvasPixmaps. \l{QtCanvasSprite}s are used to show pixmaps on + \l{QtCanvas}es and hold their pixmaps in a QtCanvasPixmapArray. If + you retrieve a frame (pixmap) from a QtCanvasSprite it will be + returned as a QtCanvasPixmap. + + The pixmap is a QPixmap and can only be set in the constructor. + There are three different constructors, one taking a QPixmap, one + a QImage and one a file name that refers to a file in any + supported file format (see QImageReader). + + QtCanvasPixmap can have a hotspot which is defined in terms of an (x, + y) offset. When you create a QtCanvasPixmap from a PNG file or from + a QImage that has a QImage::offset(), the offset() is initialized + appropriately, otherwise the constructor leaves it at (0, 0). You + can set it later using setOffset(). When the QtCanvasPixmap is used + in a QtCanvasSprite, the offset position is the point at + QtCanvasItem::x() and QtCanvasItem::y(), not the top-left corner of + the pixmap. + + Note that for QtCanvasPixmap objects created by a QtCanvasSprite, the + position of each QtCanvasPixmap object is set so that the hotspot + stays in the same position. + + \sa QtCanvasPixmapArray QtCanvasItem QtCanvasSprite +*/ + + +/* + Constructs a QtCanvasPixmap that uses the image stored in \a + datafilename. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QString& datafilename) +{ + QImage image(datafilename); + init(image); +} + + +/* + Constructs a QtCanvasPixmap from the image \a image. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QImage& image) +{ + init(image); +} +/* + Constructs a QtCanvasPixmap from the pixmap \a pm using the offset + \a offset. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QPixmap& pm, const QPoint& offset) +{ + init(pm, offset.x(), offset.y()); +} + +void QtCanvasPixmap::init(const QImage& image) +{ + this->QPixmap::operator = (QPixmap::fromImage(image)); + hotx = image.offset().x(); + hoty = image.offset().y(); +#ifndef QT_NO_IMAGE_DITHER_TO_1 + if(image.hasAlphaChannel()) { + QImage i = image.createAlphaMask(); + collision_mask = new QImage(i); + } else +#endif + collision_mask = 0; +} + +void QtCanvasPixmap::init(const QPixmap& pixmap, int hx, int hy) +{ + (QPixmap&)*this = pixmap; + hotx = hx; + hoty = hy; + if(pixmap.hasAlphaChannel()) { + QImage i = mask().toImage(); + collision_mask = new QImage(i); + } else + collision_mask = 0; +} + +/* + Destroys the pixmap. +*/ +QtCanvasPixmap::~QtCanvasPixmap() +{ + delete collision_mask; +} + +/* + \fn int QtCanvasPixmap::offsetX() const + + Returns the x-offset of the pixmap's hotspot. + + \sa setOffset() +*/ + +/* + \fn int QtCanvasPixmap::offsetY() const + + Returns the y-offset of the pixmap's hotspot. + + \sa setOffset() +*/ + +/* + \fn void QtCanvasPixmap::setOffset(int x, int y) + + Sets the offset of the pixmap's hotspot to (\a x, \a y). + + \warning Do not call this function if any QtCanvasSprites are + currently showing this pixmap. +*/ + +/* + \class QtCanvasPixmapArray qtcanvas.h + \brief The QtCanvasPixmapArray class provides an array of QtCanvasPixmaps. + + This class is used by QtCanvasSprite to hold an array of pixmaps. + It is used to implement animated sprites, i.e. images that change + over time, with each pixmap in the array holding one frame. + + Depending on the constructor you use you can load multiple pixmaps + into the array either from a directory (specifying a wildcard + pattern for the files), or from a list of QPixmaps. You can also + read in a set of pixmaps after construction using readPixmaps(). + + Individual pixmaps can be set with setImage() and retrieved with + image(). The number of pixmaps in the array is returned by + count(). + + QtCanvasSprite uses an image's mask for collision detection. You + can change this by reading in a separate set of image masks using + readCollisionMasks(). + +*/ + +/* + Constructs an invalid array (i.e. isValid() will return false). + You must call readPixmaps() before being able to use this + QtCanvasPixmapArray. +*/ +QtCanvasPixmapArray::QtCanvasPixmapArray() +: framecount(0), img(0) +{ +} + +/* + Constructs a QtCanvasPixmapArray from files. + + The \a fc parameter sets the number of frames to be loaded for + this image. + + If \a fc is not 0, \a datafilenamepattern should contain "%1", + e.g. "foo%1.png". The actual filenames are formed by replacing the + %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png, + foo0001.png, foo0002.png, etc. + + If \a fc is 0, \a datafilenamepattern is asssumed to be a + filename, and the image contained in this file will be loaded as + the first (and only) frame. + + If \a datafilenamepattern does not exist, is not readable, isn't + an image, or some other error occurs, the array ends up empty and + isValid() returns false. +*/ + +QtCanvasPixmapArray::QtCanvasPixmapArray(const QString& datafilenamepattern, + int fc) +: framecount(0), img(0) +{ + readPixmaps(datafilenamepattern, fc); +} + +/* + \obsolete + Use QtCanvasPixmapArray::QtCanvasPixmapArray(QtValueList, QPolygon) + instead. + + Constructs a QtCanvasPixmapArray from the list of QPixmaps \a + list. The \a hotspots list has to be of the same size as \a list. +*/ +QtCanvasPixmapArray::QtCanvasPixmapArray(const QList &list, const QPolygon &hotspots) + : framecount(list.count()), + img(new QtCanvasPixmap*[list.count()]) +{ + if (list.count() != hotspots.count()) { + qWarning("QtCanvasPixmapArray: lists have different lengths"); + reset(); + img = 0; + } else { + for (int i = 0; i < framecount; i++) + img[i] = new QtCanvasPixmap(list.at(i), hotspots.at(i)); + } +} + + +/* + Destroys the pixmap array and all the pixmaps it contains. +*/ +QtCanvasPixmapArray::~QtCanvasPixmapArray() +{ + reset(); +} + +void QtCanvasPixmapArray::reset() +{ + for (int i = 0; i < framecount; i++) + delete img[i]; + delete [] img; + img = 0; + framecount = 0; +} + +/* + Reads one or more pixmaps into the pixmap array. + + If \a fc is not 0, \a filenamepattern should contain "%1", e.g. + "foo%1.png". The actual filenames are formed by replacing the %1 + with four-digit integers from 0 to (fc - 1), e.g. foo0000.png, + foo0001.png, foo0002.png, etc. + + If \a fc is 0, \a filenamepattern is asssumed to be a filename, + and the image contained in this file will be loaded as the first + (and only) frame. + + If \a filenamepattern does not exist, is not readable, isn't an + image, or some other error occurs, this function will return + false, and isValid() will return false; otherwise this function + will return true. + + \sa isValid() +*/ +bool QtCanvasPixmapArray::readPixmaps(const QString& filenamepattern, + int fc) +{ + return readPixmaps(filenamepattern, fc, false); +} + +/* + Reads new collision masks for the array. + + By default, QtCanvasSprite uses the image mask of a sprite to + detect collisions. Use this function to set your own collision + image masks. + + If count() is 1 \a filename must specify a real filename to read + the mask from. If count() is greater than 1, the \a filename must + contain a "%1" that will get replaced by the number of the mask to + be loaded, just like QtCanvasPixmapArray::readPixmaps(). + + All collision masks must be 1-bit images or this function call + will fail. + + If the file isn't readable, contains the wrong number of images, + or there is some other error, this function will return false, and + the array will be flagged as invalid; otherwise this function + returns true. + + \sa isValid() +*/ +bool QtCanvasPixmapArray::readCollisionMasks(const QString& filename) +{ + return readPixmaps(filename, framecount, true); +} + + +bool QtCanvasPixmapArray::readPixmaps(const QString& datafilenamepattern, + int fc, bool maskonly) +{ + if (!maskonly) { + reset(); + framecount = fc; + if (!framecount) + framecount = 1; + img = new QtCanvasPixmap*[framecount]; + } + if (!img) + return false; + + bool ok = true; + bool arg = fc > 1; + if (!arg) + framecount = 1; + for (int i = 0; i < framecount; i++) { + QString r; + r.sprintf("%04d", i); + if (maskonly) { + if (!img[i]->collision_mask) + img[i]->collision_mask = new QImage(); + img[i]->collision_mask->load( + arg ? datafilenamepattern.arg(r) : datafilenamepattern); + ok = ok + && !img[i]->collision_mask->isNull() + && img[i]->collision_mask->depth() == 1; + } else { + img[i] = new QtCanvasPixmap( + arg ? datafilenamepattern.arg(r) : datafilenamepattern); + ok = ok && !img[i]->isNull(); + } + } + if (!ok) { + reset(); + } + return ok; +} + +/* + \obsolete + + Use isValid() instead. + + This returns false if the array is valid, and true if it is not. +*/ +bool QtCanvasPixmapArray::operator!() +{ + return img == 0; +} + +/* + Returns true if the pixmap array is valid; otherwise returns + false. +*/ +bool QtCanvasPixmapArray::isValid() const +{ + return (img != 0); +} + +/* + \fn QtCanvasPixmap* QtCanvasPixmapArray::image(int i) const + + Returns pixmap \a i in the array, if \a i is non-negative and less + than than count(), and returns an unspecified value otherwise. +*/ + +// ### wouldn't it be better to put empty QtCanvasPixmaps in there instead of +// initializing the additional elements in the array to 0? Lars +/* + Replaces the pixmap at index \a i with pixmap \a p. + + The array takes ownership of \a p and will delete \a p when the + array itself is deleted. + + If \a i is beyond the end of the array the array is extended to at + least i+1 elements, with elements count() to i-1 being initialized + to 0. +*/ +void QtCanvasPixmapArray::setImage(int i, QtCanvasPixmap* p) +{ + if (i >= framecount) { + QtCanvasPixmap** newimg = new QtCanvasPixmap*[i+1]; + memcpy(newimg, img, sizeof(QtCanvasPixmap *)*framecount); + memset(newimg + framecount, 0, sizeof(QtCanvasPixmap *)*(i+1 - framecount)); + framecount = i+1; + delete [] img; + img = newimg; + } + delete img[i]; img[i] = p; +} + +/* + \fn uint QtCanvasPixmapArray::count() const + + Returns the number of pixmaps in the array. +*/ + +/* + Returns the x-coordinate of the current left edge of the sprite. + (This may change as the sprite animates since different frames may + have different left edges.) + + \sa rightEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::leftEdge() const +{ + return int(x()) - image()->hotx; +} + +/* + \overload + + Returns what the x-coordinate of the left edge of the sprite would + be if the sprite (actually its hotspot) were moved to x-position + \a nx. + + \sa rightEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::leftEdge(int nx) const +{ + return nx - image()->hotx; +} + +/* + Returns the y-coordinate of the top edge of the sprite. (This may + change as the sprite animates since different frames may have + different top edges.) + + \sa leftEdge() rightEdge() bottomEdge() +*/ +int QtCanvasSprite::topEdge() const +{ + return int(y()) - image()->hoty; +} + +/* + \overload + + Returns what the y-coordinate of the top edge of the sprite would + be if the sprite (actually its hotspot) were moved to y-position + \a ny. + + \sa leftEdge() rightEdge() bottomEdge() +*/ +int QtCanvasSprite::topEdge(int ny) const +{ + return ny - image()->hoty; +} + +/* + Returns the x-coordinate of the current right edge of the sprite. + (This may change as the sprite animates since different frames may + have different right edges.) + + \sa leftEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::rightEdge() const +{ + return leftEdge() + image()->width()-1; +} + +/* + \overload + + Returns what the x-coordinate of the right edge of the sprite + would be if the sprite (actually its hotspot) were moved to + x-position \a nx. + + \sa leftEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::rightEdge(int nx) const +{ + return leftEdge(nx) + image()->width()-1; +} + +/* + Returns the y-coordinate of the current bottom edge of the sprite. + (This may change as the sprite animates since different frames may + have different bottom edges.) + + \sa leftEdge() rightEdge() topEdge() +*/ +int QtCanvasSprite::bottomEdge() const +{ + return topEdge() + image()->height()-1; +} + +/* + \overload + + Returns what the y-coordinate of the top edge of the sprite would + be if the sprite (actually its hotspot) were moved to y-position + \a ny. + + \sa leftEdge() rightEdge() topEdge() +*/ +int QtCanvasSprite::bottomEdge(int ny) const +{ + return topEdge(ny) + image()->height()-1; +} + +/* + \fn QtCanvasPixmap* QtCanvasSprite::image() const + + Returns the current frame's image. + + \sa frame(), setFrame() +*/ + +/* + \fn QtCanvasPixmap* QtCanvasSprite::image(int f) const + \overload + + Returns the image for frame \a f. Does not do any bounds checking on \a f. +*/ + +/* + Returns the image the sprite \e will have after advance(1) is + called. By default this is the same as image(). +*/ +QtCanvasPixmap* QtCanvasSprite::imageAdvanced() const +{ + return image(); +} + +/* + Returns the bounding rectangle for the image in the sprite's + current frame. This assumes that the images are tightly cropped + (i.e. do not have transparent pixels all along a side). +*/ +QRect QtCanvasSprite::boundingRect() const +{ + return QRect(leftEdge(), topEdge(), width(), height()); +} + + +/* + \internal + Returns the chunks covered by the item. +*/ +QPolygon QtCanvasItem::chunks() const +{ + QPolygon r; + int n = 0; + QRect br = boundingRect(); + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + br &= QRect(0, 0, canvas()->width(), canvas()->height()); + if (br.isValid()) { + r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2)); + for (int j = br.top()/chunksize; j <= br.bottom()/chunksize; j++) { + for (int i = br.left()/chunksize; i <= br.right()/chunksize; i++) { + r[n++] = QPoint(i, j); + } + } + } + } + r.resize(n); + return r; +} + + +/* + \internal + Add the sprite to the chunks in its QtCanvas which it overlaps. +*/ +void QtCanvasSprite::addToChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->addItemToChunk(this, i, j); + } + } + } +} + +/* + \internal + Remove the sprite from the chunks in its QtCanvas which it overlaps. + + \sa addToChunks() +*/ +void QtCanvasSprite::removeFromChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->removeItemFromChunk(this, i, j); + } + } + } +} + +/* + The width of the sprite for the current frame's image. + + \sa frame() +*/ +//### mark: Why don't we have width(int) and height(int) to be +//consistent with leftEdge() and leftEdge(int)? +int QtCanvasSprite::width() const +{ + return image()->width(); +} + +/* + The height of the sprite for the current frame's image. + + \sa frame() +*/ +int QtCanvasSprite::height() const +{ + return image()->height(); +} + + +/* + Draws the current frame's image at the sprite's current position + on painter \a painter. +*/ +void QtCanvasSprite::draw(QPainter& painter) +{ + painter.drawPixmap(leftEdge(), topEdge(), *image()); +} + +/* + \class QtCanvasView qtcanvas.h + \brief The QtCanvasView class provides an on-screen view of a QtCanvas. + + A QtCanvasView is widget which provides a view of a QtCanvas. + + If you want users to be able to interact with a canvas view, + subclass QtCanvasView. You might then reimplement + QtScrollView::contentsMousePressEvent(). For example: + + \code + void MyCanvasView::contentsMousePressEvent(QMouseEvent* e) + { + QtCanvasItemList l = canvas()->collisions(e->pos()); + for (QtCanvasItemList::Iterator it = l.begin(); it!= l.end(); ++it) { + if ((*it)->rtti() == QtCanvasRectangle::RTTI) + qDebug("A QtCanvasRectangle lies somewhere at this point"); + } + } + \endcode + + The canvas view shows canvas canvas(); this can be changed using + setCanvas(). + + A transformation matrix can be used to transform the view of the + canvas in various ways, for example, zooming in or out or rotating. + For example: + + \code + QMatrix wm; + wm.scale(2, 2); // Zooms in by 2 times + wm.rotate(90); // Rotates 90 degrees counter clockwise + // around the origin. + wm.translate(0, -canvas->height()); + // moves the canvas down so what was visible + // before is still visible. + myCanvasView->setWorldMatrix(wm); + \endcode + + Use setWorldMatrix() to set the canvas view's world matrix: you must + ensure that the world matrix is invertible. The current world matrix + is retrievable with worldMatrix(), and its inversion is retrievable + with inverseWorldMatrix(). + + Example: + + The following code finds the part of the canvas that is visible in + this view, i.e. the bounding rectangle of the view in canvas coordinates. + + \code + QRect rc = QRect(myCanvasView->contentsX(), myCanvasView->contentsY(), + myCanvasView->visibleWidth(), myCanvasView->visibleHeight()); + QRect canvasRect = myCanvasView->inverseWorldMatrix().mapRect(rc); + \endcode + + \sa QMatrix QPainter::setWorldMatrix() + +*/ + +class QtCanvasWidget : public QWidget +{ +public: + QtCanvasWidget(QtCanvasView *view) : QWidget(view) { m_view = view; } +protected: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e) { + m_view->contentsMousePressEvent(e); + } + void mouseMoveEvent(QMouseEvent *e) { + m_view->contentsMouseMoveEvent(e); + } + void mouseReleaseEvent(QMouseEvent *e) { + m_view->contentsMouseReleaseEvent(e); + } + void mouseDoubleClickEvent(QMouseEvent *e) { + m_view->contentsMouseDoubleClickEvent(e); + } + void dragEnterEvent(QDragEnterEvent *e) { + m_view->contentsDragEnterEvent(e); + } + void dragMoveEvent(QDragMoveEvent *e) { + m_view->contentsDragMoveEvent(e); + } + void dragLeaveEvent(QDragLeaveEvent *e) { + m_view->contentsDragLeaveEvent(e); + } + void dropEvent(QDropEvent *e) { + m_view->contentsDropEvent(e); + } + void wheelEvent(QWheelEvent *e) { + m_view->contentsWheelEvent(e); + } + void contextMenuEvent(QContextMenuEvent *e) { + m_view->contentsContextMenuEvent(e); + } + + QtCanvasView *m_view; +}; + +void QtCanvasWidget::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + if (m_view->d->highQuality) { + p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::SmoothPixmapTransform); + } + m_view->drawContents(&p, e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); +} + +/* + Constructs a QtCanvasView with parent \a parent. The canvas view + is not associated with a canvas, so you must to call setCanvas() + to view a canvas. +*/ +QtCanvasView::QtCanvasView(QWidget* parent) + : QScrollArea(parent) +{ + d = new QtCanvasViewData; + setWidget(new QtCanvasWidget(this)); + d->highQuality = false; + viewing = 0; + setCanvas(0); +} + +/* + \overload + + Constructs a QtCanvasView which views canvas \a canvas, with parent + \a parent. +*/ +QtCanvasView::QtCanvasView(QtCanvas* canvas, QWidget* parent) + : QScrollArea(parent) +{ + d = new QtCanvasViewData; + d->highQuality = false; + setWidget(new QtCanvasWidget(this)); + viewing = 0; + setCanvas(canvas); +} + +/* + Destroys the canvas view. The associated canvas is \e not deleted. +*/ +QtCanvasView::~QtCanvasView() +{ + delete d; + d = 0; + setCanvas(0); +} + +/* + \property QtCanvasView::highQualityRendering + \brief whether high quality rendering is turned on + + If high quality rendering is turned on, the canvas view will paint itself + using the QPainter::Antialiasing and QPainter::SmoothPixmapTransform + rendering flags. + + Enabling these flag will usually improve the visual appearance on the screen + at the cost of rendering speed. +*/ +bool QtCanvasView::highQualityRendering() const +{ + return d->highQuality; +} + +void QtCanvasView::setHighQualityRendering(bool enable) +{ + d->highQuality = enable; + widget()->update(); +} + + +void QtCanvasView::contentsMousePressEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseReleaseEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseDoubleClickEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsDragEnterEvent(QDragEnterEvent *) +{ +} + +void QtCanvasView::contentsDragMoveEvent(QDragMoveEvent *) +{ +} + +void QtCanvasView::contentsDragLeaveEvent(QDragLeaveEvent *) +{ +} + +void QtCanvasView::contentsDropEvent(QDropEvent *) +{ +} + +void QtCanvasView::contentsWheelEvent(QWheelEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + e->ignore(); +} + +/* + \fn QtCanvas* QtCanvasView::canvas() const + + Returns a pointer to the canvas which the QtCanvasView is currently + showing. +*/ + + +/* + Sets the canvas that the QtCanvasView is showing to the canvas \a + canvas. +*/ +void QtCanvasView::setCanvas(QtCanvas* canvas) +{ + if (viewing == canvas) + return; + + if (viewing) { + disconnect(viewing); + viewing->removeView(this); + } + viewing = canvas; + if (viewing) { + connect(viewing, SIGNAL(resized()), this, SLOT(updateContentsSize())); + viewing->addView(this); + } + if (d) // called by d'tor + updateContentsSize(); + update(); +} + +/* + Returns a reference to the canvas view's current transformation matrix. + + \sa setWorldMatrix() inverseWorldMatrix() +*/ +const QMatrix &QtCanvasView::worldMatrix() const +{ + return d->xform; +} + +/* + Returns a reference to the inverse of the canvas view's current + transformation matrix. + + \sa setWorldMatrix() worldMatrix() +*/ +const QMatrix &QtCanvasView::inverseWorldMatrix() const +{ + return d->ixform; +} + +/* + Sets the transformation matrix of the QtCanvasView to \a wm. The + matrix must be invertible (i.e. if you create a world matrix that + zooms out by 2 times, then the inverse of this matrix is one that + will zoom in by 2 times). + + When you use this, you should note that the performance of the + QtCanvasView will decrease considerably. + + Returns false if \a wm is not invertable; otherwise returns true. + + \sa worldMatrix() inverseWorldMatrix() QMatrix::isInvertible() +*/ +bool QtCanvasView::setWorldMatrix(const QMatrix & wm) +{ + bool ok = wm.isInvertible(); + if (ok) { + d->xform = wm; + d->ixform = wm.inverted(); + updateContentsSize(); + widget()->update(); + } + return ok; +} + +void QtCanvasView::updateContentsSize() +{ + if (viewing) { + QRect br; + br = d->xform.mapRect(QRect(0, 0, viewing->width(), viewing->height())); + + widget()->resize(br.size()); + } else { + widget()->resize(size()); + } +} + +/* + Repaints part of the QtCanvas that the canvas view is showing + starting at \a cx by \a cy, with a width of \a cw and a height of \a + ch using the painter \a p. +*/ +void QtCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + if (!viewing) + return; + QPainterPath clipPath; + clipPath.addRect(viewing->rect()); + p->setClipPath(d->xform.map(clipPath), Qt::IntersectClip); + viewing->drawViewArea(this, p, QRect(cx, cy, cw, ch), false); +} + +/* + Suggests a size sufficient to view the entire canvas. +*/ +QSize QtCanvasView::sizeHint() const +{ + if (!canvas()) + return QScrollArea::sizeHint(); + // should maybe take transformations into account + return (canvas()->size() + 2 * QSize(frameWidth(), frameWidth())) + .boundedTo(3 * QApplication::desktop()->size() / 4); +} + +/* + \class QtCanvasPolygonalItem qtcanvas.h + \brief The QtCanvasPolygonalItem class provides a polygonal canvas item + on a QtCanvas. + + The mostly rectangular classes, such as QtCanvasSprite and + QtCanvasText, use the object's bounding rectangle for movement, + repainting and collision calculations. For most other items, the + bounding rectangle can be far too large -- a diagonal line being + the worst case, and there are many other cases which are also bad. + QtCanvasPolygonalItem provides polygon-based bounding rectangle + handling, etc., which is much faster for non-rectangular items. + + Derived classes should try to define as small an area as possible + to maximize efficiency, but the polygon must \e definitely be + contained completely within the polygonal area. Calculating the + exact requirements is usually difficult, but if you allow a small + overestimate it can be easy and quick, while still getting almost + all of QtCanvasPolygonalItem's speed. + + Note that all subclasses \e must call hide() in their destructor + since hide() needs to be able to access areaPoints(). + + Normally, QtCanvasPolygonalItem uses the odd-even algorithm for + determining whether an object intersects this object. You can + change this to the winding algorithm using setWinding(). + + The bounding rectangle is available using boundingRect(). The + points bounding the polygonal item are retrieved with + areaPoints(). Use areaPointsAdvanced() to retrieve the bounding + points the polygonal item \e will have after + QtCanvasItem::advance(1) has been called. + + If the shape of the polygonal item is about to change while the + item is visible, call invalidate() before updating with a + different result from \l areaPoints(). + + By default, QtCanvasPolygonalItem objects have a black pen and no + brush (the default QPen and QBrush constructors). You can change + this with setPen() and setBrush(), but note that some + QtCanvasPolygonalItem subclasses only use the brush, ignoring the + pen setting. + + The polygonal item can be drawn on a painter with draw(). + Subclasses must reimplement drawShape() to draw themselves. + + Like any other canvas item polygonal items can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting coordinates + with QtCanvasItem::setX(), QtCanvasItem::setY() and QtCanvasItem::setZ(). + +*/ + + +/* + Since most polygonal items don't have a pen, the default is + NoPen and a black brush. +*/ +static const QPen& defaultPolygonPen() +{ + static QPen* dp = 0; + if (!dp) + dp = new QPen; + return *dp; +} + +static const QBrush& defaultPolygonBrush() +{ + static QBrush* db = 0; + if (!db) + db = new QBrush; + return *db; +} + +/* + Constructs a QtCanvasPolygonalItem on the canvas \a canvas. +*/ +QtCanvasPolygonalItem::QtCanvasPolygonalItem(QtCanvas* canvas) : + QtCanvasItem(canvas), + br(defaultPolygonBrush()), + pn(defaultPolygonPen()) +{ + wind = 0; +} + +/* + Note that all subclasses \e must call hide() in their destructor + since hide() needs to be able to access areaPoints(). +*/ +QtCanvasPolygonalItem::~QtCanvasPolygonalItem() +{ +} + +/* + Returns true if the polygonal item uses the winding algorithm to + determine the "inside" of the polygon. Returns false if it uses + the odd-even algorithm. + + The default is to use the odd-even algorithm. + + \sa setWinding() +*/ +bool QtCanvasPolygonalItem::winding() const +{ + return wind; +} + +/* + If \a enable is true, the polygonal item will use the winding + algorithm to determine the "inside" of the polygon; otherwise the + odd-even algorithm will be used. + + The default is to use the odd-even algorithm. + + \sa winding() +*/ +void QtCanvasPolygonalItem::setWinding(bool enable) +{ + wind = enable; +} + +/* + Invalidates all information about the area covered by the canvas + item. The item will be updated automatically on the next call that + changes the item's status, for example, move() or update(). Call + this function if you are going to change the shape of the item (as + returned by areaPoints()) while the item is visible. +*/ +void QtCanvasPolygonalItem::invalidate() +{ + val = (uint)false; + removeFromChunks(); +} + +/* + \fn QtCanvasPolygonalItem::isValid() const + + Returns true if the polygonal item's area information has not been + invalidated; otherwise returns false. + + \sa invalidate() +*/ + +/* + Returns the points the polygonal item \e will have after + QtCanvasItem::advance(1) is called, i.e. what the points are when + advanced by the current xVelocity() and yVelocity(). +*/ +QPolygon QtCanvasPolygonalItem::areaPointsAdvanced() const +{ + int dx = int(x()+xVelocity())-int(x()); + int dy = int(y()+yVelocity())-int(y()); + QPolygon r = areaPoints(); + r.detach(); // Explicit sharing is stupid. + if (dx || dy) + r.translate(dx, dy); + return r; +} + +//#define QCANVAS_POLYGONS_DEBUG +#ifdef QCANVAS_POLYGONS_DEBUG +static QWidget* dbg_wid = 0; +static QPainter* dbg_ptr = 0; +#endif + +class QPolygonalProcessor { +public: + QPolygonalProcessor(QtCanvas* c, const QPolygon& pa) : + canvas(c) + { + QRect pixelbounds = pa.boundingRect(); + int cs = canvas->chunkSize(); + QRect canvasbounds = pixelbounds.intersected(canvas->rect()); + bounds.setLeft(canvasbounds.left()/cs); + bounds.setRight(canvasbounds.right()/cs); + bounds.setTop(canvasbounds.top()/cs); + bounds.setBottom(canvasbounds.bottom()/cs); + bitmap = QImage(bounds.width(), bounds.height(), QImage::Format_MonoLSB); + pnt = 0; + bitmap.fill(0); +#ifdef QCANVAS_POLYGONS_DEBUG + dbg_start(); +#endif + } + + inline void add(int x, int y) + { + if (pnt >= (int)result.size()) { + result.resize(pnt*2+10); + } + result[pnt++] = QPoint(x+bounds.x(), y+bounds.y()); +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) { + int cs = canvas->chunkSize(); + QRect r(x*cs+bounds.x()*cs, y*cs+bounds.y()*cs, cs-1, cs-1); + dbg_ptr->setPen(Qt::blue); + dbg_ptr->drawRect(r); + } +#endif + } + + inline void addBits(int x1, int x2, uchar newbits, int xo, int yo) + { + for (int i = x1; i <= x2; i++) + if (newbits & (1 <resize(800, 600); + dbg_wid->show(); + dbg_ptr = new QPainter(dbg_wid); + dbg_ptr->setBrush(Qt::NoBrush); + } + dbg_ptr->fillRect(dbg_wid->rect(), Qt::white); + } +#endif + + void doSpans(int n, QPoint* pt, int* w) + { + int cs = canvas->chunkSize(); + for (int j = 0; j < n; j++) { + int y = pt[j].y()/cs-bounds.y(); + if (y >= bitmap.height() || y < 0) continue; + uchar* l = bitmap.scanLine(y); + int x = pt[j].x(); + int x1 = x/cs-bounds.x(); + if (x1 > bounds.width()) continue; + x1 = qMax(0,x1); + int x2 = (x+w[j])/cs-bounds.x(); + if (x2 < 0) continue; + x2 = qMin(bounds.width(), x2); + int x1q = x1/8; + int x1r = x1%8; + int x2q = x2/8; + int x2r = x2%8; +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::yellow); +#endif + if (x1q == x2q) { + uchar newbits = (~l[x1q]) & (((2 <<(x2r-x1r))-1) <setPen(Qt::darkGreen); +#endif + addBits(x1r, x2r, newbits, x1q*8, y); + l[x1q] |= newbits; + } + } else { +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::blue); +#endif + uchar newbits1 = (~l[x1q]) & (0xff <setPen(Qt::green); +#endif + addBits(x1r, 7, newbits1, x1q*8, y); + l[x1q] |= newbits1; + } + for (int i = x1q+1; i < x2q; i++) { + if (l[i] != 0xff) { + addBits(0, 7, ~l[i], i*8, y); + l[i] = 0xff; + } + } + uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r)); + if (newbits2) { +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::red); +#endif + addBits(0, x2r, newbits2, x2q*8, y); + l[x2q] |= newbits2; + } + } +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) { + dbg_ptr->drawLine(pt[j], pt[j]+QPoint(w[j], 0)); + } +#endif + } + result.resize(pnt); + } + + int pnt; + QPolygon result; + QtCanvas* canvas; + QRect bounds; + QImage bitmap; +}; + + +QPolygon QtCanvasPolygonalItem::chunks() const +{ + QPolygon pa = areaPoints(); + + if (!pa.size()) { + pa.detach(); // Explicit sharing is stupid. + return pa; + } + + QPolygonalProcessor processor(canvas(), pa); + + scanPolygon(pa, wind, processor); + + return processor.result; +} +/* + Simply calls QtCanvasItem::chunks(). +*/ +QPolygon QtCanvasRectangle::chunks() const +{ + // No need to do a polygon scan! + return QtCanvasItem::chunks(); +} + +/* + Returns the bounding rectangle of the polygonal item, based on + areaPoints(). +*/ +QRect QtCanvasPolygonalItem::boundingRect() const +{ + return areaPoints().boundingRect(); +} + +/* + Reimplemented from QtCanvasItem, this draws the polygonal item by + setting the pen and brush for the item on the painter \a p and + calling drawShape(). +*/ +void QtCanvasPolygonalItem::draw(QPainter & p) +{ + p.setPen(pn); + p.setBrush(br); + drawShape(p); +} + +/* + \fn void QtCanvasPolygonalItem::drawShape(QPainter & p) + + Subclasses must reimplement this function to draw their shape. The + pen and brush of \a p are already set to pen() and brush() prior + to calling this function. + + \sa draw() +*/ + +/* + \fn QPen QtCanvasPolygonalItem::pen() const + + Returns the QPen used to draw the outline of the item, if any. + + \sa setPen() +*/ + +/* + \fn QBrush QtCanvasPolygonalItem::brush() const + + Returns the QBrush used to fill the item, if filled. + + \sa setBrush() +*/ + +/* + Sets the QPen used when drawing the item to the pen \a p. + Note that many QtCanvasPolygonalItems do not use the pen value. + + \sa setBrush(), pen(), drawShape() +*/ +void QtCanvasPolygonalItem::setPen(QPen p) +{ + if (pn != p) { + removeFromChunks(); + pn = p; + addToChunks(); + } +} + +/* + Sets the QBrush used when drawing the polygonal item to the brush \a b. + + \sa setPen(), brush(), drawShape() +*/ +void QtCanvasPolygonalItem::setBrush(QBrush b) +{ + if (br != b) { + br = b; + changeChunks(); + } +} + + +/* + \class QtCanvasPolygon qtcanvas.h + \brief The QtCanvasPolygon class provides a polygon on a QtCanvas. + + Paints a polygon with a QBrush. The polygon's points can be set in + the constructor or set or changed later using setPoints(). Use + points() to retrieve the points, or areaPoints() to retrieve the + points relative to the canvas's origin. + + The polygon can be drawn on a painter with drawShape(). + + Like any other canvas item polygons can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + + Note: QtCanvasPolygon does not use the pen. +*/ + +/* + Constructs a point-less polygon on the canvas \a canvas. You + should call setPoints() before using it further. +*/ +QtCanvasPolygon::QtCanvasPolygon(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas) +{ +} + +/* + Destroys the polygon. +*/ +QtCanvasPolygon::~QtCanvasPolygon() +{ + hide(); +} + +/* + Draws the polygon using the painter \a p. + + Note that QtCanvasPolygon does not support an outline (the pen is + always NoPen). +*/ +void QtCanvasPolygon::drawShape(QPainter & p) +{ + // ### why can't we draw outlines? We could use drawPolyline for it. Lars + // ### see other message. Warwick + + p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-: + p.drawPolygon(poly); +} + +/* + Sets the points of the polygon to be \a pa. These points will have + their x and y coordinates automatically translated by x(), y() as + the polygon is moved. +*/ +void QtCanvasPolygon::setPoints(QPolygon pa) +{ + removeFromChunks(); + poly = pa; + poly.detach(); // Explicit sharing is stupid. + poly.translate((int)x(), (int)y()); + addToChunks(); +} + +/* + \reimp +*/ +void QtCanvasPolygon::moveBy(double dx, double dy) +{ + // Note: does NOT call QtCanvasPolygonalItem::moveBy(), since that + // only does half this work. + // + int idx = int(x()+dx)-int(x()); + int idy = int(y()+dy)-int(y()); + if (idx || idy) { + removeFromChunks(); + poly.translate(idx, idy); + } + myx+= dx; + myy+= dy; + if (idx || idy) { + addToChunks(); + } +} + +/* + \class QtCanvasSpline qtcanvas.h + \brief The QtCanvasSpline class provides multi-bezier splines on a QtCanvas. + + A QtCanvasSpline is a sequence of 4-point bezier curves joined + together to make a curved shape. + + You set the control points of the spline with setControlPoints(). + + If the bezier is closed(), then the first control point will be + re-used as the last control point. Therefore, a closed bezier must + have a multiple of 3 control points and an open bezier must have + one extra point. + + The beziers are not necessarily joined "smoothly". To ensure this, + set control points appropriately (general reference texts about + beziers will explain this in detail). + + Like any other canvas item splines can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + +*/ + +/* + Create a spline with no control points on the canvas \a canvas. + + \sa setControlPoints() +*/ +QtCanvasSpline::QtCanvasSpline(QtCanvas* canvas) : + QtCanvasPolygon(canvas), + cl(true) +{ +} + +/* + Destroy the spline. +*/ +QtCanvasSpline::~QtCanvasSpline() +{ +} + +/* + Set the spline control points to \a ctrl. + + If \a close is true, then the first point in \a ctrl will be + re-used as the last point, and the number of control points must + be a multiple of 3. If \a close is false, one additional control + point is required, and the number of control points must be one of + (4, 7, 10, 13, ...). + + If the number of control points doesn't meet the above conditions, + the number of points will be truncated to the largest number of + points that do meet the requirement. +*/ +void QtCanvasSpline::setControlPoints(QPolygon ctrl, bool close) +{ + if ((int)ctrl.count() % 3 != (close ? 0 : 1)) { + qWarning("QtCanvasSpline::setControlPoints(): Number of points doesn't fit."); + int numCurves = (ctrl.count() - (close ? 0 : 1))/ 3; + ctrl.resize(numCurves*3 + (close ? 0 : 1)); + } + + cl = close; + bez = ctrl; + recalcPoly(); +} + +/* + Returns the current set of control points. + + \sa setControlPoints(), closed() +*/ +QPolygon QtCanvasSpline::controlPoints() const +{ + return bez; +} + +/* + Returns true if the control points are a closed set; otherwise + returns false. +*/ +bool QtCanvasSpline::closed() const +{ + return cl; +} + +void QtCanvasSpline::recalcPoly() +{ + if (bez.count() == 0) + return; + + QPainterPath path; + path.moveTo(bez[0]); + for (int i = 1; i < (int)bez.count()-1; i+= 3) { + path.cubicTo(bez[i], bez[i+1], cl ? bez[(i+2)%bez.size()] : bez[i+2]); + } + QPolygon p = path.toFillPolygon().toPolygon(); + QtCanvasPolygon::setPoints(p); +} + +/* + \fn QPolygon QtCanvasPolygonalItem::areaPoints() const + + This function must be reimplemented by subclasses. It \e must + return the points bounding (i.e. outside and not touching) the + shape or drawing errors will occur. +*/ + +/* + \fn QPolygon QtCanvasPolygon::points() const + + Returns the vertices of the polygon, not translated by the position. + + \sa setPoints(), areaPoints() +*/ +QPolygon QtCanvasPolygon::points() const +{ + QPolygon pa = areaPoints(); + pa.translate(int(-x()), int(-y())); + return pa; +} + +/* + Returns the vertices of the polygon translated by the polygon's + current x(), y() position, i.e. relative to the canvas's origin. + + \sa setPoints(), points() +*/ +QPolygon QtCanvasPolygon::areaPoints() const +{ + return poly; +} + +/* + \class QtCanvasLine qtcanvas.h + \brief The QtCanvasLine class provides a line on a QtCanvas. + + The line inherits functionality from QtCanvasPolygonalItem, for + example the setPen() function. The start and end points of the + line are set with setPoints(). + + Like any other canvas item lines can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). +*/ + +/* + Constructs a line from (0, 0) to (0, 0) on \a canvas. + + \sa setPoints() +*/ +QtCanvasLine::QtCanvasLine(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas) +{ + x1 = y1 = x2 = y2 = 0; +} + +/* + Destroys the line. +*/ +QtCanvasLine::~QtCanvasLine() +{ + hide(); +} + +/* + \reimp +*/ +void QtCanvasLine::setPen(QPen p) +{ + QtCanvasPolygonalItem::setPen(p); +} + +/* + \fn QPoint QtCanvasLine::startPoint () const + + Returns the start point of the line. + + \sa setPoints(), endPoint() +*/ + +/* + \fn QPoint QtCanvasLine::endPoint () const + + Returns the end point of the line. + + \sa setPoints(), startPoint() +*/ + +/* + Sets the line's start point to (\a xa, \a ya) and its end point to + (\a xb, \a yb). +*/ +void QtCanvasLine::setPoints(int xa, int ya, int xb, int yb) +{ + if (x1 != xa || x2 != xb || y1 != ya || y2 != yb) { + removeFromChunks(); + x1 = xa; + y1 = ya; + x2 = xb; + y2 = yb; + addToChunks(); + } +} + +/* + \reimp +*/ +void QtCanvasLine::drawShape(QPainter &p) +{ + p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2)); +} + +/* + \reimp + + Note that the area defined by the line is somewhat thicker than + the line that is actually drawn. +*/ +QPolygon QtCanvasLine::areaPoints() const +{ + QPolygon p(4); + int xi = int(x()); + int yi = int(y()); + int pw = pen().width(); + int dx = qAbs(x1-x2); + int dy = qAbs(y1-y2); + pw = pw*4/3+2; // approx pw*sqrt(2) + int px = x1 < x2 ? -pw : pw ; + int py = y1 < y2 ? -pw : pw ; + if (dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2))) { + // steep + if (px == py) { + p[0] = QPoint(x1+xi, y1+yi+py); + p[1] = QPoint(x2+xi-px, y2+yi); + p[2] = QPoint(x2+xi, y2+yi-py); + p[3] = QPoint(x1+xi+px, y1+yi); + } else { + p[0] = QPoint(x1+xi+px, y1+yi); + p[1] = QPoint(x2+xi, y2+yi-py); + p[2] = QPoint(x2+xi-px, y2+yi); + p[3] = QPoint(x1+xi, y1+yi+py); + } + } else if (dx > dy) { + // horizontal + p[0] = QPoint(x1+xi+px, y1+yi+py); + p[1] = QPoint(x2+xi-px, y2+yi+py); + p[2] = QPoint(x2+xi-px, y2+yi-py); + p[3] = QPoint(x1+xi+px, y1+yi-py); + } else { + // vertical + p[0] = QPoint(x1+xi+px, y1+yi+py); + p[1] = QPoint(x2+xi+px, y2+yi-py); + p[2] = QPoint(x2+xi-px, y2+yi-py); + p[3] = QPoint(x1+xi-px, y1+yi+py); + } + return p; +} + +/* + \reimp + +*/ + +void QtCanvasLine::moveBy(double dx, double dy) +{ + QtCanvasPolygonalItem::moveBy(dx, dy); +} + +/* + \class QtCanvasRectangle qtcanvas.h + \brief The QtCanvasRectangle class provides a rectangle on a QtCanvas. + + This item paints a single rectangle which may have any pen() and + brush(), but may not be tilted/rotated. For rotated rectangles, + use QtCanvasPolygon. + + The rectangle's size and initial position can be set in the + constructor. The size can be set or changed later using setSize(). + Use height() and width() to retrieve the rectangle's dimensions. + + The rectangle can be drawn on a painter with drawShape(). + + Like any other canvas item rectangles can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + +*/ + +/* + Constructs a rectangle at position (0,0) with both width and + height set to 32 pixels on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(32), h(32) +{ +} + +/* + Constructs a rectangle positioned and sized by \a r on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(const QRect& r, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(r.width()), h(r.height()) +{ + move(r.x(), r.y()); +} + +/* + Constructs a rectangle at position (\a x, \a y) and size \a width + by \a height, on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(int x, int y, int width, int height, + QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height) +{ + move(x, y); +} + +/* + Destroys the rectangle. +*/ +QtCanvasRectangle::~QtCanvasRectangle() +{ + hide(); +} + + +/* + Returns the width of the rectangle. +*/ +int QtCanvasRectangle::width() const +{ + return w; +} + +/* + Returns the height of the rectangle. +*/ +int QtCanvasRectangle::height() const +{ + return h; +} + +/* + Sets the \a width and \a height of the rectangle. +*/ +void QtCanvasRectangle::setSize(int width, int height) +{ + if (w != width || h != height) { + removeFromChunks(); + w = width; + h = height; + addToChunks(); + } +} + +/* + \fn QSize QtCanvasRectangle::size() const + + Returns the width() and height() of the rectangle. + + \sa rect(), setSize() +*/ + +/* + \fn QRect QtCanvasRectangle::rect() const + + Returns the integer-converted x(), y() position and size() of the + rectangle as a QRect. +*/ + +/* + \reimp +*/ +QPolygon QtCanvasRectangle::areaPoints() const +{ + QPolygon pa(4); + int pw = (pen().width()+1)/2; + if (pw < 1) pw = 1; + if (pen() == NoPen) pw = 0; + pa[0] = QPoint((int)x()-pw, (int)y()-pw); + pa[1] = pa[0] + QPoint(w+pw*2, 0); + pa[2] = pa[1] + QPoint(0, h+pw*2); + pa[3] = pa[0] + QPoint(0, h+pw*2); + return pa; +} + +/* + Draws the rectangle on painter \a p. +*/ +void QtCanvasRectangle::drawShape(QPainter & p) +{ + p.drawRect((int)x(), (int)y(), w, h); +} + + +/* + \class QtCanvasEllipse qtcanvas.h + \brief The QtCanvasEllipse class provides an ellipse or ellipse segment on a QtCanvas. + + A canvas item that paints an ellipse or ellipse segment with a QBrush. + The ellipse's height, width, start angle and angle length can be set + at construction time. The size can be changed at runtime with + setSize(), and the angles can be changed (if you're displaying an + ellipse segment rather than a whole ellipse) with setAngles(). + + Note that angles are specified in 16ths of a degree. + + \target anglediagram + \img qcanvasellipse.png Ellipse + + If a start angle and length angle are set then an ellipse segment + will be drawn. The start angle is the angle that goes from zero in a + counter-clockwise direction (shown in green in the diagram). The + length angle is the angle from the start angle in a + counter-clockwise direction (shown in blue in the diagram). The blue + segment is the segment of the ellipse that would be drawn. If no + start angle and length angle are specified the entire ellipse is + drawn. + + The ellipse can be drawn on a painter with drawShape(). + + Like any other canvas item ellipses can be moved with move() and + moveBy(), or by setting coordinates with setX(), setY() and setZ(). + + Note: QtCanvasEllipse does not use the pen. +*/ + +/* + Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas. +*/ +QtCanvasEllipse::QtCanvasEllipse(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(32), h(32), + a1(0), a2(360*16) +{ +} + +/* + Constructs a \a width by \a height pixel ellipse, centered at + (0, 0) on \a canvas. +*/ +QtCanvasEllipse::QtCanvasEllipse(int width, int height, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height), + a1(0), a2(360*16) +{ +} + +// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars +// ### it's how QPainter does it, so QtCanvas does too for consistency. If it's +// ### a good idea, it should be added to QPainter, not just to QtCanvas. Warwick +/* + Constructs a \a width by \a height pixel ellipse, centered at + (0, 0) on \a canvas. Only a segment of the ellipse is drawn, + starting at angle \a startangle, and extending for angle \a angle + (the angle length). + + Note that angles are specified in sixteenths of a degree. +*/ +QtCanvasEllipse::QtCanvasEllipse(int width, int height, + int startangle, int angle, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height), + a1(startangle), a2(angle) +{ +} + +/* + Destroys the ellipse. +*/ +QtCanvasEllipse::~QtCanvasEllipse() +{ + hide(); +} + +/* + Returns the width of the ellipse. +*/ +int QtCanvasEllipse::width() const +{ + return w; +} + +/* + Returns the height of the ellipse. +*/ +int QtCanvasEllipse::height() const +{ + return h; +} + +/* + Sets the \a width and \a height of the ellipse. +*/ +void QtCanvasEllipse::setSize(int width, int height) +{ + if (w != width || h != height) { + removeFromChunks(); + w = width; + h = height; + addToChunks(); + } +} + +/* + \fn int QtCanvasEllipse::angleStart() const + + Returns the start angle in 16ths of a degree. Initially + this will be 0. + + \sa setAngles(), angleLength() +*/ + +/* + \fn int QtCanvasEllipse::angleLength() const + + Returns the length angle (the extent of the ellipse segment) in + 16ths of a degree. Initially this will be 360 * 16 (a complete + ellipse). + + \sa setAngles(), angleStart() +*/ + +/* + Sets the angles for the ellipse. The start angle is \a start and + the extent of the segment is \a length (the angle length) from the + \a start. The angles are specified in 16ths of a degree. By + default the ellipse will start at 0 and have an angle length of + 360 * 16 (a complete ellipse). + + \sa angleStart(), angleLength() +*/ +void QtCanvasEllipse::setAngles(int start, int length) +{ + if (a1 != start || a2 != length) { + removeFromChunks(); + a1 = start; + a2 = length; + addToChunks(); + } +} + +/* + \reimp +*/ +QPolygon QtCanvasEllipse::areaPoints() const +{ + QPainterPath path; + path.arcTo(QRectF(x()-w/2.0+0.5-1, y()-h/2.0+0.5-1, w+3, h+3), a1/16., a2/16.); + return path.toFillPolygon().toPolygon(); +} + +/* + Draws the ellipse, centered at x(), y() using the painter \a p. + + Note that QtCanvasEllipse does not support an outline (the pen is + always NoPen). +*/ +void QtCanvasEllipse::drawShape(QPainter & p) +{ + p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-: + if (!a1 && a2 == 360*16) { + p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h); + } else { + p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2); + } +} + + +/* + \class QtCanvasText + \brief The QtCanvasText class provides a text object on a QtCanvas. + + A canvas text item has text with font, color and alignment + attributes. The text and font can be set in the constructor or set + or changed later with setText() and setFont(). The color is set + with setColor() and the alignment with setTextFlags(). The text + item's bounding rectangle is retrieved with boundingRect(). + + The text can be drawn on a painter with draw(). + + Like any other canvas item text items can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). +*/ + +/* + Constructs a QtCanvasText with the text "\", on \a canvas. +*/ +QtCanvasText::QtCanvasText(QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(""), flags(0) +{ + setRect(); +} + +// ### add textflags to the constructor? Lars +/* + Constructs a QtCanvasText with the text \a t, on canvas \a canvas. +*/ +QtCanvasText::QtCanvasText(const QString& t, QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(t), flags(0) +{ + setRect(); +} + +// ### see above +/* + Constructs a QtCanvasText with the text \a t and font \a f, on the + canvas \a canvas. +*/ +QtCanvasText::QtCanvasText(const QString& t, QFont f, QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(t), flags(0), + fnt(f) +{ + setRect(); +} + +/* + Destroys the canvas text item. +*/ +QtCanvasText::~QtCanvasText() +{ + removeFromChunks(); +} + +/* + Returns the bounding rectangle of the text. +*/ +QRect QtCanvasText::boundingRect() const { return brect; } + +void QtCanvasText::setRect() +{ + brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt); +} + +/* + \fn int QtCanvasText::textFlags() const + + Returns the currently set alignment flags. + + \sa setTextFlags() Qt::AlignmentFlag Qt::TextFlag +*/ + + +/* + Sets the alignment flags to \a f. These are a bitwise OR of the + flags available to QPainter::drawText() -- see the + \l{Qt::AlignmentFlag}s and \l{Qt::TextFlag}s. + + \sa setFont() setColor() +*/ +void QtCanvasText::setTextFlags(int f) +{ + if (flags != f) { + removeFromChunks(); + flags = f; + setRect(); + addToChunks(); + } +} + +/* + Returns the text item's text. + + \sa setText() +*/ +QString QtCanvasText::text() const +{ + return txt; +} + + +/* + Sets the text item's text to \a t. The text may contain newlines. + + \sa text(), setFont(), setColor() setTextFlags() +*/ +void QtCanvasText::setText(const QString& t) +{ + if (txt != t) { + removeFromChunks(); + txt = t; + setRect(); + addToChunks(); + } +} + +/* + Returns the font in which the text is drawn. + + \sa setFont() +*/ +QFont QtCanvasText::font() const +{ + return fnt; +} + +/* + Sets the font in which the text is drawn to font \a f. + + \sa font() +*/ +void QtCanvasText::setFont(const QFont& f) +{ + if (f != fnt) { + removeFromChunks(); + fnt = f; + setRect(); + addToChunks(); + } +} + +/* + Returns the color of the text. + + \sa setColor() +*/ +QColor QtCanvasText::color() const +{ + return col; +} + +/* + Sets the color of the text to the color \a c. + + \sa color(), setFont() +*/ +void QtCanvasText::setColor(const QColor& c) +{ + col = c; + changeChunks(); +} + + +/* + \reimp +*/ +void QtCanvasText::moveBy(double dx, double dy) +{ + int idx = int(x()+dx)-int(x()); + int idy = int(y()+dy)-int(y()); + if (idx || idy) { + removeFromChunks(); + } + myx+= dx; + myy+= dy; + if (idx || idy) { + brect.translate(idx, idy); + addToChunks(); + } +} + +/* + Draws the text using the painter \a painter. +*/ +void QtCanvasText::draw(QPainter& painter) +{ + painter.setFont(fnt); + painter.setPen(col); + painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt); +} + +/* + \reimp +*/ +void QtCanvasText::changeChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->setChangedChunk(i, j); + } + } + } +} + +/* + Adds the text item to the appropriate chunks. +*/ +void QtCanvasText::addToChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->addItemToChunk(this, i, j); + } + } + } +} + +/* + Removes the text item from the appropriate chunks. +*/ +void QtCanvasText::removeFromChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->removeItemFromChunk(this, i, j); + } + } + } +} + + +/* + Returns 0 (QtCanvasItem::Rtti_Item). + + Make your derived classes return their own values for rtti(), so + that you can distinguish between objects returned by + QtCanvas::at(). You should use values greater than 1000 to allow + for extensions to this class. + + Overuse of this functionality can damage its extensibility. For + example, once you have identified a base class of a QtCanvasItem + found by QtCanvas::at(), cast it to that type and call meaningful + methods rather than acting upon the object based on its rtti + value. + + For example: + + \code + QtCanvasItem* item; + // Find an item, e.g. with QtCanvasItem::collisions(). + ... + if (item->rtti() == MySprite::RTTI) { + MySprite* s = (MySprite*)item; + if (s->isDamagable()) s->loseHitPoints(1000); + if (s->isHot()) myself->loseHitPoints(1000); + ... + } + \endcode +*/ +int QtCanvasItem::rtti() const { return RTTI; } +int QtCanvasItem::RTTI = Rtti_Item; + +/* + Returns 1 (QtCanvasItem::Rtti_Sprite). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasSprite::rtti() const { return RTTI; } +int QtCanvasSprite::RTTI = Rtti_Sprite; + +/* + Returns 2 (QtCanvasItem::Rtti_PolygonalItem). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasPolygonalItem::rtti() const { return RTTI; } +int QtCanvasPolygonalItem::RTTI = Rtti_PolygonalItem; + +/* + Returns 3 (QtCanvasItem::Rtti_Text). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasText::rtti() const { return RTTI; } +int QtCanvasText::RTTI = Rtti_Text; + +/* + Returns 4 (QtCanvasItem::Rtti_Polygon). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasPolygon::rtti() const { return RTTI; } +int QtCanvasPolygon::RTTI = Rtti_Polygon; + +/* + Returns 5 (QtCanvasItem::Rtti_Rectangle). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasRectangle::rtti() const { return RTTI; } +int QtCanvasRectangle::RTTI = Rtti_Rectangle; + +/* + Returns 6 (QtCanvasItem::Rtti_Ellipse). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasEllipse::rtti() const { return RTTI; } +int QtCanvasEllipse::RTTI = Rtti_Ellipse; + +/* + Returns 7 (QtCanvasItem::Rtti_Line). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasLine::rtti() const { return RTTI; } +int QtCanvasLine::RTTI = Rtti_Line; + +/* + Returns 8 (QtCanvasItem::Rtti_Spline). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasSpline::rtti() const { return RTTI; } +int QtCanvasSpline::RTTI = Rtti_Spline; + +/* + Constructs a QtCanvasSprite which uses images from the + QtCanvasPixmapArray \a a. + + The sprite in initially positioned at (0, 0) on \a canvas, using + frame 0. +*/ +QtCanvasSprite::QtCanvasSprite(QtCanvasPixmapArray* a, QtCanvas* canvas) : + QtCanvasItem(canvas), + frm(0), + anim_val(0), + anim_state(0), + anim_type(0), + images(a) +{ +} + + +/* + Set the array of images used for displaying the sprite to the + QtCanvasPixmapArray \a a. + + If the current frame() is larger than the number of images in \a + a, the current frame will be reset to 0. +*/ +void QtCanvasSprite::setSequence(QtCanvasPixmapArray* a) +{ + bool isvisible = isVisible(); + if (isvisible && images) + hide(); + images = a; + if (frm >= (int)images->count()) + frm = 0; + if (isvisible) + show(); +} + +/* +\internal + +Marks any chunks the sprite touches as changed. +*/ +void QtCanvasSprite::changeChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->setChangedChunk(i, j); + } + } + } +} + +/* + Destroys the sprite and removes it from the canvas. Does \e not + delete the images. +*/ +QtCanvasSprite::~QtCanvasSprite() +{ + removeFromChunks(); +} + +/* + Sets the animation frame used for displaying the sprite to \a f, + an index into the QtCanvasSprite's QtCanvasPixmapArray. The call + will be ignored if \a f is larger than frameCount() or smaller + than 0. + + \sa frame() move() +*/ +void QtCanvasSprite::setFrame(int f) +{ + move(x(), y(), f); +} + +/* + \enum QtCanvasSprite::FrameAnimationType + + This enum is used to identify the different types of frame + animation offered by QtCanvasSprite. + + \value Cycle at each advance the frame number will be incremented by + 1 (modulo the frame count). + \value Oscillate at each advance the frame number will be + incremented by 1 up to the frame count then decremented to by 1 to + 0, repeating this sequence forever. +*/ + +/* + Sets the animation characteristics for the sprite. + + For \a type == \c Cycle, the frames will increase by \a step + at each advance, modulo the frameCount(). + + For \a type == \c Oscillate, the frames will increase by \a step + at each advance, up to the frameCount(), then decrease by \a step + back to 0, repeating forever. + + The \a state parameter is for internal use. +*/ +void QtCanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state) +{ + anim_val = step; + anim_type = type; + anim_state = state; + setAnimated(true); +} + +/* + Extends the default QtCanvasItem implementation to provide the + functionality of setFrameAnimation(). + + The \a phase is 0 or 1: see QtCanvasItem::advance() for details. + + \sa QtCanvasItem::advance() setVelocity() +*/ +void QtCanvasSprite::advance(int phase) +{ + if (phase == 1) { + int nf = frame(); + if (anim_type == Oscillate) { + if (anim_state) + nf += anim_val; + else + nf -= anim_val; + if (nf < 0) { + nf = abs(anim_val); + anim_state = !anim_state; + } else if (nf >= frameCount()) { + nf = frameCount()-1-abs(anim_val); + anim_state = !anim_state; + } + } else { + nf = (nf + anim_val + frameCount()) % frameCount(); + } + move(x()+xVelocity(), y()+yVelocity(), nf); + } +} + + +/* + \fn int QtCanvasSprite::frame() const + + Returns the index of the current animation frame in the + QtCanvasSprite's QtCanvasPixmapArray. + + \sa setFrame(), move() +*/ + +/* + \fn int QtCanvasSprite::frameCount() const + + Returns the number of frames in the QtCanvasSprite's + QtCanvasPixmapArray. +*/ + + +/* + Moves the sprite to (\a x, \a y). +*/ +void QtCanvasSprite::move(double x, double y) { QtCanvasItem::move(x, y); } + +/* + \fn void QtCanvasSprite::move(double nx, double ny, int nf) + + Moves the sprite to (\a nx, \a ny) and sets the current + frame to \a nf. \a nf will be ignored if it is larger than + frameCount() or smaller than 0. +*/ +void QtCanvasSprite::move(double nx, double ny, int nf) +{ + if (isVisible() && canvas()) { + hide(); + QtCanvasItem::move(nx, ny); + if (nf >= 0 && nf < frameCount()) + frm = nf; + show(); + } else { + QtCanvasItem::move(nx, ny); + if (nf >= 0 && nf < frameCount()) + frm = nf; + } +} + + +class QPoint; + +class QtPolygonScanner { +public: + virtual ~QtPolygonScanner() {} + void scan(const QPolygon& pa, bool winding, int index = 0, int npoints = -1); + void scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable); + enum Edge { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + void scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges); + virtual void processSpans(int n, QPoint* point, int* width) = 0; +}; + + +// Based on Xserver code miFillGeneralPoly... +/* + * + * Written by Brian Kelleher; Oct. 1985 + * + * Routine to fill a polygon. Two fill rules are + * supported: frWINDING and frEVENODD. + * + * See fillpoly.h for a complete description of the algorithm. + */ + +/* + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counterclockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* $XConsortium: miscanfill.h, v 1.5 94/04/17 20:27:50 dpw Exp $ */ +/* + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + + +/* + * scanfill.h + * + * Written by Brian Kelleher; Jan 1985 + * + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2) + + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres) \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + +/*********************************************************** + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#define MAXINT 0x7fffffff +#define MININT -MAXINT + +/* + * fillUtils.c + * + * Written by Brian Kelleher; Oct. 1985 + * + * This module contains all of the utility functions + * needed to scan convert a polygon. + * + */ +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +static bool +miInsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, + int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock) +{ + register EdgeTableEntry *start, *prev; + register ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock)); + if (!tmpSLLBlock) + return false; + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = 0; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = 0; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = 0; + start = pSLL->edgelist; + while (start && (start->bres.minor < ETE->bres.minor)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; + return true; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +typedef struct { +#if defined(Q_OS_MAC) + int y, x; +#else + int x, y; +#endif + +} DDXPointRec, *DDXPointPtr; + +/* + * Clean up our act. + */ +static void +miFreeStorage(ScanLineListBlock *pSLLBlock) +{ + register ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + free(pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} + +static bool +miCreateETandAET(int count, DDXPointPtr pts, EdgeTable *ET, + EdgeTableEntry *AET, EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock) +{ + register DDXPointPtr top, bottom; + register DDXPointPtr PrevPt, CurrPt; + int iSLLBlock = 0; + + int dy; + + if (count < 2) return true; + + /* + * initialize the Active Edge Table + */ + AET->next = 0; + AET->back = 0; + AET->nextWETE = 0; + AET->bres.minor = MININT; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = 0; + ET->ymax = MININT; + ET->ymin = MAXINT; + pSLLBlock->next = 0; + + PrevPt = &pts[count-1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres) + + if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock)) + { + miFreeStorage(pSLLBlock->next); + return false; + } + + ET->ymax = qMax(ET->ymax, PrevPt->y); + ET->ymin = qMin(ET->ymin, PrevPt->y); + pETEs++; + } + + PrevPt = CurrPt; + } + return true; +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +static void +miloadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs) +{ + register EdgeTableEntry *pPrevAET; + register EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor < ETEs->bres.minor)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +static void +micomputeWAET(EdgeTableEntry *AET) +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = 0; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + (inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = 0; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +static int +miInsertionSort(EdgeTableEntry *AET) +{ + register EdgeTableEntry *pETEchase; + register EdgeTableEntry *pETEinsert; + register EdgeTableEntry *pETEchaseBackTMP; + register int changed = 0; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor > AET->bres.minor) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return changed; +} + +/* + \overload +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints) +{ + scan(pa, winding, index, npoints, true); +} + +/* + \overload + + If \a stitchable is false, the right and bottom edges of the + polygon are included. This causes adjacent polygons to overlap. +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable) +{ + scan(pa, winding, index, npoints, + stitchable ? Edge(Left+Top) : Edge(Left+Right+Top+Bottom)); +} + +/* + Calls processSpans() for all scanlines of the polygon defined by + \a npoints starting at \a index in \a pa. + + If \a winding is true, the Winding algorithm rather than the + Odd-Even rule is used. + + The \a edges is any bitwise combination of: + \list + \i QtPolygonScanner::Left + \i QtPolygonScanner::Right + \i QtPolygonScanner::Top + \i QtPolygonScanner::Bottom + \endlist + \a edges determines which edges are included. + + \warning The edges feature does not work properly. + +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges) +{ + + + DDXPointPtr ptsIn = (DDXPointPtr)pa.data(); + ptsIn += index; + register EdgeTableEntry *pAET; /* the Active Edge Table */ + register int y; /* the current scanline */ + register int nPts = 0; /* number of pts in buffer */ + register EdgeTableEntry *pWETE; /* Winding Edge Table */ + register ScanLineList *pSLL; /* Current ScanLineList */ + register DDXPointPtr ptsOut; /* ptr to output buffers */ + int *width; + DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */ + int FirstWidth[NUMPTSTOBUFFER]; + EdgeTableEntry *pPrevAET; /* previous AET entry */ + EdgeTable ET; /* Edge Table header node */ + EdgeTableEntry AET; /* Active ET header node */ + EdgeTableEntry *pETEs; /* Edge Table Entries buff */ + ScanLineListBlock SLLBlock; /* header for ScanLineList */ + int fixWAET = 0; + int edge_l = (edges & Left) ? 1 : 0; + int edge_r = (edges & Right) ? 1 : 0; + int edge_t = 1; //#### (edges & Top) ? 1 : 0; + int edge_b = (edges & Bottom) ? 1 : 0; + + if (npoints == -1) + npoints = pa.size(); + + if (npoints < 3) + return; + + if(!(pETEs = (EdgeTableEntry *) + malloc(sizeof(EdgeTableEntry) * npoints))) + return; + ptsOut = FirstPoint; + width = FirstWidth; + if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock)) + { + free(pETEs); + return; + } + pSLL = ET.scanlines.next; + + if (!winding) + { + /* + * for each scanline + */ + for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) + { + ptsOut->x = pAET->bres.minor + 1 - edge_l; + ptsOut++->y = y; + *width++ = pAET->next->bres.minor - pAET->bres.minor + - 1 + edge_l + edge_r; + nPts++; + + /* + * send out the buffer when its full + */ + if (nPts == NUMPTSTOBUFFER) + { + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + } + miInsertionSort(&AET); + } + } + else /* default to WindingNumber */ + { + /* + * for each scanline + */ + for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + micomputeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) + { + /* + * if the next edge in the active edge table is + * also the next edge in the winding active edge + * table. + */ + if (pWETE == pAET) + { + ptsOut->x = pAET->bres.minor + 1 - edge_l; + ptsOut++->y = y; + *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r; + nPts++; + + /* + * send out the buffer + */ + if (nPts == NUMPTSTOBUFFER) + { + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + + pWETE = pWETE->nextWETE; + while (pWETE != pAET) { + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) + } + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) + } + + /* + * reevaluate the Winding active edge table if we + * just had to resort it or if we just exited an edge. + */ + if (miInsertionSort(&AET) || fixWAET) + { + micomputeWAET(&AET); + fixWAET = 0; + } + } + } + + /* + * Get any spans that we missed by buffering + */ + + + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + free(pETEs); + miFreeStorage(SLLBlock.next); +} +/***** END OF X11-based CODE *****/ + + + + + +class QtCanvasPolygonScanner : public QtPolygonScanner { + QPolygonalProcessor& processor; +public: + QtCanvasPolygonScanner(QPolygonalProcessor& p) : + processor(p) + { + } + void processSpans(int n, QPoint* point, int* width) + { + processor.doSpans(n, point, width); + } +}; + +void QtCanvasPolygonalItem::scanPolygon(const QPolygon& pa, int winding, QPolygonalProcessor& process) const +{ + QtCanvasPolygonScanner scanner(process); + scanner.scan(pa, winding); +} diff --git a/examples/canvas_typed/qtcanvas.h b/examples/canvas_typed/qtcanvas.h new file mode 100644 index 0000000..e6d90db --- /dev/null +++ b/examples/canvas_typed/qtcanvas.h @@ -0,0 +1,778 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#ifndef QTCANVAS_H +#define QTCANVAS_H + +#include +#include +#include +#include +#include + +class QtCanvasSprite; +class QtCanvasPolygonalItem; +class QtCanvasRectangle; +class QtCanvasPolygon; +class QtCanvasEllipse; +class QtCanvasText; +class QtCanvasLine; +class QtCanvasChunk; +class QtCanvas; +class QtCanvasItem; +class QtCanvasView; +class QtCanvasPixmap; + +typedef QList QtCanvasItemList; + + +class QtCanvasItemExtra; + +class QtCanvasItem +{ +public: + QtCanvasItem(QtCanvas* canvas); + virtual ~QtCanvasItem(); + + double x() const + { return myx; } + double y() const + { return myy; } + double z() const + { return myz; } // (depth) + + virtual void moveBy(double dx, double dy); + void move(double x, double y); + void setX(double a) { move(a,y()); } + void setY(double a) { move(x(),a); } + void setZ(double a) { myz=a; changeChunks(); } + + bool animated() const; + virtual void setAnimated(bool y); + virtual void setVelocity(double vx, double vy); + void setXVelocity(double vx) { setVelocity(vx,yVelocity()); } + void setYVelocity(double vy) { setVelocity(xVelocity(),vy); } + double xVelocity() const; + double yVelocity() const; + virtual void advance(int stage); + + virtual bool collidesWith(const QtCanvasItem*) const=0; + + QtCanvasItemList collisions(bool exact /* NO DEFAULT */) const; + + virtual void setCanvas(QtCanvas*); + + virtual void draw(QPainter&)=0; + + void show(); + void hide(); + + virtual void setVisible(bool yes); + bool isVisible() const + { return (bool)vis; } + virtual void setSelected(bool yes); + bool isSelected() const + { return (bool)sel; } + virtual void setEnabled(bool yes); + bool isEnabled() const + { return (bool)ena; } + virtual void setActive(bool yes); + bool isActive() const + { return (bool)act; } + bool visible() const + { return (bool)vis; } + bool selected() const + { return (bool)sel; } + bool enabled() const + { return (bool)ena; } + bool active() const + { return (bool)act; } + + enum RttiValues { + Rtti_Item = 0, + Rtti_Sprite = 1, + Rtti_PolygonalItem = 2, + Rtti_Text = 3, + Rtti_Polygon = 4, + Rtti_Rectangle = 5, + Rtti_Ellipse = 6, + Rtti_Line = 7, + Rtti_Spline = 8 + }; + + virtual int rtti() const; + static int RTTI; + + virtual QRect boundingRect() const=0; + virtual QRect boundingRectAdvanced() const; + + QtCanvas* canvas() const + { return cnv; } + +protected: + void update() { changeChunks(); } + +private: + // For friendly subclasses... + + friend class QtCanvasPolygonalItem; + friend class QtCanvasSprite; + friend class QtCanvasRectangle; + friend class QtCanvasPolygon; + friend class QtCanvasEllipse; + friend class QtCanvasText; + friend class QtCanvasLine; + + virtual QPolygon chunks() const; + virtual void addToChunks(); + virtual void removeFromChunks(); + virtual void changeChunks(); + virtual bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const = 0; + // End of friend stuff + + QtCanvas* cnv; + static QtCanvas* current_canvas; + double myx,myy,myz; + QtCanvasItemExtra *ext; + QtCanvasItemExtra& extra(); + uint ani:1; + uint vis:1; + uint val:1; + uint sel:1; + uint ena:1; + uint act:1; +}; + + +class QtCanvasData; + +class QtCanvas : public QObject +{ + Q_OBJECT +public: + QtCanvas(QObject* parent = 0); + QtCanvas(int w, int h); + QtCanvas(QPixmap p, int h, int v, int tilewidth, int tileheight); + + virtual ~QtCanvas(); + + virtual void setTiles(QPixmap tiles, int h, int v, + int tilewidth, int tileheight); + virtual void setBackgroundPixmap(const QPixmap& p); + QPixmap backgroundPixmap() const; + + virtual void setBackgroundColor(const QColor& c); + QColor backgroundColor() const; + + virtual void setTile(int x, int y, int tilenum); + int tile(int x, int y) const + { return grid[x+y*htiles]; } + + int tilesHorizontally() const + { return htiles; } + int tilesVertically() const + { return vtiles; } + + int tileWidth() const + { return tilew; } + int tileHeight() const + { return tileh; } + + virtual void resize(int width, int height); + int width() const + { return awidth; } + int height() const + { return aheight; } + QSize size() const + { return QSize(awidth,aheight); } + QRect rect() const + { return QRect(0, 0, awidth, aheight); } + bool onCanvas(int x, int y) const + { return x>=0 && y>=0 && x=0 && y>=0 && x &pixmaps, const QPolygon &hotspots = QPolygon()); + ~QtCanvasPixmapArray(); + +#ifndef QT_NO_IMAGEIO + bool readPixmaps(const QString& datafilenamepattern, int framecount=0); + bool readCollisionMasks(const QString& filenamepattern); +#endif + + // deprecated + bool operator!(); // Failure check. + bool isValid() const; + + QtCanvasPixmap* image(int i) const + { return img ? img[i] : 0; } + void setImage(int i, QtCanvasPixmap* p); + uint count() const + { return (uint)framecount; } + +private: + Q_DISABLE_COPY(QtCanvasPixmapArray) + +#ifndef QT_NO_IMAGEIO + bool readPixmaps(const QString& datafilenamepattern, int framecount, bool maskonly); +#endif + + void reset(); + int framecount; + QtCanvasPixmap** img; +}; + + +class QtCanvasSprite : public QtCanvasItem +{ +public: + QtCanvasSprite(QtCanvasPixmapArray* array, QtCanvas* canvas); + + void setSequence(QtCanvasPixmapArray* seq); + + virtual ~QtCanvasSprite(); + + void move(double x, double y); + virtual void move(double x, double y, int frame); + void setFrame(int); + enum FrameAnimationType { Cycle, Oscillate }; + virtual void setFrameAnimation(FrameAnimationType=Cycle, int step=1, int state=0); + int frame() const + { return frm; } + int frameCount() const + { return images->count(); } + + int rtti() const; + static int RTTI; + + bool collidesWith(const QtCanvasItem*) const; + + QRect boundingRect() const; + + // is there a reason for these to be protected? Lars +//protected: + + int width() const; + int height() const; + + int leftEdge() const; + int topEdge() const; + int rightEdge() const; + int bottomEdge() const; + + int leftEdge(int nx) const; + int topEdge(int ny) const; + int rightEdge(int nx) const; + int bottomEdge(int ny) const; + QtCanvasPixmap* image() const + { return images->image(frm); } + virtual QtCanvasPixmap* imageAdvanced() const; + QtCanvasPixmap* image(int f) const + { return images->image(f); } + virtual void advance(int stage); + +public: + void draw(QPainter& painter); + +private: + Q_DISABLE_COPY(QtCanvasSprite) + + void addToChunks(); + void removeFromChunks(); + void changeChunks(); + + int frm; + ushort anim_val; + uint anim_state:2; + uint anim_type:14; + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + friend bool qt_testCollision(const QtCanvasSprite* s1, + const QtCanvasSprite* s2); + + QtCanvasPixmapArray* images; +}; + +class QPolygonalProcessor; + +class QtCanvasPolygonalItem : public QtCanvasItem +{ +public: + QtCanvasPolygonalItem(QtCanvas* canvas); + virtual ~QtCanvasPolygonalItem(); + + bool collidesWith(const QtCanvasItem*) const; + + virtual void setPen(QPen p); + virtual void setBrush(QBrush b); + + QPen pen() const + { return pn; } + QBrush brush() const + { return br; } + + virtual QPolygon areaPoints() const=0; + virtual QPolygon areaPointsAdvanced() const; + QRect boundingRect() const; + + int rtti() const; + static int RTTI; + +protected: + void draw(QPainter &); + virtual void drawShape(QPainter &) = 0; + + bool winding() const; + void setWinding(bool); + + void invalidate(); + bool isValid() const + { return (bool)val; } + +private: + void scanPolygon(const QPolygon& pa, int winding, + QPolygonalProcessor& process) const; + QPolygon chunks() const; + + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + QBrush br; + QPen pn; + uint wind:1; +}; + + +class QtCanvasRectangle : public QtCanvasPolygonalItem +{ +public: + QtCanvasRectangle(QtCanvas* canvas); + QtCanvasRectangle(const QRect&, QtCanvas* canvas); + QtCanvasRectangle(int x, int y, int width, int height, QtCanvas* canvas); + + ~QtCanvasRectangle(); + + int width() const; + int height() const; + void setSize(int w, int h); + QSize size() const + { return QSize(w,h); } + QPolygon areaPoints() const; + QRect rect() const + { return QRect(int(x()),int(y()),w,h); } + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + QPolygon chunks() const; + +private: + bool collidesWith( const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + int w, h; +}; + + +class QtCanvasPolygon : public QtCanvasPolygonalItem +{ +public: + QtCanvasPolygon(QtCanvas* canvas); + ~QtCanvasPolygon(); + void setPoints(QPolygon); + QPolygon points() const; + void moveBy(double dx, double dy); + + QPolygon areaPoints() const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + QPolygon poly; +}; + + +class QtCanvasSpline : public QtCanvasPolygon +{ +public: + QtCanvasSpline(QtCanvas* canvas); + ~QtCanvasSpline(); + + void setControlPoints(QPolygon, bool closed=true); + QPolygon controlPoints() const; + bool closed() const; + + int rtti() const; + static int RTTI; + +private: + void recalcPoly(); + QPolygon bez; + bool cl; +}; + + +class QtCanvasLine : public QtCanvasPolygonalItem +{ +public: + QtCanvasLine(QtCanvas* canvas); + ~QtCanvasLine(); + void setPoints(int x1, int y1, int x2, int y2); + + QPoint startPoint() const + { return QPoint(x1,y1); } + QPoint endPoint() const + { return QPoint(x2,y2); } + + int rtti() const; + static int RTTI; + + void setPen(QPen p); + void moveBy(double dx, double dy); + +protected: + void drawShape(QPainter &); + QPolygon areaPoints() const; + +private: + int x1,y1,x2,y2; +}; + + +class QtCanvasEllipse : public QtCanvasPolygonalItem +{ + +public: + QtCanvasEllipse(QtCanvas* canvas); + QtCanvasEllipse(int width, int height, QtCanvas* canvas); + QtCanvasEllipse(int width, int height, int startangle, int angle, + QtCanvas* canvas); + + ~QtCanvasEllipse(); + + int width() const; + int height() const; + void setSize(int w, int h); + void setAngles(int start, int length); + int angleStart() const + { return a1; } + int angleLength() const + { return a2; } + QPolygon areaPoints() const; + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + +private: + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + int w, h; + int a1, a2; +}; + + +class QtCanvasTextExtra; + +class QtCanvasText : public QtCanvasItem +{ +public: + QtCanvasText(QtCanvas* canvas); + QtCanvasText(const QString&, QtCanvas* canvas); + QtCanvasText(const QString&, QFont, QtCanvas* canvas); + + virtual ~QtCanvasText(); + + void setText(const QString&); + void setFont(const QFont&); + void setColor(const QColor&); + QString text() const; + QFont font() const; + QColor color() const; + + void moveBy(double dx, double dy); + + int textFlags() const + { return flags; } + void setTextFlags(int); + + QRect boundingRect() const; + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + virtual void draw(QPainter&); + +private: + Q_DISABLE_COPY(QtCanvasText) + + void addToChunks(); + void removeFromChunks(); + void changeChunks(); + + void setRect(); + QRect brect; + QString txt; + int flags; + QFont fnt; + QColor col; + QtCanvasTextExtra* extra; + + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; +}; + +#endif // QTCANVAS_H diff --git a/examples/canvas_variant/CMakeLists.txt b/examples/canvas_variant/CMakeLists.txt new file mode 100644 index 0000000..2191be1 --- /dev/null +++ b/examples/canvas_variant/CMakeLists.txt @@ -0,0 +1,19 @@ +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(example_name canvas_variant) + +SET(KIT_SRCS + main.cpp + mainwindow.cpp + mainwindow.h + qtcanvas.cpp + qtcanvas.h + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) diff --git a/examples/canvas_variant/canvas_variant.qdoc b/examples/canvas_variant/canvas_variant.qdoc new file mode 100644 index 0000000..b106e83 --- /dev/null +++ b/examples/canvas_variant/canvas_variant.qdoc @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-canvas_variant.html + \title Variant Based Canvas Example + + This example demonstrates how to use the QtVariantPropertyManager + convenience class for all property types. In this approach only + one instance of the property manager class is used, and the + developer interfaces with a dynamic API based on QVariant. + + \image canvas_variant.png + + The example presents a canvas filled up with items of different + types, and a tree property browser which displays the currently + selected item's properties. + + All item types has a few common properties like "Position X", "Position Y" + or "Position Z", but each type also adds its own type-specific + properties (e.g. the text items provide "Text" and "Font" + properties, and the line items provide a "Vector" property). + + The source files can be found in examples/canvas_variant directory + of the package. + + \section1 Third party copyright notice + + The canvas class used in this example contains third party code + with the following copyright notice: + + \legalese + \code + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + + \endcode + \endlegalese + +*/ diff --git a/examples/canvas_variant/main.cpp b/examples/canvas_variant/main.cpp new file mode 100644 index 0000000..6127bba --- /dev/null +++ b/examples/canvas_variant/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include "mainwindow.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + MainWindow mw; + mw.show(); + + return app.exec(); +} diff --git a/examples/canvas_variant/mainwindow.cpp b/examples/canvas_variant/mainwindow.cpp new file mode 100644 index 0000000..8fa04c5 --- /dev/null +++ b/examples/canvas_variant/mainwindow.cpp @@ -0,0 +1,434 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include "mainwindow.h" +#include "qtvariantproperty.h" +#include "qttreepropertybrowser.h" +#include +#include +#include +#include +#include +#include + +void CanvasView::contentsMousePressEvent(QMouseEvent* event) +{ + handleMouseClickEvent(event); +} + +void CanvasView::contentsMouseDoubleClickEvent(QMouseEvent* event) +{ + handleMouseClickEvent(event); +} + +void CanvasView::handleMouseClickEvent(QMouseEvent* event) +{ + QPoint p = inverseWorldMatrix().map(event->pos()); + QtCanvasItemList l = canvas()->collisions(p); + moving = 0; + if (!l.isEmpty()) + moving = l.first(); + moving_start = p; + emit itemClicked(moving); +} + +void CanvasView::contentsMouseMoveEvent(QMouseEvent* event) +{ + if (moving) { + QPoint p = inverseWorldMatrix().map(event->pos()); + moving->moveBy(p.x() - moving_start.x(), p.y() - moving_start.y()); + moving_start = p; + canvas()->update(); + emit itemMoved(moving); + } +} + + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) +{ + QMenu *editMenu = menuBar()->addMenu(tr("Edit")); + QMenu *newObjectMenu = editMenu->addMenu(tr("New Object")); + + QAction *newRectangleAction = new QAction(tr("Rectangle"), this); + connect(newRectangleAction, SIGNAL(triggered(bool)), this, SLOT(newRectangle())); + newObjectMenu->addAction(newRectangleAction); + + QAction *newLineAction = new QAction(tr("Line"), this); + connect(newLineAction, SIGNAL(triggered(bool)), this, SLOT(newLine())); + newObjectMenu->addAction(newLineAction); + + QAction *newEllipseAction = new QAction(tr("Ellipse"), this); + connect(newEllipseAction, SIGNAL(triggered(bool)), this, SLOT(newEllipse())); + newObjectMenu->addAction(newEllipseAction); + + QAction *newTextAction = new QAction(tr("Text"), this); + connect(newTextAction, SIGNAL(triggered(bool)), this, SLOT(newText())); + newObjectMenu->addAction(newTextAction); + + deleteAction = new QAction(tr("Delete Object"), this); + connect(deleteAction, SIGNAL(triggered(bool)), this, SLOT(deleteObject())); + editMenu->addAction(deleteAction); + + QAction *clearAction = new QAction(tr("Clear All"), this); + connect(clearAction, SIGNAL(triggered(bool)), this, SLOT(clearAll())); + editMenu->addAction(clearAction); + + QAction *fillAction = new QAction(tr("Fill View"), this); + connect(fillAction, SIGNAL(triggered(bool)), this, SLOT(fillView())); + editMenu->addAction(fillAction); + + variantManager = new QtVariantPropertyManager(this); + + connect(variantManager, SIGNAL(valueChanged(QtProperty *, const QVariant &)), + this, SLOT(valueChanged(QtProperty *, const QVariant &))); + + QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(this); + + canvas = new QtCanvas(800, 600); + canvasView = new CanvasView(canvas, this); + setCentralWidget(canvasView); + + QDockWidget *dock = new QDockWidget(this); + addDockWidget(Qt::RightDockWidgetArea, dock); + + propertyEditor = new QtTreePropertyBrowser(dock); + propertyEditor->setFactoryForManager(variantManager, variantFactory); + dock->setWidget(propertyEditor); + + currentItem = 0; + + connect(canvasView, SIGNAL(itemClicked(QtCanvasItem *)), + this, SLOT(itemClicked(QtCanvasItem *))); + connect(canvasView, SIGNAL(itemMoved(QtCanvasItem *)), + this, SLOT(itemMoved(QtCanvasItem *))); + + fillView(); + itemClicked(0); +} + +void MainWindow::newRectangle() +{ + QtCanvasItem *item = addRectangle(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newEllipse() +{ + QtCanvasItem *item = addEllipse(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newLine() +{ + QtCanvasItem *item = addLine(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::newText() +{ + QtCanvasItem *item = addText(); + canvas->update(); + itemClicked(item); +} + +void MainWindow::deleteObject() +{ + if (!currentItem) + return; + + delete currentItem; + itemClicked(0); + canvas->update(); +} + +void MainWindow::clearAll() +{ + QtCanvasItemList list = canvas->allItems(); + qDeleteAll(list); + itemClicked(0); + canvas->update(); +} + +void MainWindow::fillView() +{ + for (int i = 0; i < 10; i++) { + addRectangle(); + addEllipse(); + addLine(); + addText(); + } + canvas->update(); +} + +QtCanvasItem *MainWindow::addRectangle() +{ + QtCanvasPolygonalItem *item = new QtCanvasRectangle(rand() % canvas->width(), + rand() % canvas->height(), 50, 50, canvas); + int z = rand() % 256; + item->setBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + item->setPen(QPen(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8), 4)); + item->setZ(z); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addEllipse() +{ + QtCanvasPolygonalItem *item = new QtCanvasEllipse(50, 50, canvas); + item->setBrush(QColor(rand() % 32 * 8, rand() % 32 * 8, rand() % 32 * 8)); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setZ(rand() % 256); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addLine() +{ + QtCanvasLine *item = new QtCanvasLine(canvas); + item->setPoints(0, 0, rand() % canvas->width() - canvas->width() / 2, + rand() % canvas->height() - canvas->height() / 2); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setPen(QPen(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8), 6)); + item->setZ(rand() % 256); + item->show(); + return item; +} + +QtCanvasItem *MainWindow::addText() +{ + QtCanvasText *item = new QtCanvasText(canvas); + item->setText(tr("Text")); + item->setColor(QColor(rand() % 32*8, rand() % 32*8, rand() % 32*8)); + item->move(rand() % canvas->width(), rand() % canvas->height()); + item->setZ(rand() % 256); + item->show(); + return item; +} + +void MainWindow::itemMoved(QtCanvasItem *item) +{ + if (item != currentItem) + return; + + variantManager->setValue(idToProperty[QLatin1String("xpos")], item->x()); + variantManager->setValue(idToProperty[QLatin1String("ypos")], item->y()); + variantManager->setValue(idToProperty[QLatin1String("zpos")], item->z()); +} + +void MainWindow::updateExpandState() +{ + QList list = propertyEditor->topLevelItems(); + QListIterator it(list); + while (it.hasNext()) { + QtBrowserItem *item = it.next(); + QtProperty *prop = item->property(); + idToExpanded[propertyToId[prop]] = propertyEditor->isExpanded(item); + } +} + +void MainWindow::itemClicked(QtCanvasItem *item) +{ + updateExpandState(); + + QMap::ConstIterator itProp = propertyToId.constBegin(); + while (itProp != propertyToId.constEnd()) { + delete itProp.key(); + itProp++; + } + propertyToId.clear(); + idToProperty.clear(); + + currentItem = item; + if (!currentItem) { + deleteAction->setEnabled(false); + return; + } + + deleteAction->setEnabled(true); + + QtVariantProperty *property; + + property = variantManager->addProperty(QVariant::Double, tr("Position X")); + property->setAttribute(QLatin1String("minimum"), 0); + property->setAttribute(QLatin1String("maximum"), canvas->width()); + property->setValue(item->x()); + addProperty(property, QLatin1String("xpos")); + + property = variantManager->addProperty(QVariant::Double, tr("Position Y")); + property->setAttribute(QLatin1String("minimum"), 0); + property->setAttribute(QLatin1String("maximum"), canvas->height()); + property->setValue(item->y()); + addProperty(property, QLatin1String("ypos")); + + property = variantManager->addProperty(QVariant::Double, tr("Position Z")); + property->setAttribute(QLatin1String("minimum"), 0); + property->setAttribute(QLatin1String("maximum"), 256); + property->setValue(item->z()); + addProperty(property, QLatin1String("zpos")); + + if (item->rtti() == QtCanvasItem::Rtti_Rectangle) { + QtCanvasRectangle *i = (QtCanvasRectangle *)item; + + property = variantManager->addProperty(QVariant::Color, tr("Brush Color")); + property->setValue(i->brush().color()); + addProperty(property, QLatin1String("brush")); + + property = variantManager->addProperty(QVariant::Color, tr("Pen Color")); + property->setValue(i->pen().color()); + addProperty(property, QLatin1String("pen")); + + property = variantManager->addProperty(QVariant::Size, tr("Size")); + property->setValue(i->size()); + addProperty(property, QLatin1String("size")); + } else if (item->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasLine *i = (QtCanvasLine *)item; + + property = variantManager->addProperty(QVariant::Color, tr("Pen Color")); + property->setValue(i->pen().color()); + addProperty(property, QLatin1String("pen")); + + property = variantManager->addProperty(QVariant::Point, tr("Vector")); + property->setValue(i->endPoint()); + addProperty(property, QLatin1String("endpoint")); + } else if (item->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasEllipse *i = (QtCanvasEllipse *)item; + + property = variantManager->addProperty(QVariant::Color, tr("Brush Color")); + property->setValue(i->brush().color()); + addProperty(property, QLatin1String("brush")); + + property = variantManager->addProperty(QVariant::Size, tr("Size")); + property->setValue(QSize(i->width(), i->height())); + addProperty(property, QLatin1String("size")); + } else if (item->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)item; + + property = variantManager->addProperty(QVariant::Color, tr("Color")); + property->setValue(i->color()); + addProperty(property, QLatin1String("color")); + + property = variantManager->addProperty(QVariant::String, tr("Text")); + property->setValue(i->text()); + addProperty(property, QLatin1String("text")); + + property = variantManager->addProperty(QVariant::Font, tr("Font")); + property->setValue(i->font()); + addProperty(property, QLatin1String("font")); + } +} + +void MainWindow::addProperty(QtVariantProperty *property, const QString &id) +{ + propertyToId[property] = id; + idToProperty[id] = property; + QtBrowserItem *item = propertyEditor->addProperty(property); + if (idToExpanded.contains(id)) + propertyEditor->setExpanded(item, idToExpanded[id]); +} + +void MainWindow::valueChanged(QtProperty *property, const QVariant &value) +{ + if (!propertyToId.contains(property)) + return; + + if (!currentItem) + return; + + QString id = propertyToId[property]; + if (id == QLatin1String("xpos")) { + currentItem->setX(value.toDouble()); + } else if (id == QLatin1String("ypos")) { + currentItem->setY(value.toDouble()); + } else if (id == QLatin1String("zpos")) { + currentItem->setZ(value.toDouble()); + } else if (id == QLatin1String("text")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setText(value.value()); + } + } else if (id == QLatin1String("color")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setColor(value.value()); + } + } else if (id == QLatin1String("brush")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle || + currentItem->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasPolygonalItem *i = (QtCanvasPolygonalItem *)currentItem; + QBrush b = i->brush(); + b.setColor(value.value()); + i->setBrush(b); + } + } else if (id == QLatin1String("pen")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle || + currentItem->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasPolygonalItem *i = (QtCanvasPolygonalItem *)currentItem; + QPen p = i->pen(); + p.setColor(value.value()); + i->setPen(p); + } + } else if (id == QLatin1String("font")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Text) { + QtCanvasText *i = (QtCanvasText *)currentItem; + i->setFont(value.value()); + } + } else if (id == QLatin1String("endpoint")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Line) { + QtCanvasLine *i = (QtCanvasLine *)currentItem; + QPoint p = value.value(); + i->setPoints(i->startPoint().x(), i->startPoint().y(), p.x(), p.y()); + } + } else if (id == QLatin1String("size")) { + if (currentItem->rtti() == QtCanvasItem::Rtti_Rectangle) { + QtCanvasRectangle *i = (QtCanvasRectangle *)currentItem; + QSize s = value.value(); + i->setSize(s.width(), s.height()); + } else if (currentItem->rtti() == QtCanvasItem::Rtti_Ellipse) { + QtCanvasEllipse *i = (QtCanvasEllipse *)currentItem; + QSize s = value.value(); + i->setSize(s.width(), s.height()); + } + } + canvas->update(); +} + diff --git a/examples/canvas_variant/mainwindow.h b/examples/canvas_variant/mainwindow.h new file mode 100644 index 0000000..4d4112b --- /dev/null +++ b/examples/canvas_variant/mainwindow.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "qtcanvas.h" + +class QtVariantProperty; +class QtProperty; + +class QtBrowserIndex; + +class CanvasView : public QtCanvasView +{ + Q_OBJECT +public: + CanvasView(QWidget *parent = 0) + : QtCanvasView(parent), moving(0) { } + CanvasView(QtCanvas *canvas, QWidget *parent = 0) + : QtCanvasView(canvas, parent), moving(0) { } +signals: + void itemClicked(QtCanvasItem *item); + void itemMoved(QtCanvasItem *item); +protected: + void contentsMousePressEvent(QMouseEvent *event); + void contentsMouseDoubleClickEvent(QMouseEvent *event); + void contentsMouseMoveEvent(QMouseEvent* event); +private: + void handleMouseClickEvent(QMouseEvent *event); + QPoint moving_start; + QtCanvasItem *moving; +}; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget *parent = 0); + +private slots: + void newRectangle(); + void newEllipse(); + void newLine(); + void newText(); + void deleteObject(); + void clearAll(); + void fillView(); + + + void itemClicked(QtCanvasItem *item); + void itemMoved(QtCanvasItem *item); + void valueChanged(QtProperty *property, const QVariant &value); +private: + + QtCanvasItem *addRectangle(); + QtCanvasItem *addEllipse(); + QtCanvasItem *addLine(); + QtCanvasItem *addText(); + void addProperty(QtVariantProperty *property, const QString &id); + void updateExpandState(); + + QAction *deleteAction; + + class QtVariantPropertyManager *variantManager; + + class QtTreePropertyBrowser *propertyEditor; + CanvasView *canvasView; + QtCanvas *canvas; + QtCanvasItem *currentItem; + QMap propertyToId; + QMap idToProperty; + QMap idToExpanded; +}; + +#endif diff --git a/examples/canvas_variant/qtcanvas.cpp b/examples/canvas_variant/qtcanvas.cpp new file mode 100644 index 0000000..1e297c5 --- /dev/null +++ b/examples/canvas_variant/qtcanvas.cpp @@ -0,0 +1,5906 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include "qtcanvas.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace Qt; + +class QtCanvasData { +public: + QtCanvasData() + { + } + + QList viewList; + QSet itemDict; + QSet animDict; +}; + +class QtCanvasViewData { +public: + QtCanvasViewData() {} + QMatrix xform; + QMatrix ixform; + bool highQuality; +}; + +// clusterizer + +class QtCanvasClusterizer { +public: + QtCanvasClusterizer(int maxclusters); + ~QtCanvasClusterizer(); + + void add(int x, int y); // 1x1 rectangle (point) + void add(int x, int y, int w, int h); + void add(const QRect& rect); + + void clear(); + int clusters() const { return count; } + const QRect& operator[](int i) const; + +private: + QRect* cluster; + int count; + const int maxcl; +}; + +static +void include(QRect& r, const QRect& rect) +{ + if (rect.left() < r.left()) { + r.setLeft(rect.left()); + } + if (rect.right()>r.right()) { + r.setRight(rect.right()); + } + if (rect.top() < r.top()) { + r.setTop(rect.top()); + } + if (rect.bottom()>r.bottom()) { + r.setBottom(rect.bottom()); + } +} + +/* +A QtCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles +by a merging heuristic. +*/ +QtCanvasClusterizer::QtCanvasClusterizer(int maxclusters) : + cluster(new QRect[maxclusters]), + count(0), + maxcl(maxclusters) +{ } + +QtCanvasClusterizer::~QtCanvasClusterizer() +{ + delete [] cluster; +} + +void QtCanvasClusterizer::clear() +{ + count = 0; +} + +void QtCanvasClusterizer::add(int x, int y) +{ + add(QRect(x, y, 1, 1)); +} + +void QtCanvasClusterizer::add(int x, int y, int w, int h) +{ + add(QRect(x, y, w, h)); +} + +void QtCanvasClusterizer::add(const QRect& rect) +{ + QRect biggerrect(rect.x()-1, rect.y()-1, rect.width()+2, rect.height()+2); + + //assert(rect.width()>0 && rect.height()>0); + + int cursor; + + for (cursor = 0; cursor < count; cursor++) { + if (cluster[cursor].contains(rect)) { + // Wholly contained already. + return; + } + } + + int lowestcost = 9999999; + int cheapest = -1; + cursor = 0; + while(cursor < count) { + if (cluster[cursor].intersects(biggerrect)) { + QRect larger = cluster[cursor]; + include(larger, rect); + int cost = larger.width()*larger.height() - + cluster[cursor].width()*cluster[cursor].height(); + + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapest = cursor; + lowestcost = cost; + } + } + } + cursor++; + } + + if (cheapest>= 0) { + include(cluster[cheapest], rect); + return; + } + + if (count < maxcl) { + cluster[count++] = rect; + return; + } + + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + + lowestcost = 9999999; + cheapest = -1; + cursor = 0; + while(cursor < count) { + QRect larger = cluster[cursor]; + include(larger, rect); + int cost = larger.width()*larger.height() + - cluster[cursor].width()*cluster[cursor].height(); + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapest = cursor; + lowestcost = cost; + } + } + cursor++; + } + + // ### + // could make an heuristic guess as to whether we need to bother + // looking for a cheap merge. + + int cheapestmerge1 = -1; + int cheapestmerge2 = -1; + + int merge1 = 0; + while(merge1 < count) { + int merge2 = 0; + while(merge2 < count) { + if(merge1!= merge2) { + QRect larger = cluster[merge1]; + include(larger, cluster[merge2]); + int cost = larger.width()*larger.height() + - cluster[merge1].width()*cluster[merge1].height() + - cluster[merge2].width()*cluster[merge2].height(); + if (cost < lowestcost) { + bool bad = false; + for (int c = 0; c < count && !bad; c++) { + bad = cluster[c].intersects(larger) && c!= cursor; + } + if (!bad) { + cheapestmerge1 = merge1; + cheapestmerge2 = merge2; + lowestcost = cost; + } + } + } + merge2++; + } + merge1++; + } + + if (cheapestmerge1>= 0) { + include(cluster[cheapestmerge1], cluster[cheapestmerge2]); + cluster[cheapestmerge2] = cluster[count--]; + } else { + // if (!cheapest) debugRectangles(rect); + include(cluster[cheapest], rect); + } + + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x, y) are ordered topleft + // to bottomright. + + // ### + // + // add explicit x/y ordering to that comment, move it to the top + // and rephrase it as pre-/post-conditions. +} + +const QRect& QtCanvasClusterizer::operator[](int i) const +{ + return cluster[i]; +} + +// end of clusterizer + + +class QtCanvasItemLess +{ +public: + inline bool operator()(const QtCanvasItem *i1, const QtCanvasItem *i2) const + { + if (i1->z() == i2->z()) + return i1 > i2; + return (i1->z() > i2->z()); + } +}; + + +class QtCanvasChunk { +public: + QtCanvasChunk() : changed(true) { } + // Other code assumes lists are not deleted. Assignment is also + // done on ChunkRecs. So don't add that sort of thing here. + + void sort() + { + qSort(m_list.begin(), m_list.end(), QtCanvasItemLess()); + } + + const QtCanvasItemList &list() const + { + return m_list; + } + + void add(QtCanvasItem* item) + { + m_list.prepend(item); + changed = true; + } + + void remove(QtCanvasItem* item) + { + m_list.removeAll(item); + changed = true; + } + + void change() + { + changed = true; + } + + bool hasChanged() const + { + return changed; + } + + bool takeChange() + { + bool y = changed; + changed = false; + return y; + } + +private: + QtCanvasItemList m_list; + bool changed; +}; + + +static int gcd(int a, int b) +{ + int r; + while ((r = a%b)) { + a = b; + b = r; + } + return b; +} + +static int scm(int a, int b) +{ + int g = gcd(a, b); + return a/g*b; +} + + + +/* + \class QtCanvas qtcanvas.h + \brief The QtCanvas class provides a 2D area that can contain QtCanvasItem objects. + + The QtCanvas class manages its 2D graphic area and all the canvas + items the area contains. The canvas has no visual appearance of + its own. Instead, it is displayed on screen using a QtCanvasView. + Multiple QtCanvasView widgets may be associated with a canvas to + provide multiple views of the same canvas. + + The canvas is optimized for large numbers of items, particularly + where only a small percentage of the items change at any + one time. If the entire display changes very frequently, you should + consider using your own custom QtScrollView subclass. + + Qt provides a rich + set of canvas item classes, e.g. QtCanvasEllipse, QtCanvasLine, + QtCanvasPolygon, QtCanvasPolygonalItem, QtCanvasRectangle, QtCanvasSpline, + QtCanvasSprite and QtCanvasText. You can subclass to create your own + canvas items; QtCanvasPolygonalItem is the most common base class used + for this purpose. + + Items appear on the canvas after their \link QtCanvasItem::show() + show()\endlink function has been called (or \link + QtCanvasItem::setVisible() setVisible(true)\endlink), and \e after + update() has been called. The canvas only shows items that are + \link QtCanvasItem::setVisible() visible\endlink, and then only if + \l update() is called. (By default the canvas is white and so are + canvas items, so if nothing appears try changing colors.) + + If you created the canvas without passing a width and height to + the constructor you must also call resize(). + + Although a canvas may appear to be similar to a widget with child + widgets, there are several notable differences: + + \list + \i Canvas items are usually much faster to manipulate and redraw than + child widgets, with the speed advantage becoming especially great when + there are \e many canvas items and non-rectangular items. In most + situations canvas items are also a lot more memory efficient than child + widgets. + + \i It's easy to detect overlapping items (collision detection). + + \i The canvas can be larger than a widget. A million-by-million canvas + is perfectly possible. At such a size a widget might be very + inefficient, and some window systems might not support it at all, + whereas QtCanvas scales well. Even with a billion pixels and a million + items, finding a particular canvas item, detecting collisions, etc., + is still fast (though the memory consumption may be prohibitive + at such extremes). + + \i Two or more QtCanvasView objects can view the same canvas. + + \i An arbitrary transformation matrix can be set on each QtCanvasView + which makes it easy to zoom, rotate or shear the viewed canvas. + + \i Widgets provide a lot more functionality, such as input (QKeyEvent, + QMouseEvent etc.) and layout management (QGridLayout etc.). + + \endlist + + A canvas consists of a background, a number of canvas items organized by + x, y and z coordinates, and a foreground. A canvas item's z coordinate + can be treated as a layer number -- canvas items with a higher z + coordinate appear in front of canvas items with a lower z coordinate. + + The background is white by default, but can be set to a different color + using setBackgroundColor(), or to a repeated pixmap using + setBackgroundPixmap() or to a mosaic of smaller pixmaps using + setTiles(). Individual tiles can be set with setTile(). There + are corresponding get functions, e.g. backgroundColor() and + backgroundPixmap(). + + Note that QtCanvas does not inherit from QWidget, even though it has some + functions which provide the same functionality as those in QWidget. One + of these is setBackgroundPixmap(); some others are resize(), size(), + width() and height(). \l QtCanvasView is the widget used to display a + canvas on the screen. + + Canvas items are added to a canvas by constructing them and passing the + canvas to the canvas item's constructor. An item can be moved to a + different canvas using QtCanvasItem::setCanvas(). + + Canvas items are movable (and in the case of QtCanvasSprites, animated) + objects that inherit QtCanvasItem. Each canvas item has a position on the + canvas (x, y coordinates) and a height (z coordinate), all of which are + held as floating-point numbers. Moving canvas items also have x and y + velocities. It's possible for a canvas item to be outside the canvas + (for example QtCanvasItem::x() is greater than width()). When a canvas + item is off the canvas, onCanvas() returns false and the canvas + disregards the item. (Canvas items off the canvas do not slow down any + of the common operations on the canvas.) + + Canvas items can be moved with QtCanvasItem::move(). The advance() + function moves all QtCanvasItem::animated() canvas items and + setAdvancePeriod() makes QtCanvas move them automatically on a periodic + basis. In the context of the QtCanvas classes, to `animate' a canvas item + is to set it in motion, i.e. using QtCanvasItem::setVelocity(). Animation + of a canvas item itself, i.e. items which change over time, is enabled + by calling QtCanvasSprite::setFrameAnimation(), or more generally by + subclassing and reimplementing QtCanvasItem::advance(). To detect collisions + use one of the QtCanvasItem::collisions() functions. + + The changed parts of the canvas are redrawn (if they are visible in a + canvas view) whenever update() is called. You can either call update() + manually after having changed the contents of the canvas, or force + periodic updates using setUpdatePeriod(). If you have moving objects on + the canvas, you must call advance() every time the objects should + move one step further. Periodic calls to advance() can be forced using + setAdvancePeriod(). The advance() function will call + QtCanvasItem::advance() on every item that is \link + QtCanvasItem::animated() animated\endlink and trigger an update of the + affected areas afterwards. (A canvas item that is `animated' is simply + a canvas item that is in motion.) + + QtCanvas organizes its canvas items into \e chunks; these are areas on + the canvas that are used to speed up most operations. Many operations + start by eliminating most chunks (i.e. those which haven't changed) + and then process only the canvas items that are in the few interesting + (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on + the canvas. + + The chunk size is a key factor to QtCanvas's speed: if there are too many + chunks, the speed benefit of grouping canvas items into chunks is + reduced. If the chunks are too large, it takes too long to process each + one. The QtCanvas constructor tries to pick a suitable size, but you + can call retune() to change it at any time. The chunkSize() function + returns the current chunk size. The canvas items always make sure + they're in the right chunks; all you need to make sure of is that + the canvas uses the right chunk size. A good rule of thumb is that + the size should be a bit smaller than the average canvas item + size. If you have moving objects, the chunk size should be a bit + smaller than the average size of the moving items. + + The foreground is normally nothing, but if you reimplement + drawForeground(), you can draw things in front of all the canvas + items. + + Areas can be set as changed with setChanged() and set unchanged with + setUnchanged(). The entire canvas can be set as changed with + setAllChanged(). A list of all the items on the canvas is returned by + allItems(). + + An area can be copied (painted) to a QPainter with drawArea(). + + If the canvas is resized it emits the resized() signal. + + The examples/canvas application and the 2D graphics page of the + examples/demo application demonstrate many of QtCanvas's facilities. + + \sa QtCanvasView QtCanvasItem +*/ +void QtCanvas::init(int w, int h, int chunksze, int mxclusters) +{ + d = new QtCanvasData; + awidth = w; + aheight = h; + chunksize = chunksze; + maxclusters = mxclusters; + chwidth = (w+chunksize-1)/chunksize; + chheight = (h+chunksize-1)/chunksize; + chunks = new QtCanvasChunk[chwidth*chheight]; + update_timer = 0; + bgcolor = white; + grid = 0; + htiles = 0; + vtiles = 0; + debug_redraw_areas = false; +} + +/* + Create a QtCanvas with no size. \a parent is passed to the QObject + superclass. + + \warning You \e must call resize() at some time after creation to + be able to use the canvas. +*/ +QtCanvas::QtCanvas(QObject* parent) + : QObject(parent) +{ + init(0, 0); +} + +/* + Constructs a QtCanvas that is \a w pixels wide and \a h pixels high. +*/ +QtCanvas::QtCanvas(int w, int h) +{ + init(w, h); +} + +/* + Constructs a QtCanvas which will be composed of \a h tiles + horizontally and \a v tiles vertically. Each tile will be an image + \a tilewidth by \a tileheight pixels taken from pixmap \a p. + + The pixmap \a p is a list of tiles, arranged left to right, (and + in the case of pixmaps that have multiple rows of tiles, top to + bottom), with tile 0 in the top-left corner, tile 1 next to the + right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + The QtCanvas is initially sized to show exactly the given number of + tiles horizontally and vertically. If it is resized to be larger, + the entire matrix of tiles will be repeated as often as necessary + to cover the area. If it is smaller, tiles to the right and bottom + will not be visible. + + \sa setTiles() +*/ +QtCanvas::QtCanvas(QPixmap p, + int h, int v, int tilewidth, int tileheight) +{ + init(h*tilewidth, v*tileheight, scm(tilewidth, tileheight)); + setTiles(p, h, v, tilewidth, tileheight); +} + +/* + Destroys the canvas and all the canvas's canvas items. +*/ +QtCanvas::~QtCanvas() +{ + for (int i = 0; i < d->viewList.size(); ++i) + d->viewList[i]->viewing = 0; + QtCanvasItemList all = allItems(); + for (QtCanvasItemList::Iterator it = all.begin(); it!= all.end(); ++it) + delete *it; + delete [] chunks; + delete [] grid; + delete d; +} + +/* +\internal +Returns the chunk at a chunk position \a i, \a j. +*/ +QtCanvasChunk& QtCanvas::chunk(int i, int j) const +{ + return chunks[i+chwidth*j]; +} + +/* +\internal +Returns the chunk at a pixel position \a x, \a y. +*/ +QtCanvasChunk& QtCanvas::chunkContaining(int x, int y) const +{ + return chunk(x/chunksize, y/chunksize); +} + +/* + Returns a list of all the items in the canvas. +*/ +QtCanvasItemList QtCanvas::allItems() +{ + return d->itemDict.toList(); +} + + +/* + Changes the size of the canvas to have a width of \a w and a + height of \a h. This is a slow operation. +*/ +void QtCanvas::resize(int w, int h) +{ + if (awidth == w && aheight == h) + return; + + QList hidden; + for (QSet::const_iterator it = d->itemDict.begin(); it != d->itemDict.end(); ++it) { + if ((*it)->isVisible()) { + (*it)->hide(); + hidden.append(*it); + } + } + + int nchwidth = (w+chunksize-1)/chunksize; + int nchheight = (h+chunksize-1)/chunksize; + + QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth*nchheight]; + + // Commit the new values. + // + awidth = w; + aheight = h; + chwidth = nchwidth; + chheight = nchheight; + delete [] chunks; + chunks = newchunks; + + for (int i = 0; i < hidden.size(); ++i) + hidden.at(i)->show(); + + setAllChanged(); + + emit resized(); +} + +/* + \fn void QtCanvas::resized() + + This signal is emitted whenever the canvas is resized. Each + QtCanvasView connects to this signal to keep the scrollview's size + correct. +*/ + +/* + Change the efficiency tuning parameters to \a mxclusters clusters, + each of size \a chunksze. This is a slow operation if there are + many objects on the canvas. + + The canvas is divided into chunks which are rectangular areas \a + chunksze wide by \a chunksze high. Use a chunk size which is about + the average size of the canvas items. If you choose a chunk size + which is too small it will increase the amount of calculation + required when drawing since each change will affect many chunks. + If you choose a chunk size which is too large the amount of + drawing required will increase because for each change, a lot of + drawing will be required since there will be many (unchanged) + canvas items which are in the same chunk as the changed canvas + items. + + Internally, a canvas uses a low-resolution "chunk matrix" to keep + track of all the items in the canvas. A 64x64 chunk matrix is the + default for a 1024x1024 pixel canvas, where each chunk collects + canvas items in a 16x16 pixel square. This default is also + affected by setTiles(). You can tune this default using this + function. For example if you have a very large canvas and want to + trade off speed for memory then you might set the chunk size to 32 + or 64. + + The \a mxclusters argument is the number of rectangular groups of + chunks that will be separately drawn. If the canvas has a large + number of small, dispersed items, this should be about that + number. Our testing suggests that a large number of clusters is + almost always best. + +*/ +void QtCanvas::retune(int chunksze, int mxclusters) +{ + maxclusters = mxclusters; + + if (chunksize!= chunksze) { + QList hidden; + for (QSet::const_iterator it = d->itemDict.begin(); it != d->itemDict.end(); ++it) { + if ((*it)->isVisible()) { + (*it)->hide(); + hidden.append(*it); + } + } + + chunksize = chunksze; + + int nchwidth = (awidth+chunksize-1)/chunksize; + int nchheight = (aheight+chunksize-1)/chunksize; + + QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth*nchheight]; + + // Commit the new values. + // + chwidth = nchwidth; + chheight = nchheight; + delete [] chunks; + chunks = newchunks; + + for (int i = 0; i < hidden.size(); ++i) + hidden.at(i)->show(); + } +} + +/* + \fn int QtCanvas::width() const + + Returns the width of the canvas, in pixels. +*/ + +/* + \fn int QtCanvas::height() const + + Returns the height of the canvas, in pixels. +*/ + +/* + \fn QSize QtCanvas::size() const + + Returns the size of the canvas, in pixels. +*/ + +/* + \fn QRect QtCanvas::rect() const + + Returns a rectangle the size of the canvas. +*/ + + +/* + \fn bool QtCanvas::onCanvas(int x, int y) const + + Returns true if the pixel position (\a x, \a y) is on the canvas; + otherwise returns false. + + \sa validChunk() +*/ + +/* + \fn bool QtCanvas::onCanvas(const QPoint& p) const + \overload + + Returns true if the pixel position \a p is on the canvas; + otherwise returns false. + + \sa validChunk() +*/ + +/* + \fn bool QtCanvas::validChunk(int x, int y) const + + Returns true if the chunk position (\a x, \a y) is on the canvas; + otherwise returns false. + + \sa onCanvas() +*/ + +/* + \fn bool QtCanvas::validChunk(const QPoint& p) const + \overload + + Returns true if the chunk position \a p is on the canvas; otherwise + returns false. + + \sa onCanvas() +*/ + +/* + \fn int QtCanvas::chunkSize() const + + Returns the chunk size of the canvas. + + \sa retune() +*/ + +/* +\fn bool QtCanvas::sameChunk(int x1, int y1, int x2, int y2) const +\internal +Tells if the points (\a x1, \a y1) and (\a x2, \a y2) are within the same chunk. +*/ + +/* +\internal +This method adds an the item \a item to the list of QtCanvasItem objects +in the QtCanvas. The QtCanvasItem class calls this. +*/ +void QtCanvas::addItem(QtCanvasItem* item) +{ + d->itemDict.insert(item); +} + +/* +\internal +This method adds the item \a item to the list of QtCanvasItem objects +to be moved. The QtCanvasItem class calls this. +*/ +void QtCanvas::addAnimation(QtCanvasItem* item) +{ + d->animDict.insert(item); +} + +/* +\internal +This method adds the item \a item to the list of QtCanvasItem objects +which are no longer to be moved. The QtCanvasItem class calls this. +*/ +void QtCanvas::removeAnimation(QtCanvasItem* item) +{ + d->animDict.remove(item); +} + +/* +\internal +This method removes the item \a item from the list of QtCanvasItem objects +in this QtCanvas. The QtCanvasItem class calls this. +*/ +void QtCanvas::removeItem(QtCanvasItem* item) +{ + d->itemDict.remove(item); +} + +/* +\internal +This method adds the view \a view to the list of QtCanvasView objects +viewing this QtCanvas. The QtCanvasView class calls this. +*/ +void QtCanvas::addView(QtCanvasView* view) +{ + d->viewList.append(view); + if (htiles>1 || vtiles>1 || pm.isNull()) { + QPalette::ColorRole role = view->widget()->backgroundRole(); + QPalette viewPalette = view->widget()->palette(); + viewPalette.setColor(role, backgroundColor()); + view->widget()->setPalette(viewPalette); + } +} + +/* +\internal +This method removes the view \a view from the list of QtCanvasView objects +viewing this QtCanvas. The QtCanvasView class calls this. +*/ +void QtCanvas::removeView(QtCanvasView* view) +{ + d->viewList.removeAll(view); +} + +/* + Sets the canvas to call advance() every \a ms milliseconds. Any + previous setting by setAdvancePeriod() or setUpdatePeriod() is + overridden. + + If \a ms is less than 0 advancing will be stopped. +*/ +void QtCanvas::setAdvancePeriod(int ms) +{ + if (ms < 0) { + if (update_timer) + update_timer->stop(); + } else { + if (update_timer) + delete update_timer; + update_timer = new QTimer(this); + connect(update_timer, SIGNAL(timeout()), this, SLOT(advance())); + update_timer->start(ms); + } +} + +/* + Sets the canvas to call update() every \a ms milliseconds. Any + previous setting by setAdvancePeriod() or setUpdatePeriod() is + overridden. + + If \a ms is less than 0 automatic updating will be stopped. +*/ +void QtCanvas::setUpdatePeriod(int ms) +{ + if (ms < 0) { + if (update_timer) + update_timer->stop(); + } else { + if (update_timer) + delete update_timer; + update_timer = new QTimer(this); + connect(update_timer, SIGNAL(timeout()), this, SLOT(update())); + update_timer->start(ms); + } +} + +/* + Moves all QtCanvasItem::animated() canvas items on the canvas and + refreshes all changes to all views of the canvas. (An `animated' + item is an item that is in motion; see setVelocity().) + + The advance takes place in two phases. In phase 0, the + QtCanvasItem::advance() function of each QtCanvasItem::animated() + canvas item is called with paramater 0. Then all these canvas + items are called again, with parameter 1. In phase 0, the canvas + items should not change position, merely examine other items on + the canvas for which special processing is required, such as + collisions between items. In phase 1, all canvas items should + change positions, ignoring any other items on the canvas. This + two-phase approach allows for considerations of "fairness", + although no QtCanvasItem subclasses supplied with Qt do anything + interesting in phase 0. + + The canvas can be configured to call this function periodically + with setAdvancePeriod(). + + \sa update() +*/ +void QtCanvas::advance() +{ + QSetIterator it = d->animDict; + while (it.hasNext()) { + QtCanvasItem *i = it.next(); + if (i) + i->advance(0); + } + // we expect the dict contains the exact same items as in the + // first pass. + it.toFront(); + while (it.hasNext()) { + QtCanvasItem* i = it.next(); + if (i) + i->advance(1); + } + update(); +} + +// Don't call this unless you know what you're doing. +// p is in the content's co-ordinate example. +/* + \internal +*/ +void QtCanvas::drawViewArea(QtCanvasView* view, QPainter* p, const QRect& vr, bool) +{ + QMatrix wm = view->worldMatrix(); + QMatrix iwm = wm.inverted(); + // ivr = covers all chunks in vr + QRect ivr = iwm.mapRect(vr); + + p->setMatrix(wm); + drawCanvasArea(ivr, p, false); +} + +/* + Repaints changed areas in all views of the canvas. + + \sa advance() +*/ +void QtCanvas::update() +{ + QRect r = changeBounds(); + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView* view = d->viewList.at(i); + if (!r.isEmpty()) { + QRect tr = view->worldMatrix().mapRect(r); + view->widget()->update(tr); + } + } + setUnchanged(r); +} + + +/* + Marks the whole canvas as changed. + All views of the canvas will be entirely redrawn when + update() is called next. +*/ +void QtCanvas::setAllChanged() +{ + setChanged(QRect(0, 0, width(), height())); +} + +/* + Marks \a area as changed. This \a area will be redrawn in all + views that are showing it when update() is called next. +*/ +void QtCanvas::setChanged(const QRect& area) +{ + QRect thearea = area.intersected(QRect(0, 0, width(), height())); + + int mx = (thearea.x()+thearea.width()+chunksize)/chunksize; + int my = (thearea.y()+thearea.height()+chunksize)/chunksize; + if (mx>chwidth) + mx = chwidth; + if (my>chheight) + my = chheight; + + int x = thearea.x()/chunksize; + while(x < mx) { + int y = thearea.y()/chunksize; + while(y < my) { + chunk(x, y).change(); + y++; + } + x++; + } +} + +/* + Marks \a area as \e unchanged. The area will \e not be redrawn in + the views for the next update(), unless it is marked or changed + again before the next call to update(). +*/ +void QtCanvas::setUnchanged(const QRect& area) +{ + QRect thearea = area.intersected(QRect(0, 0, width(), height())); + + int mx = (thearea.x()+thearea.width()+chunksize)/chunksize; + int my = (thearea.y()+thearea.height()+chunksize)/chunksize; + if (mx>chwidth) + mx = chwidth; + if (my>chheight) + my = chheight; + + int x = thearea.x()/chunksize; + while(x < mx) { + int y = thearea.y()/chunksize; + while(y < my) { + chunk(x, y).takeChange(); + y++; + } + x++; + } +} + + +/* + \internal +*/ +QRect QtCanvas::changeBounds() +{ + QRect area = QRect(0, 0, width(), height()); + + int mx = (area.x()+area.width()+chunksize)/chunksize; + int my = (area.y()+area.height()+chunksize)/chunksize; + if (mx > chwidth) + mx = chwidth; + if (my > chheight) + my = chheight; + + QRect result; + + int x = area.x()/chunksize; + while(x < mx) { + int y = area.y()/chunksize; + while(y < my) { + QtCanvasChunk& ch = chunk(x, y); + if (ch.hasChanged()) + result |= QRect(x*chunksize, y*chunksize, chunksize + 1, chunksize + 1); + y++; + } + x++; + } + + return result; +} + +/* + Paints all canvas items that are in the area \a clip to \a + painter, using double-buffering if \a dbuf is true. + + e.g. to print the canvas to a printer: + \code + QPrinter pr; + if (pr.setup()) { + QPainter p(&pr); + canvas.drawArea(canvas.rect(), &p); + } + \endcode +*/ +void QtCanvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf) +{ + if (painter) + drawCanvasArea(clip, painter, dbuf); +} + +#include +/* + \internal +*/ +void QtCanvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool /*double_buffer*/) +{ + QRect area = inarea.intersected(QRect(0, 0, width(), height())); + + if (!p) return; // Nothing to do. + + int lx = area.x()/chunksize; + int ly = area.y()/chunksize; + int mx = area.right()/chunksize; + int my = area.bottom()/chunksize; + if (mx>= chwidth) + mx = chwidth-1; + if (my>= chheight) + my = chheight-1; + + QtCanvasItemList allvisible; + + // Stores the region within area that need to be drawn. It is relative + // to area.topLeft() (so as to keep within bounds of 16-bit XRegions) + QRegion rgn; + + for (int x = lx; x <= mx; x++) { + for (int y = ly; y <= my; y++) { + // Only reset change if all views updating, and + // wholy within area. (conservative: ignore entire boundary) + // + // Disable this to help debugging. + // + if (!p) { + if (chunk(x, y).takeChange()) { + // ### should at least make bands + rgn |= QRegion(x*chunksize-area.x(), y*chunksize-area.y(), + chunksize, chunksize); + allvisible += chunk(x, y).list(); + } + } else { + allvisible += chunk(x, y).list(); + } + } + } + qSort(allvisible.begin(), allvisible.end(), QtCanvasItemLess()); + + drawBackground(*p, area); + if (!allvisible.isEmpty()) { + QtCanvasItem* prev = 0; + for (int i = allvisible.size() - 1; i >= 0; --i) { + QtCanvasItem *g = allvisible[i]; + if (g != prev) { + g->draw(*p); + prev = g; + } + } + } + + drawForeground(*p, area); +} + +/* +\internal +This method to informs the QtCanvas that a given chunk is +`dirty' and needs to be redrawn in the next Update. + +(\a x, \a y) is a chunk location. + +The sprite classes call this. Any new derived class of QtCanvasItem +must do so too. SetChangedChunkContaining can be used instead. +*/ +void QtCanvas::setChangedChunk(int x, int y) +{ + if (validChunk(x, y)) { + QtCanvasChunk& ch = chunk(x, y); + ch.change(); + } +} + +/* +\internal +This method to informs the QtCanvas that the chunk containing a given +pixel is `dirty' and needs to be redrawn in the next Update. + +(\a x, \a y) is a pixel location. + +The item classes call this. Any new derived class of QtCanvasItem must +do so too. SetChangedChunk can be used instead. +*/ +void QtCanvas::setChangedChunkContaining(int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + QtCanvasChunk& chunk = chunkContaining(x, y); + chunk.change(); + } +} + +/* +\internal +This method adds the QtCanvasItem \a g to the list of those which need to be +drawn if the given chunk at location (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the +chunk as `dirty'. +*/ +void QtCanvas::addItemToChunk(QtCanvasItem* g, int x, int y) +{ + if (validChunk(x, y)) { + chunk(x, y).add(g); + } +} + +/* +\internal +This method removes the QtCanvasItem \a g from the list of those which need to +be drawn if the given chunk at location (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the chunk +as `dirty'. +*/ +void QtCanvas::removeItemFromChunk(QtCanvasItem* g, int x, int y) +{ + if (validChunk(x, y)) { + chunk(x, y).remove(g); + } +} + + +/* +\internal +This method adds the QtCanvasItem \a g to the list of those which need to be +drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. Like +SetChangedChunk and SetChangedChunkContaining, this method marks the +chunk as `dirty'. +*/ +void QtCanvas::addItemToChunkContaining(QtCanvasItem* g, int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + chunkContaining(x, y).add(g); + } +} + +/* +\internal +This method removes the QtCanvasItem \a g from the list of those which need to +be drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. +Like SetChangedChunk and SetChangedChunkContaining, this method +marks the chunk as `dirty'. +*/ +void QtCanvas::removeItemFromChunkContaining(QtCanvasItem* g, int x, int y) +{ + if (x>= 0 && x < width() && y>= 0 && y < height()) { + chunkContaining(x, y).remove(g); + } +} + +/* + Returns the color set by setBackgroundColor(). By default, this is + white. + + This function is not a reimplementation of + QWidget::backgroundColor() (QtCanvas is not a subclass of QWidget), + but all QtCanvasViews that are viewing the canvas will set their + backgrounds to this color. + + \sa setBackgroundColor(), backgroundPixmap() +*/ +QColor QtCanvas::backgroundColor() const +{ + return bgcolor; +} + +/* + Sets the solid background to be the color \a c. + + \sa backgroundColor(), setBackgroundPixmap(), setTiles() +*/ +void QtCanvas::setBackgroundColor(const QColor& c) +{ + if (bgcolor != c) { + bgcolor = c; + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView *view = d->viewList.at(i); + QPalette::ColorRole role = view->widget()->backgroundRole(); + QPalette viewPalette = view->widget()->palette(); + viewPalette.setColor(role, bgcolor); + view->widget()->setPalette(viewPalette); + } + setAllChanged(); + } +} + +/* + Returns the pixmap set by setBackgroundPixmap(). By default, + this is a null pixmap. + + \sa setBackgroundPixmap(), backgroundColor() +*/ +QPixmap QtCanvas::backgroundPixmap() const +{ + return pm; +} + +/* + Sets the solid background to be the pixmap \a p repeated as + necessary to cover the entire canvas. + + \sa backgroundPixmap(), setBackgroundColor(), setTiles() +*/ +void QtCanvas::setBackgroundPixmap(const QPixmap& p) +{ + setTiles(p, 1, 1, p.width(), p.height()); + for (int i = 0; i < d->viewList.size(); ++i) { + QtCanvasView* view = d->viewList.at(i); + view->widget()->update(); + } +} + +/* + This virtual function is called for all updates of the canvas. It + renders any background graphics using the painter \a painter, in + the area \a clip. If the canvas has a background pixmap or a tiled + background, that graphic is used, otherwise the canvas is cleared + using the background color. + + If the graphics for an area change, you must explicitly call + setChanged(const QRect&) for the result to be visible when + update() is next called. + + \sa setBackgroundColor(), setBackgroundPixmap(), setTiles() +*/ +void QtCanvas::drawBackground(QPainter& painter, const QRect& clip) +{ + if (pm.isNull()) { + painter.fillRect(clip, bgcolor); + } else if (!grid) { + for (int x = clip.x()/pm.width(); + x < (clip.x()+clip.width()+pm.width()-1)/pm.width(); x++) + { + for (int y = clip.y()/pm.height(); + y < (clip.y()+clip.height()+pm.height()-1)/pm.height(); y++) + { + painter.drawPixmap(x*pm.width(), y*pm.height(), pm); + } + } + } else { + const int x1 = clip.left()/tilew; + int x2 = clip.right()/tilew; + const int y1 = clip.top()/tileh; + int y2 = clip.bottom()/tileh; + + const int roww = pm.width()/tilew; + + for (int j = y1; j <= y2; j++) { + int jj = j%tilesVertically(); + for (int i = x1; i <= x2; i++) { + int t = tile(i%tilesHorizontally(), jj); + int tx = t % roww; + int ty = t / roww; + painter.drawPixmap(i*tilew, j*tileh, pm, + tx*tilew, ty*tileh, tilew, tileh); + } + } + } +} + +/* + This virtual function is called for all updates of the canvas. It + renders any foreground graphics using the painter \a painter, in + the area \a clip. + + If the graphics for an area change, you must explicitly call + setChanged(const QRect&) for the result to be visible when + update() is next called. + + The default is to draw nothing. +*/ +void QtCanvas::drawForeground(QPainter& painter, const QRect& clip) +{ + if (debug_redraw_areas) { + painter.setPen(red); + painter.setBrush(NoBrush); + painter.drawRect(clip); + } +} + +/* + Sets the QtCanvas to be composed of \a h tiles horizontally and \a + v tiles vertically. Each tile will be an image \a tilewidth by \a + tileheight pixels from pixmap \a p. + + The pixmap \a p is a list of tiles, arranged left to right, (and + in the case of pixmaps that have multiple rows of tiles, top to + bottom), with tile 0 in the top-left corner, tile 1 next to the + right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + If the canvas is larger than the matrix of tiles, the entire + matrix is repeated as necessary to cover the whole canvas. If it + is smaller, tiles to the right and bottom are not visible. + + The width and height of \a p must be a multiple of \a tilewidth + and \a tileheight. If they are not the function will do nothing. + + If you want to unset any tiling set, then just pass in a null + pixmap and 0 for \a h, \a v, \a tilewidth, and + \a tileheight. +*/ +void QtCanvas::setTiles(QPixmap p, + int h, int v, int tilewidth, int tileheight) +{ + if (!p.isNull() && (!tilewidth || !tileheight || + p.width() % tilewidth != 0 || p.height() % tileheight != 0)) + return; + + htiles = h; + vtiles = v; + delete[] grid; + pm = p; + if (h && v && !p.isNull()) { + grid = new ushort[h*v]; + memset(grid, 0, h*v*sizeof(ushort)); + tilew = tilewidth; + tileh = tileheight; + } else { + grid = 0; + } + if (h + v > 10) { + int s = scm(tilewidth, tileheight); + retune(s < 128 ? s : qMax(tilewidth, tileheight)); + } + setAllChanged(); +} + +/* + \fn int QtCanvas::tile(int x, int y) const + + Returns the tile at position (\a x, \a y). Initially, all tiles + are 0. + + The parameters must be within range, i.e. + 0 \< \a x \< tilesHorizontally() and + 0 \< \a y \< tilesVertically(). + + \sa setTile() +*/ + +/* + \fn int QtCanvas::tilesHorizontally() const + + Returns the number of tiles horizontally. +*/ + +/* + \fn int QtCanvas::tilesVertically() const + + Returns the number of tiles vertically. +*/ + +/* + \fn int QtCanvas::tileWidth() const + + Returns the width of each tile. +*/ + +/* + \fn int QtCanvas::tileHeight() const + + Returns the height of each tile. +*/ + + +/* + Sets the tile at (\a x, \a y) to use tile number \a tilenum, which + is an index into the tile pixmaps. The canvas will update + appropriately when update() is next called. + + The images are taken from the pixmap set by setTiles() and are + arranged left to right, (and in the case of pixmaps that have + multiple rows of tiles, top to bottom), with tile 0 in the + top-left corner, tile 1 next to the right, and so on, e.g. + + \table + \row \i 0 \i 1 \i 2 \i 3 + \row \i 4 \i 5 \i 6 \i 7 + \endtable + + \sa tile() setTiles() +*/ +void QtCanvas::setTile(int x, int y, int tilenum) +{ + ushort& t = grid[x+y*htiles]; + if (t != tilenum) { + t = tilenum; + if (tilew == tileh && tilew == chunksize) + setChangedChunk(x, y); // common case + else + setChanged(QRect(x*tilew, y*tileh, tilew, tileh)); + } +} + + +// lesser-used data in canvas item, plus room for extension. +// Be careful adding to this - check all usages. +class QtCanvasItemExtra { + QtCanvasItemExtra() : vx(0.0), vy(0.0) { } + double vx, vy; + friend class QtCanvasItem; +}; + + +/* + \class QtCanvasItem qtcanvas.h + \brief The QtCanvasItem class provides an abstract graphic object on a QtCanvas. + + A variety of QtCanvasItem subclasses provide immediately usable + behaviour. This class is a pure abstract superclass providing the + behaviour that is shared among all the concrete canvas item classes. + QtCanvasItem is not intended for direct subclassing. It is much easier + to subclass one of its subclasses, e.g. QtCanvasPolygonalItem (the + commonest base class), QtCanvasRectangle, QtCanvasSprite, QtCanvasEllipse + or QtCanvasText. + + Canvas items are added to a canvas by constructing them and passing the + canvas to the canvas item's constructor. An item can be moved to a + different canvas using setCanvas(). + + Items appear on the canvas after their \link show() show()\endlink + function has been called (or \link setVisible() + setVisible(true)\endlink), and \e after update() has been called. The + canvas only shows items that are \link setVisible() visible\endlink, + and then only if \l update() is called. If you created the canvas + without passing a width and height to the constructor you'll also need + to call \link QtCanvas::resize() resize()\endlink. Since the canvas + background defaults to white and canvas items default to white, + you may need to change colors to see your items. + + A QtCanvasItem object can be moved in the x(), y() and z() dimensions + using functions such as move(), moveBy(), setX(), setY() and setZ(). A + canvas item can be set in motion, `animated', using setAnimated() and + given a velocity in the x and y directions with setXVelocity() and + setYVelocity() -- the same effect can be achieved by calling + setVelocity(). Use the collidesWith() function to see if the canvas item + will collide on the \e next advance(1) and use collisions() to see what + collisions have occurred. + + Use QtCanvasSprite or your own subclass of QtCanvasSprite to create canvas + items which are animated, i.e. which change over time. + + The size of a canvas item is given by boundingRect(). Use + boundingRectAdvanced() to see what the size of the canvas item will be + \e after the next advance(1) call. + + The rtti() function is used for identifying subclasses of QtCanvasItem. + The canvas() function returns a pointer to the canvas which contains the + canvas item. + + QtCanvasItem provides the show() and isVisible() functions like those in + QWidget. + + QtCanvasItem also provides the setEnabled(), setActive() and + setSelected() functions; these functions set the relevant boolean and + cause a repaint but the boolean values they set are not used in + QtCanvasItem itself. You can make use of these booleans in your subclasses. + + By default, canvas items have no velocity, no size, and are not in + motion. The subclasses provided in Qt do not change these defaults + except where noted. + +*/ + +/* + \enum QtCanvasItem::RttiValues + + This enum is used to name the different types of canvas item. + + \value Rtti_Item Canvas item abstract base class + \value Rtti_Ellipse + \value Rtti_Line + \value Rtti_Polygon + \value Rtti_PolygonalItem + \value Rtti_Rectangle + \value Rtti_Spline + \value Rtti_Sprite + \value Rtti_Text + +*/ + +/* + \fn void QtCanvasItem::update() + + Call this function to repaint the canvas's changed chunks. +*/ + +/* + Constructs a QtCanvasItem on canvas \a canvas. + + \sa setCanvas() +*/ +QtCanvasItem::QtCanvasItem(QtCanvas* canvas) : + cnv(canvas), + myx(0), myy(0), myz(0) +{ + ani = 0; + vis = 0; + val = 0; + sel = 0; + ena = 0; + act = 0; + + ext = 0; + if (cnv) cnv->addItem(this); +} + +/* + Destroys the QtCanvasItem and removes it from its canvas. +*/ +QtCanvasItem::~QtCanvasItem() +{ + if (cnv) { + cnv->removeItem(this); + cnv->removeAnimation(this); + } + delete ext; +} + +QtCanvasItemExtra& QtCanvasItem::extra() +{ + if (!ext) + ext = new QtCanvasItemExtra; + return *ext; +} + +/* + \fn double QtCanvasItem::x() const + + Returns the horizontal position of the canvas item. Note that + subclasses often have an origin other than the top-left corner. +*/ + +/* + \fn double QtCanvasItem::y() const + + Returns the vertical position of the canvas item. Note that + subclasses often have an origin other than the top-left corner. +*/ + +/* + \fn double QtCanvasItem::z() const + + Returns the z index of the canvas item, which is used for visual + order: higher-z items obscure (are in front of) lower-z items. +*/ + +/* + \fn void QtCanvasItem::setX(double x) + + Moves the canvas item so that its x-position is \a x. + + \sa x(), move() +*/ + +/* + \fn void QtCanvasItem::setY(double y) + + Moves the canvas item so that its y-position is \a y. + + \sa y(), move() +*/ + +/* + \fn void QtCanvasItem::setZ(double z) + + Sets the z index of the canvas item to \a z. Higher-z items + obscure (are in front of) lower-z items. + + \sa z(), move() +*/ + + +/* + Moves the canvas item relative to its current position by (\a dx, + \a dy). +*/ +void QtCanvasItem::moveBy(double dx, double dy) +{ + if (dx || dy) { + removeFromChunks(); + myx += dx; + myy += dy; + addToChunks(); + } +} + + +/* + Moves the canvas item to the absolute position (\a x, \a y). +*/ +void QtCanvasItem::move(double x, double y) +{ + moveBy(x-myx, y-myy); +} + + +/* + Returns true if the canvas item is in motion; otherwise returns + false. + + \sa setVelocity(), setAnimated() +*/ +bool QtCanvasItem::animated() const +{ + return (bool)ani; +} + +/* + Sets the canvas item to be in motion if \a y is true, or not if \a + y is false. The speed and direction of the motion is set with + setVelocity(), or with setXVelocity() and setYVelocity(). + + \sa advance(), QtCanvas::advance() +*/ +void QtCanvasItem::setAnimated(bool y) +{ + if (y != (bool)ani) { + ani = (uint)y; + if (y) { + cnv->addAnimation(this); + } else { + cnv->removeAnimation(this); + } + } +} + +/* + \fn void QtCanvasItem::setXVelocity(double vx) + + Sets the horizontal component of the canvas item's velocity to \a vx. + + \sa setYVelocity() setVelocity() +*/ + +/* + \fn void QtCanvasItem::setYVelocity(double vy) + + Sets the vertical component of the canvas item's velocity to \a vy. + + \sa setXVelocity() setVelocity() +*/ + +/* + Sets the canvas item to be in motion, moving by \a vx and \a vy + pixels in the horizontal and vertical directions respectively. + + \sa advance() setXVelocity() setYVelocity() +*/ +void QtCanvasItem::setVelocity(double vx, double vy) +{ + if (ext || vx!= 0.0 || vy!= 0.0) { + if (!ani) + setAnimated(true); + extra().vx = vx; + extra().vy = vy; + } +} + +/* + Returns the horizontal velocity component of the canvas item. +*/ +double QtCanvasItem::xVelocity() const +{ + return ext ? ext->vx : 0; +} + +/* + Returns the vertical velocity component of the canvas item. +*/ +double QtCanvasItem::yVelocity() const +{ + return ext ? ext->vy : 0; +} + +/* + The default implementation moves the canvas item, if it is + animated(), by the preset velocity if \a phase is 1, and does + nothing if \a phase is 0. + + Note that if you reimplement this function, the reimplementation + must not change the canvas in any way, for example it must not add + or remove items. + + \sa QtCanvas::advance() setVelocity() +*/ +void QtCanvasItem::advance(int phase) +{ + if (ext && phase == 1) + moveBy(ext->vx, ext->vy); +} + +/* + \fn void QtCanvasItem::draw(QPainter& painter) + + This abstract virtual function draws the canvas item using \a painter. +*/ + +/* + Sets the QtCanvas upon which the canvas item is to be drawn to \a c. + + \sa canvas() +*/ +void QtCanvasItem::setCanvas(QtCanvas* c) +{ + bool v = isVisible(); + setVisible(false); + if (cnv) { + if (ext) + cnv->removeAnimation(this); + cnv->removeItem(this); + } + cnv = c; + if (cnv) { + cnv->addItem(this); + if (ext) + cnv->addAnimation(this); + } + setVisible(v); +} + +/* + \fn QtCanvas* QtCanvasItem::canvas() const + + Returns the canvas containing the canvas item. +*/ + +/* Shorthand for setVisible(true). */ +void QtCanvasItem::show() +{ + setVisible(true); +} + +/* Shorthand for setVisible(false). */ +void QtCanvasItem::hide() +{ + setVisible(false); +} + +/* + Makes the canvas item visible if \a yes is true, or invisible if + \a yes is false. The change takes effect when QtCanvas::update() is + next called. +*/ +void QtCanvasItem::setVisible(bool yes) +{ + if ((bool)vis!= yes) { + if (yes) { + vis = (uint)yes; + addToChunks(); + } else { + removeFromChunks(); + vis = (uint)yes; + } + } +} +/* + \obsolete + \fn bool QtCanvasItem::visible() const + Use isVisible() instead. +*/ + +/* + \fn bool QtCanvasItem::isVisible() const + + Returns true if the canvas item is visible; otherwise returns + false. + + Note that in this context true does \e not mean that the canvas + item is currently in a view, merely that if a view is showing the + area where the canvas item is positioned, and the item is not + obscured by items with higher z values, and the view is not + obscured by overlaying windows, it would be visible. + + \sa setVisible(), z() +*/ + +/* + \obsolete + \fn bool QtCanvasItem::selected() const + Use isSelected() instead. +*/ + +/* + \fn bool QtCanvasItem::isSelected() const + + Returns true if the canvas item is selected; otherwise returns false. +*/ + +/* + Sets the selected flag of the item to \a yes. If this changes the + item's selected state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setSelected() + function is supplied because many applications need it, but it is + up to you how you use the isSelected() value. +*/ +void QtCanvasItem::setSelected(bool yes) +{ + if ((bool)sel!= yes) { + sel = (uint)yes; + changeChunks(); + } +} + +/* + \obsolete + \fn bool QtCanvasItem::enabled() const + Use isEnabled() instead. +*/ + +/* + \fn bool QtCanvasItem::isEnabled() const + + Returns true if the QtCanvasItem is enabled; otherwise returns false. +*/ + +/* + Sets the enabled flag of the item to \a yes. If this changes the + item's enabled state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setEnabled() + function is supplied because many applications need it, but it is + up to you how you use the isEnabled() value. +*/ +void QtCanvasItem::setEnabled(bool yes) +{ + if (ena!= (uint)yes) { + ena = (uint)yes; + changeChunks(); + } +} + +/* + \obsolete + \fn bool QtCanvasItem::active() const + Use isActive() instead. +*/ + +/* + \fn bool QtCanvasItem::isActive() const + + Returns true if the QtCanvasItem is active; otherwise returns false. +*/ + +/* + Sets the active flag of the item to \a yes. If this changes the + item's active state the item will be redrawn when + QtCanvas::update() is next called. + + The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem + subclasses do not make use of this value. The setActive() function + is supplied because many applications need it, but it is up to you + how you use the isActive() value. +*/ +void QtCanvasItem::setActive(bool yes) +{ + if (act!= (uint)yes) { + act = (uint)yes; + changeChunks(); + } +} + +bool qt_testCollision(const QtCanvasSprite* s1, const QtCanvasSprite* s2) +{ + const QImage* s2image = s2->imageAdvanced()->collision_mask; + QRect s2area = s2->boundingRectAdvanced(); + + QRect cyourarea(s2area.x(), s2area.y(), + s2area.width(), s2area.height()); + + QImage* s1image = s1->imageAdvanced()->collision_mask; + + QRect s1area = s1->boundingRectAdvanced(); + + QRect ourarea = s1area.intersected(cyourarea); + + if (ourarea.isEmpty()) + return false; + + int x2 = ourarea.x()-cyourarea.x(); + int y2 = ourarea.y()-cyourarea.y(); + int x1 = ourarea.x()-s1area.x(); + int y1 = ourarea.y()-s1area.y(); + int w = ourarea.width(); + int h = ourarea.height(); + + if (!s2image) { + if (!s1image) + return w>0 && h>0; + // swap everything around + int t; + t = x1; x1 = x2; x2 = t; + t = y1; x1 = y2; y2 = t; + s2image = s1image; + s1image = 0; + } + + // s2image != 0 + + // A non-linear search may be more efficient. + // Perhaps spiralling out from the center, or a simpler + // vertical expansion from the centreline. + + // We assume that sprite masks don't have + // different bit orders. + // + // Q_ASSERT(s1image->bitOrder() == s2image->bitOrder()); + + if (s1image) { + if (s1image->format() == QImage::Format_MonoLSB) { + for (int j = 0; j < h; j++) { + uchar* ml = s1image->scanLine(y1+j); + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7)) + && *(ml + ((x1+i) >> 3)) & (1 << ((x1+i) & 7))) + { + return true; + } + } + } + } else { + for (int j = 0; j < h; j++) { + uchar* ml = s1image->scanLine(y1+j); + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7))) + && *(ml + ((x1+i) >> 3)) & (1 << (7-((x1+i) & 7)))) + { + return true; + } + } + } + } + } else { + if (s2image->format() == QImage::Format_MonoLSB) { + for (int j = 0; j < h; j++) { + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << ((x2+i) & 7))) + { + return true; + } + } + } + } else { + for (int j = 0; j< h; j++) { + const uchar* yl = s2image->scanLine(y2+j); + for (int i = 0; i < w; i++) { + if (*(yl + ((x2+i) >> 3)) & (1 << (7-((x2+i) & 7)))) + { + return true; + } + } + } + } + } + + return false; +} + +static bool collision_double_dispatch(const QtCanvasSprite* s1, + const QtCanvasPolygonalItem* p1, + const QtCanvasRectangle* r1, + const QtCanvasEllipse* e1, + const QtCanvasText* t1, + const QtCanvasSprite* s2, + const QtCanvasPolygonalItem* p2, + const QtCanvasRectangle* r2, + const QtCanvasEllipse* e2, + const QtCanvasText* t2) +{ + const QtCanvasItem* i1 = s1 ? + (const QtCanvasItem*)s1 : p1 ? + (const QtCanvasItem*)p1 : r1 ? + (const QtCanvasItem*)r1 : e1 ? + (const QtCanvasItem*)e1 : (const QtCanvasItem*)t1; + const QtCanvasItem* i2 = s2 ? + (const QtCanvasItem*)s2 : p2 ? + (const QtCanvasItem*)p2 : r2 ? + (const QtCanvasItem*)r2 : e2 ? + (const QtCanvasItem*)e2 : (const QtCanvasItem*)t2; + + if (s1 && s2) { + // a + return qt_testCollision(s1, s2); + } else if ((r1 || t1 || s1) && (r2 || t2 || s2)) { + // b + QRect rc1 = i1->boundingRectAdvanced(); + QRect rc2 = i2->boundingRectAdvanced(); + return rc1.intersects(rc2); + } else if (e1 && e2 + && e1->angleLength()>= 360*16 && e2->angleLength()>= 360*16 + && e1->width() == e1->height() + && e2->width() == e2->height()) { + // c + double xd = (e1->x()+e1->xVelocity())-(e2->x()+e1->xVelocity()); + double yd = (e1->y()+e1->yVelocity())-(e2->y()+e1->yVelocity()); + double rd = (e1->width()+e2->width())/2; + return xd*xd+yd*yd <= rd*rd; + } else if (p1 && (p2 || s2 || t2)) { + // d + QPolygon pa1 = p1->areaPointsAdvanced(); + QPolygon pa2 = p2 ? p2->areaPointsAdvanced() + : QPolygon(i2->boundingRectAdvanced()); + bool col = !(QRegion(pa1) & QRegion(pa2, Qt::WindingFill)).isEmpty(); + + return col; + } else { + return collision_double_dispatch(s2, p2, r2, e2, t2, + s1, p1, r1, e1, t1); + } +} + +/* + \fn bool QtCanvasItem::collidesWith(const QtCanvasItem* other) const + + Returns true if the canvas item will collide with the \a other + item \e after they have moved by their current velocities; + otherwise returns false. + + \sa collisions() +*/ + + +/* + \class QtCanvasSprite qtcanvas.h + \brief The QtCanvasSprite class provides an animated canvas item on a QtCanvas. + + A canvas sprite is an object which can contain any number of images + (referred to as frames), only one of which is current, i.e. + displayed, at any one time. The images can be passed in the + constructor or set or changed later with setSequence(). If you + subclass QtCanvasSprite you can change the frame that is displayed + periodically, e.g. whenever QtCanvasItem::advance(1) is called to + create the effect of animation. + + The current frame can be set with setFrame() or with move(). The + number of frames available is given by frameCount(). The bounding + rectangle of the current frame is returned by boundingRect(). + + The current frame's image can be retrieved with image(); use + imageAdvanced() to retrieve the image for the frame that will be + shown after advance(1) is called. Use the image() overload passing + it an integer index to retrieve a particular image from the list of + frames. + + Use width() and height() to retrieve the dimensions of the current + frame. + + Use leftEdge() and rightEdge() to retrieve the current frame's + left-hand and right-hand x-coordinates respectively. Use + bottomEdge() and topEdge() to retrieve the current frame's bottom + and top y-coordinates respectively. These functions have an overload + which will accept an integer frame number to retrieve the + coordinates of a particular frame. + + QtCanvasSprite draws very quickly, at the expense of memory. + + The current frame's image can be drawn on a painter with draw(). + + Like any other canvas item, canvas sprites can be moved with + move() which sets the x and y coordinates and the frame number, as + well as with QtCanvasItem::move() and QtCanvasItem::moveBy(), or by + setting coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() + and QtCanvasItem::setZ(). + +*/ + + +/* + \reimp +*/ +bool QtCanvasSprite::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(this, 0, 0, 0, 0); +} + +/* + Returns true if the canvas item collides with any of the given + items; otherwise returns false. The parameters, \a s, \a p, \a r, + \a e and \a t, are all the same object, this is just a type + resolution trick. +*/ +bool QtCanvasSprite::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, this, 0, 0, 0, 0); +} + +/* + \reimp +*/ +bool QtCanvasPolygonalItem::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, this, 0, 0, 0); +} + +bool QtCanvasPolygonalItem::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, 0, 0, 0); +} + +/* + \reimp +*/ +bool QtCanvasRectangle::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, this, this, 0, 0); +} + +bool QtCanvasRectangle::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, this, 0, 0); +} + + +/* + \reimp +*/ +bool QtCanvasEllipse::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0,this, 0, this, 0); +} + +bool QtCanvasEllipse::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, this, 0, this, 0); +} + +/* + \reimp +*/ +bool QtCanvasText::collidesWith(const QtCanvasItem* i) const +{ + return i->collidesWith(0, 0, 0, 0, this); +} + +bool QtCanvasText::collidesWith(const QtCanvasSprite* s, + const QtCanvasPolygonalItem* p, + const QtCanvasRectangle* r, + const QtCanvasEllipse* e, + const QtCanvasText* t) const +{ + return collision_double_dispatch(s, p, r, e, t, 0, 0, 0, 0, this); +} + +/* + Returns the list of canvas items that this canvas item has + collided with. + + A collision is generally defined as occurring when the pixels of + one item draw on the pixels of another item, but not all + subclasses are so precise. Also, since pixel-wise collision + detection can be slow, this function works in either exact or + inexact mode, according to the \a exact parameter. + + If \a exact is true, the canvas items returned have been + accurately tested for collision with the canvas item. + + If \a exact is false, the canvas items returned are \e near the + canvas item. You can test the canvas items returned using + collidesWith() if any are interesting collision candidates. By + using this approach, you can ignore some canvas items for which + collisions are not relevant. + + The returned list is a list of QtCanvasItems, but often you will + need to cast the items to their subclass types. The safe way to do + this is to use rtti() before casting. This provides some of the + functionality of the standard C++ dynamic cast operation even on + compilers where dynamic casts are not available. + + Note that a canvas item may be `on' a canvas, e.g. it was created + with the canvas as parameter, even though its coordinates place it + beyond the edge of the canvas's area. Collision detection only + works for canvas items which are wholly or partly within the + canvas's area. + + Note that if items have a velocity (see \l setVelocity()), then + collision testing is done based on where the item \e will be when + it moves, not its current location. For example, a "ball" item + doesn't need to actually embed into a "wall" item before a + collision is detected. For items without velocity, plain + intersection is used. +*/ +QtCanvasItemList QtCanvasItem::collisions(bool exact) const +{ + return canvas()->collisions(chunks(), this, exact); +} + +/* + Returns a list of canvas items that collide with the point \a p. + The list is ordered by z coordinates, from highest z coordinate + (front-most item) to lowest z coordinate (rear-most item). +*/ +QtCanvasItemList QtCanvas::collisions(const QPoint& p) const +{ + return collisions(QRect(p, QSize(1, 1))); +} + +/* + \overload + + Returns a list of items which collide with the rectangle \a r. The + list is ordered by z coordinates, from highest z coordinate + (front-most item) to lowest z coordinate (rear-most item). +*/ +QtCanvasItemList QtCanvas::collisions(const QRect& r) const +{ + QtCanvasRectangle i(r, (QtCanvas*)this); + i.setPen(NoPen); + i.show(); // doesn't actually show, since we destroy it + QtCanvasItemList l = i.collisions(true); + qSort(l.begin(), l.end(), QtCanvasItemLess()); + return l; +} + +/* + \overload + + Returns a list of canvas items which intersect with the chunks + listed in \a chunklist, excluding \a item. If \a exact is true, + only those which actually \link QtCanvasItem::collidesWith() + collide with\endlink \a item are returned; otherwise canvas items + are included just for being in the chunks. + + This is a utility function mainly used to implement the simpler + QtCanvasItem::collisions() function. +*/ +QtCanvasItemList QtCanvas::collisions(const QPolygon& chunklist, + const QtCanvasItem* item, bool exact) const +{ + QSet seen; + QtCanvasItemList result; + for (int i = 0; i <(int)chunklist.count(); i++) { + int x = chunklist[i].x(); + int y = chunklist[i].y(); + if (validChunk(x, y)) { + const QtCanvasItemList &l = chunk(x, y).list(); + for (int i = 0; i < l.size(); ++i) { + QtCanvasItem *g = l.at(i); + if (g != item) { + if (!seen.contains(g)) { + seen.insert(g); + if (!exact || item->collidesWith(g)) + result.append(g); + } + } + } + } + } + return result; +} + +/* + \internal + Adds the item to all the chunks it covers. +*/ +void QtCanvasItem::addToChunks() +{ + if (isVisible() && canvas()) { + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->addItemToChunk(this, pa[i].x(), pa[i].y()); + val = (uint)true; + } +} + +/* + \internal + Removes the item from all the chunks it covers. +*/ +void QtCanvasItem::removeFromChunks() +{ + if (isVisible() && canvas()) { + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->removeItemFromChunk(this, pa[i].x(), pa[i].y()); + } +} + +/* + \internal + Sets all the chunks covered by the item to be refreshed with QtCanvas::update() + is next called. +*/ +void QtCanvasItem::changeChunks() +{ + if (isVisible() && canvas()) { + if (!val) + addToChunks(); + QPolygon pa = chunks(); + for (int i = 0; i < (int)pa.count(); i++) + canvas()->setChangedChunk(pa[i].x(), pa[i].y()); + } +} + +/* + \fn QRect QtCanvasItem::boundingRect() const + + Returns the bounding rectangle in pixels that the canvas item covers. + + \sa boundingRectAdvanced() +*/ + +/* + Returns the bounding rectangle of pixels that the canvas item \e + will cover after advance(1) is called. + + \sa boundingRect() +*/ +QRect QtCanvasItem::boundingRectAdvanced() const +{ + int dx = int(x()+xVelocity())-int(x()); + int dy = int(y()+yVelocity())-int(y()); + QRect r = boundingRect(); + r.translate(dx, dy); + return r; +} + +/* + \class QtCanvasPixmap qtcanvas.h + \brief The QtCanvasPixmap class provides pixmaps for QtCanvasSprites. + + If you want to show a single pixmap on a QtCanvas use a + QtCanvasSprite with just one pixmap. + + When pixmaps are inserted into a QtCanvasPixmapArray they are held + as QtCanvasPixmaps. \l{QtCanvasSprite}s are used to show pixmaps on + \l{QtCanvas}es and hold their pixmaps in a QtCanvasPixmapArray. If + you retrieve a frame (pixmap) from a QtCanvasSprite it will be + returned as a QtCanvasPixmap. + + The pixmap is a QPixmap and can only be set in the constructor. + There are three different constructors, one taking a QPixmap, one + a QImage and one a file name that refers to a file in any + supported file format (see QImageReader). + + QtCanvasPixmap can have a hotspot which is defined in terms of an (x, + y) offset. When you create a QtCanvasPixmap from a PNG file or from + a QImage that has a QImage::offset(), the offset() is initialized + appropriately, otherwise the constructor leaves it at (0, 0). You + can set it later using setOffset(). When the QtCanvasPixmap is used + in a QtCanvasSprite, the offset position is the point at + QtCanvasItem::x() and QtCanvasItem::y(), not the top-left corner of + the pixmap. + + Note that for QtCanvasPixmap objects created by a QtCanvasSprite, the + position of each QtCanvasPixmap object is set so that the hotspot + stays in the same position. + + \sa QtCanvasPixmapArray QtCanvasItem QtCanvasSprite +*/ + + +/* + Constructs a QtCanvasPixmap that uses the image stored in \a + datafilename. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QString& datafilename) +{ + QImage image(datafilename); + init(image); +} + + +/* + Constructs a QtCanvasPixmap from the image \a image. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QImage& image) +{ + init(image); +} +/* + Constructs a QtCanvasPixmap from the pixmap \a pm using the offset + \a offset. +*/ +QtCanvasPixmap::QtCanvasPixmap(const QPixmap& pm, const QPoint& offset) +{ + init(pm, offset.x(), offset.y()); +} + +void QtCanvasPixmap::init(const QImage& image) +{ + this->QPixmap::operator = (QPixmap::fromImage(image)); + hotx = image.offset().x(); + hoty = image.offset().y(); +#ifndef QT_NO_IMAGE_DITHER_TO_1 + if(image.hasAlphaChannel()) { + QImage i = image.createAlphaMask(); + collision_mask = new QImage(i); + } else +#endif + collision_mask = 0; +} + +void QtCanvasPixmap::init(const QPixmap& pixmap, int hx, int hy) +{ + (QPixmap&)*this = pixmap; + hotx = hx; + hoty = hy; + if(pixmap.hasAlphaChannel()) { + QImage i = mask().toImage(); + collision_mask = new QImage(i); + } else + collision_mask = 0; +} + +/* + Destroys the pixmap. +*/ +QtCanvasPixmap::~QtCanvasPixmap() +{ + delete collision_mask; +} + +/* + \fn int QtCanvasPixmap::offsetX() const + + Returns the x-offset of the pixmap's hotspot. + + \sa setOffset() +*/ + +/* + \fn int QtCanvasPixmap::offsetY() const + + Returns the y-offset of the pixmap's hotspot. + + \sa setOffset() +*/ + +/* + \fn void QtCanvasPixmap::setOffset(int x, int y) + + Sets the offset of the pixmap's hotspot to (\a x, \a y). + + \warning Do not call this function if any QtCanvasSprites are + currently showing this pixmap. +*/ + +/* + \class QtCanvasPixmapArray qtcanvas.h + \brief The QtCanvasPixmapArray class provides an array of QtCanvasPixmaps. + + This class is used by QtCanvasSprite to hold an array of pixmaps. + It is used to implement animated sprites, i.e. images that change + over time, with each pixmap in the array holding one frame. + + Depending on the constructor you use you can load multiple pixmaps + into the array either from a directory (specifying a wildcard + pattern for the files), or from a list of QPixmaps. You can also + read in a set of pixmaps after construction using readPixmaps(). + + Individual pixmaps can be set with setImage() and retrieved with + image(). The number of pixmaps in the array is returned by + count(). + + QtCanvasSprite uses an image's mask for collision detection. You + can change this by reading in a separate set of image masks using + readCollisionMasks(). + +*/ + +/* + Constructs an invalid array (i.e. isValid() will return false). + You must call readPixmaps() before being able to use this + QtCanvasPixmapArray. +*/ +QtCanvasPixmapArray::QtCanvasPixmapArray() +: framecount(0), img(0) +{ +} + +/* + Constructs a QtCanvasPixmapArray from files. + + The \a fc parameter sets the number of frames to be loaded for + this image. + + If \a fc is not 0, \a datafilenamepattern should contain "%1", + e.g. "foo%1.png". The actual filenames are formed by replacing the + %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png, + foo0001.png, foo0002.png, etc. + + If \a fc is 0, \a datafilenamepattern is asssumed to be a + filename, and the image contained in this file will be loaded as + the first (and only) frame. + + If \a datafilenamepattern does not exist, is not readable, isn't + an image, or some other error occurs, the array ends up empty and + isValid() returns false. +*/ + +QtCanvasPixmapArray::QtCanvasPixmapArray(const QString& datafilenamepattern, + int fc) +: framecount(0), img(0) +{ + readPixmaps(datafilenamepattern, fc); +} + +/* + \obsolete + Use QtCanvasPixmapArray::QtCanvasPixmapArray(QtValueList, QPolygon) + instead. + + Constructs a QtCanvasPixmapArray from the list of QPixmaps \a + list. The \a hotspots list has to be of the same size as \a list. +*/ +QtCanvasPixmapArray::QtCanvasPixmapArray(const QList &list, const QPolygon &hotspots) + : framecount(list.count()), + img(new QtCanvasPixmap*[list.count()]) +{ + if (list.count() != hotspots.count()) { + qWarning("QtCanvasPixmapArray: lists have different lengths"); + reset(); + img = 0; + } else { + for (int i = 0; i < framecount; i++) + img[i] = new QtCanvasPixmap(list.at(i), hotspots.at(i)); + } +} + + +/* + Destroys the pixmap array and all the pixmaps it contains. +*/ +QtCanvasPixmapArray::~QtCanvasPixmapArray() +{ + reset(); +} + +void QtCanvasPixmapArray::reset() +{ + for (int i = 0; i < framecount; i++) + delete img[i]; + delete [] img; + img = 0; + framecount = 0; +} + +/* + Reads one or more pixmaps into the pixmap array. + + If \a fc is not 0, \a filenamepattern should contain "%1", e.g. + "foo%1.png". The actual filenames are formed by replacing the %1 + with four-digit integers from 0 to (fc - 1), e.g. foo0000.png, + foo0001.png, foo0002.png, etc. + + If \a fc is 0, \a filenamepattern is asssumed to be a filename, + and the image contained in this file will be loaded as the first + (and only) frame. + + If \a filenamepattern does not exist, is not readable, isn't an + image, or some other error occurs, this function will return + false, and isValid() will return false; otherwise this function + will return true. + + \sa isValid() +*/ +bool QtCanvasPixmapArray::readPixmaps(const QString& filenamepattern, + int fc) +{ + return readPixmaps(filenamepattern, fc, false); +} + +/* + Reads new collision masks for the array. + + By default, QtCanvasSprite uses the image mask of a sprite to + detect collisions. Use this function to set your own collision + image masks. + + If count() is 1 \a filename must specify a real filename to read + the mask from. If count() is greater than 1, the \a filename must + contain a "%1" that will get replaced by the number of the mask to + be loaded, just like QtCanvasPixmapArray::readPixmaps(). + + All collision masks must be 1-bit images or this function call + will fail. + + If the file isn't readable, contains the wrong number of images, + or there is some other error, this function will return false, and + the array will be flagged as invalid; otherwise this function + returns true. + + \sa isValid() +*/ +bool QtCanvasPixmapArray::readCollisionMasks(const QString& filename) +{ + return readPixmaps(filename, framecount, true); +} + + +bool QtCanvasPixmapArray::readPixmaps(const QString& datafilenamepattern, + int fc, bool maskonly) +{ + if (!maskonly) { + reset(); + framecount = fc; + if (!framecount) + framecount = 1; + img = new QtCanvasPixmap*[framecount]; + } + if (!img) + return false; + + bool ok = true; + bool arg = fc > 1; + if (!arg) + framecount = 1; + for (int i = 0; i < framecount; i++) { + QString r; + r.sprintf("%04d", i); + if (maskonly) { + if (!img[i]->collision_mask) + img[i]->collision_mask = new QImage(); + img[i]->collision_mask->load( + arg ? datafilenamepattern.arg(r) : datafilenamepattern); + ok = ok + && !img[i]->collision_mask->isNull() + && img[i]->collision_mask->depth() == 1; + } else { + img[i] = new QtCanvasPixmap( + arg ? datafilenamepattern.arg(r) : datafilenamepattern); + ok = ok && !img[i]->isNull(); + } + } + if (!ok) { + reset(); + } + return ok; +} + +/* + \obsolete + + Use isValid() instead. + + This returns false if the array is valid, and true if it is not. +*/ +bool QtCanvasPixmapArray::operator!() +{ + return img == 0; +} + +/* + Returns true if the pixmap array is valid; otherwise returns + false. +*/ +bool QtCanvasPixmapArray::isValid() const +{ + return (img != 0); +} + +/* + \fn QtCanvasPixmap* QtCanvasPixmapArray::image(int i) const + + Returns pixmap \a i in the array, if \a i is non-negative and less + than than count(), and returns an unspecified value otherwise. +*/ + +// ### wouldn't it be better to put empty QtCanvasPixmaps in there instead of +// initializing the additional elements in the array to 0? Lars +/* + Replaces the pixmap at index \a i with pixmap \a p. + + The array takes ownership of \a p and will delete \a p when the + array itself is deleted. + + If \a i is beyond the end of the array the array is extended to at + least i+1 elements, with elements count() to i-1 being initialized + to 0. +*/ +void QtCanvasPixmapArray::setImage(int i, QtCanvasPixmap* p) +{ + if (i >= framecount) { + QtCanvasPixmap** newimg = new QtCanvasPixmap*[i+1]; + memcpy(newimg, img, sizeof(QtCanvasPixmap *)*framecount); + memset(newimg + framecount, 0, sizeof(QtCanvasPixmap *)*(i+1 - framecount)); + framecount = i+1; + delete [] img; + img = newimg; + } + delete img[i]; img[i] = p; +} + +/* + \fn uint QtCanvasPixmapArray::count() const + + Returns the number of pixmaps in the array. +*/ + +/* + Returns the x-coordinate of the current left edge of the sprite. + (This may change as the sprite animates since different frames may + have different left edges.) + + \sa rightEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::leftEdge() const +{ + return int(x()) - image()->hotx; +} + +/* + \overload + + Returns what the x-coordinate of the left edge of the sprite would + be if the sprite (actually its hotspot) were moved to x-position + \a nx. + + \sa rightEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::leftEdge(int nx) const +{ + return nx - image()->hotx; +} + +/* + Returns the y-coordinate of the top edge of the sprite. (This may + change as the sprite animates since different frames may have + different top edges.) + + \sa leftEdge() rightEdge() bottomEdge() +*/ +int QtCanvasSprite::topEdge() const +{ + return int(y()) - image()->hoty; +} + +/* + \overload + + Returns what the y-coordinate of the top edge of the sprite would + be if the sprite (actually its hotspot) were moved to y-position + \a ny. + + \sa leftEdge() rightEdge() bottomEdge() +*/ +int QtCanvasSprite::topEdge(int ny) const +{ + return ny - image()->hoty; +} + +/* + Returns the x-coordinate of the current right edge of the sprite. + (This may change as the sprite animates since different frames may + have different right edges.) + + \sa leftEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::rightEdge() const +{ + return leftEdge() + image()->width()-1; +} + +/* + \overload + + Returns what the x-coordinate of the right edge of the sprite + would be if the sprite (actually its hotspot) were moved to + x-position \a nx. + + \sa leftEdge() bottomEdge() topEdge() +*/ +int QtCanvasSprite::rightEdge(int nx) const +{ + return leftEdge(nx) + image()->width()-1; +} + +/* + Returns the y-coordinate of the current bottom edge of the sprite. + (This may change as the sprite animates since different frames may + have different bottom edges.) + + \sa leftEdge() rightEdge() topEdge() +*/ +int QtCanvasSprite::bottomEdge() const +{ + return topEdge() + image()->height()-1; +} + +/* + \overload + + Returns what the y-coordinate of the top edge of the sprite would + be if the sprite (actually its hotspot) were moved to y-position + \a ny. + + \sa leftEdge() rightEdge() topEdge() +*/ +int QtCanvasSprite::bottomEdge(int ny) const +{ + return topEdge(ny) + image()->height()-1; +} + +/* + \fn QtCanvasPixmap* QtCanvasSprite::image() const + + Returns the current frame's image. + + \sa frame(), setFrame() +*/ + +/* + \fn QtCanvasPixmap* QtCanvasSprite::image(int f) const + \overload + + Returns the image for frame \a f. Does not do any bounds checking on \a f. +*/ + +/* + Returns the image the sprite \e will have after advance(1) is + called. By default this is the same as image(). +*/ +QtCanvasPixmap* QtCanvasSprite::imageAdvanced() const +{ + return image(); +} + +/* + Returns the bounding rectangle for the image in the sprite's + current frame. This assumes that the images are tightly cropped + (i.e. do not have transparent pixels all along a side). +*/ +QRect QtCanvasSprite::boundingRect() const +{ + return QRect(leftEdge(), topEdge(), width(), height()); +} + + +/* + \internal + Returns the chunks covered by the item. +*/ +QPolygon QtCanvasItem::chunks() const +{ + QPolygon r; + int n = 0; + QRect br = boundingRect(); + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + br &= QRect(0, 0, canvas()->width(), canvas()->height()); + if (br.isValid()) { + r.resize((br.width()/chunksize+2)*(br.height()/chunksize+2)); + for (int j = br.top()/chunksize; j <= br.bottom()/chunksize; j++) { + for (int i = br.left()/chunksize; i <= br.right()/chunksize; i++) { + r[n++] = QPoint(i, j); + } + } + } + } + r.resize(n); + return r; +} + + +/* + \internal + Add the sprite to the chunks in its QtCanvas which it overlaps. +*/ +void QtCanvasSprite::addToChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->addItemToChunk(this, i, j); + } + } + } +} + +/* + \internal + Remove the sprite from the chunks in its QtCanvas which it overlaps. + + \sa addToChunks() +*/ +void QtCanvasSprite::removeFromChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->removeItemFromChunk(this, i, j); + } + } + } +} + +/* + The width of the sprite for the current frame's image. + + \sa frame() +*/ +//### mark: Why don't we have width(int) and height(int) to be +//consistent with leftEdge() and leftEdge(int)? +int QtCanvasSprite::width() const +{ + return image()->width(); +} + +/* + The height of the sprite for the current frame's image. + + \sa frame() +*/ +int QtCanvasSprite::height() const +{ + return image()->height(); +} + + +/* + Draws the current frame's image at the sprite's current position + on painter \a painter. +*/ +void QtCanvasSprite::draw(QPainter& painter) +{ + painter.drawPixmap(leftEdge(), topEdge(), *image()); +} + +/* + \class QtCanvasView qtcanvas.h + \brief The QtCanvasView class provides an on-screen view of a QtCanvas. + + A QtCanvasView is widget which provides a view of a QtCanvas. + + If you want users to be able to interact with a canvas view, + subclass QtCanvasView. You might then reimplement + QtScrollView::contentsMousePressEvent(). For example: + + \code + void MyCanvasView::contentsMousePressEvent(QMouseEvent* e) + { + QtCanvasItemList l = canvas()->collisions(e->pos()); + for (QtCanvasItemList::Iterator it = l.begin(); it!= l.end(); ++it) { + if ((*it)->rtti() == QtCanvasRectangle::RTTI) + qDebug("A QtCanvasRectangle lies somewhere at this point"); + } + } + \endcode + + The canvas view shows canvas canvas(); this can be changed using + setCanvas(). + + A transformation matrix can be used to transform the view of the + canvas in various ways, for example, zooming in or out or rotating. + For example: + + \code + QMatrix wm; + wm.scale(2, 2); // Zooms in by 2 times + wm.rotate(90); // Rotates 90 degrees counter clockwise + // around the origin. + wm.translate(0, -canvas->height()); + // moves the canvas down so what was visible + // before is still visible. + myCanvasView->setWorldMatrix(wm); + \endcode + + Use setWorldMatrix() to set the canvas view's world matrix: you must + ensure that the world matrix is invertible. The current world matrix + is retrievable with worldMatrix(), and its inversion is retrievable + with inverseWorldMatrix(). + + Example: + + The following code finds the part of the canvas that is visible in + this view, i.e. the bounding rectangle of the view in canvas coordinates. + + \code + QRect rc = QRect(myCanvasView->contentsX(), myCanvasView->contentsY(), + myCanvasView->visibleWidth(), myCanvasView->visibleHeight()); + QRect canvasRect = myCanvasView->inverseWorldMatrix().mapRect(rc); + \endcode + + \sa QMatrix QPainter::setWorldMatrix() + +*/ + +class QtCanvasWidget : public QWidget +{ +public: + QtCanvasWidget(QtCanvasView *view) : QWidget(view) { m_view = view; } +protected: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e) { + m_view->contentsMousePressEvent(e); + } + void mouseMoveEvent(QMouseEvent *e) { + m_view->contentsMouseMoveEvent(e); + } + void mouseReleaseEvent(QMouseEvent *e) { + m_view->contentsMouseReleaseEvent(e); + } + void mouseDoubleClickEvent(QMouseEvent *e) { + m_view->contentsMouseDoubleClickEvent(e); + } + void dragEnterEvent(QDragEnterEvent *e) { + m_view->contentsDragEnterEvent(e); + } + void dragMoveEvent(QDragMoveEvent *e) { + m_view->contentsDragMoveEvent(e); + } + void dragLeaveEvent(QDragLeaveEvent *e) { + m_view->contentsDragLeaveEvent(e); + } + void dropEvent(QDropEvent *e) { + m_view->contentsDropEvent(e); + } + void wheelEvent(QWheelEvent *e) { + m_view->contentsWheelEvent(e); + } + void contextMenuEvent(QContextMenuEvent *e) { + m_view->contentsContextMenuEvent(e); + } + + QtCanvasView *m_view; +}; + +void QtCanvasWidget::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + if (m_view->d->highQuality) { + p.setRenderHint(QPainter::Antialiasing); + p.setRenderHint(QPainter::SmoothPixmapTransform); + } + m_view->drawContents(&p, e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); +} + +/* + Constructs a QtCanvasView with parent \a parent. The canvas view + is not associated with a canvas, so you must to call setCanvas() + to view a canvas. +*/ +QtCanvasView::QtCanvasView(QWidget* parent) + : QScrollArea(parent) +{ + d = new QtCanvasViewData; + setWidget(new QtCanvasWidget(this)); + d->highQuality = false; + viewing = 0; + setCanvas(0); +} + +/* + \overload + + Constructs a QtCanvasView which views canvas \a canvas, with parent + \a parent. +*/ +QtCanvasView::QtCanvasView(QtCanvas* canvas, QWidget* parent) + : QScrollArea(parent) +{ + d = new QtCanvasViewData; + d->highQuality = false; + setWidget(new QtCanvasWidget(this)); + viewing = 0; + setCanvas(canvas); +} + +/* + Destroys the canvas view. The associated canvas is \e not deleted. +*/ +QtCanvasView::~QtCanvasView() +{ + delete d; + d = 0; + setCanvas(0); +} + +/* + \property QtCanvasView::highQualityRendering + \brief whether high quality rendering is turned on + + If high quality rendering is turned on, the canvas view will paint itself + using the QPainter::Antialiasing and QPainter::SmoothPixmapTransform + rendering flags. + + Enabling these flag will usually improve the visual appearance on the screen + at the cost of rendering speed. +*/ +bool QtCanvasView::highQualityRendering() const +{ + return d->highQuality; +} + +void QtCanvasView::setHighQualityRendering(bool enable) +{ + d->highQuality = enable; + widget()->update(); +} + + +void QtCanvasView::contentsMousePressEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseReleaseEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseDoubleClickEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsMouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsDragEnterEvent(QDragEnterEvent *) +{ +} + +void QtCanvasView::contentsDragMoveEvent(QDragMoveEvent *) +{ +} + +void QtCanvasView::contentsDragLeaveEvent(QDragLeaveEvent *) +{ +} + +void QtCanvasView::contentsDropEvent(QDropEvent *) +{ +} + +void QtCanvasView::contentsWheelEvent(QWheelEvent *e) +{ + e->ignore(); +} + +void QtCanvasView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + e->ignore(); +} + +/* + \fn QtCanvas* QtCanvasView::canvas() const + + Returns a pointer to the canvas which the QtCanvasView is currently + showing. +*/ + + +/* + Sets the canvas that the QtCanvasView is showing to the canvas \a + canvas. +*/ +void QtCanvasView::setCanvas(QtCanvas* canvas) +{ + if (viewing == canvas) + return; + + if (viewing) { + disconnect(viewing); + viewing->removeView(this); + } + viewing = canvas; + if (viewing) { + connect(viewing, SIGNAL(resized()), this, SLOT(updateContentsSize())); + viewing->addView(this); + } + if (d) // called by d'tor + updateContentsSize(); + update(); +} + +/* + Returns a reference to the canvas view's current transformation matrix. + + \sa setWorldMatrix() inverseWorldMatrix() +*/ +const QMatrix &QtCanvasView::worldMatrix() const +{ + return d->xform; +} + +/* + Returns a reference to the inverse of the canvas view's current + transformation matrix. + + \sa setWorldMatrix() worldMatrix() +*/ +const QMatrix &QtCanvasView::inverseWorldMatrix() const +{ + return d->ixform; +} + +/* + Sets the transformation matrix of the QtCanvasView to \a wm. The + matrix must be invertible (i.e. if you create a world matrix that + zooms out by 2 times, then the inverse of this matrix is one that + will zoom in by 2 times). + + When you use this, you should note that the performance of the + QtCanvasView will decrease considerably. + + Returns false if \a wm is not invertable; otherwise returns true. + + \sa worldMatrix() inverseWorldMatrix() QMatrix::isInvertible() +*/ +bool QtCanvasView::setWorldMatrix(const QMatrix & wm) +{ + bool ok = wm.isInvertible(); + if (ok) { + d->xform = wm; + d->ixform = wm.inverted(); + updateContentsSize(); + widget()->update(); + } + return ok; +} + +void QtCanvasView::updateContentsSize() +{ + if (viewing) { + QRect br; + br = d->xform.mapRect(QRect(0, 0, viewing->width(), viewing->height())); + + widget()->resize(br.size()); + } else { + widget()->resize(size()); + } +} + +/* + Repaints part of the QtCanvas that the canvas view is showing + starting at \a cx by \a cy, with a width of \a cw and a height of \a + ch using the painter \a p. +*/ +void QtCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch) +{ + if (!viewing) + return; + QPainterPath clipPath; + clipPath.addRect(viewing->rect()); + p->setClipPath(d->xform.map(clipPath), Qt::IntersectClip); + viewing->drawViewArea(this, p, QRect(cx, cy, cw, ch), false); +} + +/* + Suggests a size sufficient to view the entire canvas. +*/ +QSize QtCanvasView::sizeHint() const +{ + if (!canvas()) + return QScrollArea::sizeHint(); + // should maybe take transformations into account + return (canvas()->size() + 2 * QSize(frameWidth(), frameWidth())) + .boundedTo(3 * QApplication::desktop()->size() / 4); +} + +/* + \class QtCanvasPolygonalItem qtcanvas.h + \brief The QtCanvasPolygonalItem class provides a polygonal canvas item + on a QtCanvas. + + The mostly rectangular classes, such as QtCanvasSprite and + QtCanvasText, use the object's bounding rectangle for movement, + repainting and collision calculations. For most other items, the + bounding rectangle can be far too large -- a diagonal line being + the worst case, and there are many other cases which are also bad. + QtCanvasPolygonalItem provides polygon-based bounding rectangle + handling, etc., which is much faster for non-rectangular items. + + Derived classes should try to define as small an area as possible + to maximize efficiency, but the polygon must \e definitely be + contained completely within the polygonal area. Calculating the + exact requirements is usually difficult, but if you allow a small + overestimate it can be easy and quick, while still getting almost + all of QtCanvasPolygonalItem's speed. + + Note that all subclasses \e must call hide() in their destructor + since hide() needs to be able to access areaPoints(). + + Normally, QtCanvasPolygonalItem uses the odd-even algorithm for + determining whether an object intersects this object. You can + change this to the winding algorithm using setWinding(). + + The bounding rectangle is available using boundingRect(). The + points bounding the polygonal item are retrieved with + areaPoints(). Use areaPointsAdvanced() to retrieve the bounding + points the polygonal item \e will have after + QtCanvasItem::advance(1) has been called. + + If the shape of the polygonal item is about to change while the + item is visible, call invalidate() before updating with a + different result from \l areaPoints(). + + By default, QtCanvasPolygonalItem objects have a black pen and no + brush (the default QPen and QBrush constructors). You can change + this with setPen() and setBrush(), but note that some + QtCanvasPolygonalItem subclasses only use the brush, ignoring the + pen setting. + + The polygonal item can be drawn on a painter with draw(). + Subclasses must reimplement drawShape() to draw themselves. + + Like any other canvas item polygonal items can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting coordinates + with QtCanvasItem::setX(), QtCanvasItem::setY() and QtCanvasItem::setZ(). + +*/ + + +/* + Since most polygonal items don't have a pen, the default is + NoPen and a black brush. +*/ +static const QPen& defaultPolygonPen() +{ + static QPen* dp = 0; + if (!dp) + dp = new QPen; + return *dp; +} + +static const QBrush& defaultPolygonBrush() +{ + static QBrush* db = 0; + if (!db) + db = new QBrush; + return *db; +} + +/* + Constructs a QtCanvasPolygonalItem on the canvas \a canvas. +*/ +QtCanvasPolygonalItem::QtCanvasPolygonalItem(QtCanvas* canvas) : + QtCanvasItem(canvas), + br(defaultPolygonBrush()), + pn(defaultPolygonPen()) +{ + wind = 0; +} + +/* + Note that all subclasses \e must call hide() in their destructor + since hide() needs to be able to access areaPoints(). +*/ +QtCanvasPolygonalItem::~QtCanvasPolygonalItem() +{ +} + +/* + Returns true if the polygonal item uses the winding algorithm to + determine the "inside" of the polygon. Returns false if it uses + the odd-even algorithm. + + The default is to use the odd-even algorithm. + + \sa setWinding() +*/ +bool QtCanvasPolygonalItem::winding() const +{ + return wind; +} + +/* + If \a enable is true, the polygonal item will use the winding + algorithm to determine the "inside" of the polygon; otherwise the + odd-even algorithm will be used. + + The default is to use the odd-even algorithm. + + \sa winding() +*/ +void QtCanvasPolygonalItem::setWinding(bool enable) +{ + wind = enable; +} + +/* + Invalidates all information about the area covered by the canvas + item. The item will be updated automatically on the next call that + changes the item's status, for example, move() or update(). Call + this function if you are going to change the shape of the item (as + returned by areaPoints()) while the item is visible. +*/ +void QtCanvasPolygonalItem::invalidate() +{ + val = (uint)false; + removeFromChunks(); +} + +/* + \fn QtCanvasPolygonalItem::isValid() const + + Returns true if the polygonal item's area information has not been + invalidated; otherwise returns false. + + \sa invalidate() +*/ + +/* + Returns the points the polygonal item \e will have after + QtCanvasItem::advance(1) is called, i.e. what the points are when + advanced by the current xVelocity() and yVelocity(). +*/ +QPolygon QtCanvasPolygonalItem::areaPointsAdvanced() const +{ + int dx = int(x()+xVelocity())-int(x()); + int dy = int(y()+yVelocity())-int(y()); + QPolygon r = areaPoints(); + r.detach(); // Explicit sharing is stupid. + if (dx || dy) + r.translate(dx, dy); + return r; +} + +//#define QCANVAS_POLYGONS_DEBUG +#ifdef QCANVAS_POLYGONS_DEBUG +static QWidget* dbg_wid = 0; +static QPainter* dbg_ptr = 0; +#endif + +class QPolygonalProcessor { +public: + QPolygonalProcessor(QtCanvas* c, const QPolygon& pa) : + canvas(c) + { + QRect pixelbounds = pa.boundingRect(); + int cs = canvas->chunkSize(); + QRect canvasbounds = pixelbounds.intersected(canvas->rect()); + bounds.setLeft(canvasbounds.left()/cs); + bounds.setRight(canvasbounds.right()/cs); + bounds.setTop(canvasbounds.top()/cs); + bounds.setBottom(canvasbounds.bottom()/cs); + bitmap = QImage(bounds.width(), bounds.height(), QImage::Format_MonoLSB); + pnt = 0; + bitmap.fill(0); +#ifdef QCANVAS_POLYGONS_DEBUG + dbg_start(); +#endif + } + + inline void add(int x, int y) + { + if (pnt >= (int)result.size()) { + result.resize(pnt*2+10); + } + result[pnt++] = QPoint(x+bounds.x(), y+bounds.y()); +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) { + int cs = canvas->chunkSize(); + QRect r(x*cs+bounds.x()*cs, y*cs+bounds.y()*cs, cs-1, cs-1); + dbg_ptr->setPen(Qt::blue); + dbg_ptr->drawRect(r); + } +#endif + } + + inline void addBits(int x1, int x2, uchar newbits, int xo, int yo) + { + for (int i = x1; i <= x2; i++) + if (newbits & (1 <resize(800, 600); + dbg_wid->show(); + dbg_ptr = new QPainter(dbg_wid); + dbg_ptr->setBrush(Qt::NoBrush); + } + dbg_ptr->fillRect(dbg_wid->rect(), Qt::white); + } +#endif + + void doSpans(int n, QPoint* pt, int* w) + { + int cs = canvas->chunkSize(); + for (int j = 0; j < n; j++) { + int y = pt[j].y()/cs-bounds.y(); + if (y >= bitmap.height() || y < 0) continue; + uchar* l = bitmap.scanLine(y); + int x = pt[j].x(); + int x1 = x/cs-bounds.x(); + if (x1 > bounds.width()) continue; + x1 = qMax(0,x1); + int x2 = (x+w[j])/cs-bounds.x(); + if (x2 < 0) continue; + x2 = qMin(bounds.width(), x2); + int x1q = x1/8; + int x1r = x1%8; + int x2q = x2/8; + int x2r = x2%8; +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::yellow); +#endif + if (x1q == x2q) { + uchar newbits = (~l[x1q]) & (((2 <<(x2r-x1r))-1) <setPen(Qt::darkGreen); +#endif + addBits(x1r, x2r, newbits, x1q*8, y); + l[x1q] |= newbits; + } + } else { +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::blue); +#endif + uchar newbits1 = (~l[x1q]) & (0xff <setPen(Qt::green); +#endif + addBits(x1r, 7, newbits1, x1q*8, y); + l[x1q] |= newbits1; + } + for (int i = x1q+1; i < x2q; i++) { + if (l[i] != 0xff) { + addBits(0, 7, ~l[i], i*8, y); + l[i] = 0xff; + } + } + uchar newbits2 = (~l[x2q]) & (0xff>>(7-x2r)); + if (newbits2) { +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) dbg_ptr->setPen(Qt::red); +#endif + addBits(0, x2r, newbits2, x2q*8, y); + l[x2q] |= newbits2; + } + } +#ifdef QCANVAS_POLYGONS_DEBUG + if (dbg_ptr) { + dbg_ptr->drawLine(pt[j], pt[j]+QPoint(w[j], 0)); + } +#endif + } + result.resize(pnt); + } + + int pnt; + QPolygon result; + QtCanvas* canvas; + QRect bounds; + QImage bitmap; +}; + + +QPolygon QtCanvasPolygonalItem::chunks() const +{ + QPolygon pa = areaPoints(); + + if (!pa.size()) { + pa.detach(); // Explicit sharing is stupid. + return pa; + } + + QPolygonalProcessor processor(canvas(), pa); + + scanPolygon(pa, wind, processor); + + return processor.result; +} +/* + Simply calls QtCanvasItem::chunks(). +*/ +QPolygon QtCanvasRectangle::chunks() const +{ + // No need to do a polygon scan! + return QtCanvasItem::chunks(); +} + +/* + Returns the bounding rectangle of the polygonal item, based on + areaPoints(). +*/ +QRect QtCanvasPolygonalItem::boundingRect() const +{ + return areaPoints().boundingRect(); +} + +/* + Reimplemented from QtCanvasItem, this draws the polygonal item by + setting the pen and brush for the item on the painter \a p and + calling drawShape(). +*/ +void QtCanvasPolygonalItem::draw(QPainter & p) +{ + p.setPen(pn); + p.setBrush(br); + drawShape(p); +} + +/* + \fn void QtCanvasPolygonalItem::drawShape(QPainter & p) + + Subclasses must reimplement this function to draw their shape. The + pen and brush of \a p are already set to pen() and brush() prior + to calling this function. + + \sa draw() +*/ + +/* + \fn QPen QtCanvasPolygonalItem::pen() const + + Returns the QPen used to draw the outline of the item, if any. + + \sa setPen() +*/ + +/* + \fn QBrush QtCanvasPolygonalItem::brush() const + + Returns the QBrush used to fill the item, if filled. + + \sa setBrush() +*/ + +/* + Sets the QPen used when drawing the item to the pen \a p. + Note that many QtCanvasPolygonalItems do not use the pen value. + + \sa setBrush(), pen(), drawShape() +*/ +void QtCanvasPolygonalItem::setPen(QPen p) +{ + if (pn != p) { + removeFromChunks(); + pn = p; + addToChunks(); + } +} + +/* + Sets the QBrush used when drawing the polygonal item to the brush \a b. + + \sa setPen(), brush(), drawShape() +*/ +void QtCanvasPolygonalItem::setBrush(QBrush b) +{ + if (br != b) { + br = b; + changeChunks(); + } +} + + +/* + \class QtCanvasPolygon qtcanvas.h + \brief The QtCanvasPolygon class provides a polygon on a QtCanvas. + + Paints a polygon with a QBrush. The polygon's points can be set in + the constructor or set or changed later using setPoints(). Use + points() to retrieve the points, or areaPoints() to retrieve the + points relative to the canvas's origin. + + The polygon can be drawn on a painter with drawShape(). + + Like any other canvas item polygons can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + + Note: QtCanvasPolygon does not use the pen. +*/ + +/* + Constructs a point-less polygon on the canvas \a canvas. You + should call setPoints() before using it further. +*/ +QtCanvasPolygon::QtCanvasPolygon(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas) +{ +} + +/* + Destroys the polygon. +*/ +QtCanvasPolygon::~QtCanvasPolygon() +{ + hide(); +} + +/* + Draws the polygon using the painter \a p. + + Note that QtCanvasPolygon does not support an outline (the pen is + always NoPen). +*/ +void QtCanvasPolygon::drawShape(QPainter & p) +{ + // ### why can't we draw outlines? We could use drawPolyline for it. Lars + // ### see other message. Warwick + + p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-: + p.drawPolygon(poly); +} + +/* + Sets the points of the polygon to be \a pa. These points will have + their x and y coordinates automatically translated by x(), y() as + the polygon is moved. +*/ +void QtCanvasPolygon::setPoints(QPolygon pa) +{ + removeFromChunks(); + poly = pa; + poly.detach(); // Explicit sharing is stupid. + poly.translate((int)x(), (int)y()); + addToChunks(); +} + +/* + \reimp +*/ +void QtCanvasPolygon::moveBy(double dx, double dy) +{ + // Note: does NOT call QtCanvasPolygonalItem::moveBy(), since that + // only does half this work. + // + int idx = int(x()+dx)-int(x()); + int idy = int(y()+dy)-int(y()); + if (idx || idy) { + removeFromChunks(); + poly.translate(idx, idy); + } + myx+= dx; + myy+= dy; + if (idx || idy) { + addToChunks(); + } +} + +/* + \class QtCanvasSpline qtcanvas.h + \brief The QtCanvasSpline class provides multi-bezier splines on a QtCanvas. + + A QtCanvasSpline is a sequence of 4-point bezier curves joined + together to make a curved shape. + + You set the control points of the spline with setControlPoints(). + + If the bezier is closed(), then the first control point will be + re-used as the last control point. Therefore, a closed bezier must + have a multiple of 3 control points and an open bezier must have + one extra point. + + The beziers are not necessarily joined "smoothly". To ensure this, + set control points appropriately (general reference texts about + beziers will explain this in detail). + + Like any other canvas item splines can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + +*/ + +/* + Create a spline with no control points on the canvas \a canvas. + + \sa setControlPoints() +*/ +QtCanvasSpline::QtCanvasSpline(QtCanvas* canvas) : + QtCanvasPolygon(canvas), + cl(true) +{ +} + +/* + Destroy the spline. +*/ +QtCanvasSpline::~QtCanvasSpline() +{ +} + +/* + Set the spline control points to \a ctrl. + + If \a close is true, then the first point in \a ctrl will be + re-used as the last point, and the number of control points must + be a multiple of 3. If \a close is false, one additional control + point is required, and the number of control points must be one of + (4, 7, 10, 13, ...). + + If the number of control points doesn't meet the above conditions, + the number of points will be truncated to the largest number of + points that do meet the requirement. +*/ +void QtCanvasSpline::setControlPoints(QPolygon ctrl, bool close) +{ + if ((int)ctrl.count() % 3 != (close ? 0 : 1)) { + qWarning("QtCanvasSpline::setControlPoints(): Number of points doesn't fit."); + int numCurves = (ctrl.count() - (close ? 0 : 1))/ 3; + ctrl.resize(numCurves*3 + (close ? 0 : 1)); + } + + cl = close; + bez = ctrl; + recalcPoly(); +} + +/* + Returns the current set of control points. + + \sa setControlPoints(), closed() +*/ +QPolygon QtCanvasSpline::controlPoints() const +{ + return bez; +} + +/* + Returns true if the control points are a closed set; otherwise + returns false. +*/ +bool QtCanvasSpline::closed() const +{ + return cl; +} + +void QtCanvasSpline::recalcPoly() +{ + if (bez.count() == 0) + return; + + QPainterPath path; + path.moveTo(bez[0]); + for (int i = 1; i < (int)bez.count()-1; i+= 3) { + path.cubicTo(bez[i], bez[i+1], cl ? bez[(i+2)%bez.size()] : bez[i+2]); + } + QPolygon p = path.toFillPolygon().toPolygon(); + QtCanvasPolygon::setPoints(p); +} + +/* + \fn QPolygon QtCanvasPolygonalItem::areaPoints() const + + This function must be reimplemented by subclasses. It \e must + return the points bounding (i.e. outside and not touching) the + shape or drawing errors will occur. +*/ + +/* + \fn QPolygon QtCanvasPolygon::points() const + + Returns the vertices of the polygon, not translated by the position. + + \sa setPoints(), areaPoints() +*/ +QPolygon QtCanvasPolygon::points() const +{ + QPolygon pa = areaPoints(); + pa.translate(int(-x()), int(-y())); + return pa; +} + +/* + Returns the vertices of the polygon translated by the polygon's + current x(), y() position, i.e. relative to the canvas's origin. + + \sa setPoints(), points() +*/ +QPolygon QtCanvasPolygon::areaPoints() const +{ + return poly; +} + +/* + \class QtCanvasLine qtcanvas.h + \brief The QtCanvasLine class provides a line on a QtCanvas. + + The line inherits functionality from QtCanvasPolygonalItem, for + example the setPen() function. The start and end points of the + line are set with setPoints(). + + Like any other canvas item lines can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). +*/ + +/* + Constructs a line from (0, 0) to (0, 0) on \a canvas. + + \sa setPoints() +*/ +QtCanvasLine::QtCanvasLine(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas) +{ + x1 = y1 = x2 = y2 = 0; +} + +/* + Destroys the line. +*/ +QtCanvasLine::~QtCanvasLine() +{ + hide(); +} + +/* + \reimp +*/ +void QtCanvasLine::setPen(QPen p) +{ + QtCanvasPolygonalItem::setPen(p); +} + +/* + \fn QPoint QtCanvasLine::startPoint () const + + Returns the start point of the line. + + \sa setPoints(), endPoint() +*/ + +/* + \fn QPoint QtCanvasLine::endPoint () const + + Returns the end point of the line. + + \sa setPoints(), startPoint() +*/ + +/* + Sets the line's start point to (\a xa, \a ya) and its end point to + (\a xb, \a yb). +*/ +void QtCanvasLine::setPoints(int xa, int ya, int xb, int yb) +{ + if (x1 != xa || x2 != xb || y1 != ya || y2 != yb) { + removeFromChunks(); + x1 = xa; + y1 = ya; + x2 = xb; + y2 = yb; + addToChunks(); + } +} + +/* + \reimp +*/ +void QtCanvasLine::drawShape(QPainter &p) +{ + p.drawLine((int)(x()+x1), (int)(y()+y1), (int)(x()+x2), (int)(y()+y2)); +} + +/* + \reimp + + Note that the area defined by the line is somewhat thicker than + the line that is actually drawn. +*/ +QPolygon QtCanvasLine::areaPoints() const +{ + QPolygon p(4); + int xi = int(x()); + int yi = int(y()); + int pw = pen().width(); + int dx = qAbs(x1-x2); + int dy = qAbs(y1-y2); + pw = pw*4/3+2; // approx pw*sqrt(2) + int px = x1 < x2 ? -pw : pw ; + int py = y1 < y2 ? -pw : pw ; + if (dx && dy && (dx > dy ? (dx*2/dy <= 2) : (dy*2/dx <= 2))) { + // steep + if (px == py) { + p[0] = QPoint(x1+xi, y1+yi+py); + p[1] = QPoint(x2+xi-px, y2+yi); + p[2] = QPoint(x2+xi, y2+yi-py); + p[3] = QPoint(x1+xi+px, y1+yi); + } else { + p[0] = QPoint(x1+xi+px, y1+yi); + p[1] = QPoint(x2+xi, y2+yi-py); + p[2] = QPoint(x2+xi-px, y2+yi); + p[3] = QPoint(x1+xi, y1+yi+py); + } + } else if (dx > dy) { + // horizontal + p[0] = QPoint(x1+xi+px, y1+yi+py); + p[1] = QPoint(x2+xi-px, y2+yi+py); + p[2] = QPoint(x2+xi-px, y2+yi-py); + p[3] = QPoint(x1+xi+px, y1+yi-py); + } else { + // vertical + p[0] = QPoint(x1+xi+px, y1+yi+py); + p[1] = QPoint(x2+xi+px, y2+yi-py); + p[2] = QPoint(x2+xi-px, y2+yi-py); + p[3] = QPoint(x1+xi-px, y1+yi+py); + } + return p; +} + +/* + \reimp + +*/ + +void QtCanvasLine::moveBy(double dx, double dy) +{ + QtCanvasPolygonalItem::moveBy(dx, dy); +} + +/* + \class QtCanvasRectangle qtcanvas.h + \brief The QtCanvasRectangle class provides a rectangle on a QtCanvas. + + This item paints a single rectangle which may have any pen() and + brush(), but may not be tilted/rotated. For rotated rectangles, + use QtCanvasPolygon. + + The rectangle's size and initial position can be set in the + constructor. The size can be set or changed later using setSize(). + Use height() and width() to retrieve the rectangle's dimensions. + + The rectangle can be drawn on a painter with drawShape(). + + Like any other canvas item rectangles can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). + +*/ + +/* + Constructs a rectangle at position (0,0) with both width and + height set to 32 pixels on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(32), h(32) +{ +} + +/* + Constructs a rectangle positioned and sized by \a r on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(const QRect& r, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(r.width()), h(r.height()) +{ + move(r.x(), r.y()); +} + +/* + Constructs a rectangle at position (\a x, \a y) and size \a width + by \a height, on \a canvas. +*/ +QtCanvasRectangle::QtCanvasRectangle(int x, int y, int width, int height, + QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height) +{ + move(x, y); +} + +/* + Destroys the rectangle. +*/ +QtCanvasRectangle::~QtCanvasRectangle() +{ + hide(); +} + + +/* + Returns the width of the rectangle. +*/ +int QtCanvasRectangle::width() const +{ + return w; +} + +/* + Returns the height of the rectangle. +*/ +int QtCanvasRectangle::height() const +{ + return h; +} + +/* + Sets the \a width and \a height of the rectangle. +*/ +void QtCanvasRectangle::setSize(int width, int height) +{ + if (w != width || h != height) { + removeFromChunks(); + w = width; + h = height; + addToChunks(); + } +} + +/* + \fn QSize QtCanvasRectangle::size() const + + Returns the width() and height() of the rectangle. + + \sa rect(), setSize() +*/ + +/* + \fn QRect QtCanvasRectangle::rect() const + + Returns the integer-converted x(), y() position and size() of the + rectangle as a QRect. +*/ + +/* + \reimp +*/ +QPolygon QtCanvasRectangle::areaPoints() const +{ + QPolygon pa(4); + int pw = (pen().width()+1)/2; + if (pw < 1) pw = 1; + if (pen() == NoPen) pw = 0; + pa[0] = QPoint((int)x()-pw, (int)y()-pw); + pa[1] = pa[0] + QPoint(w+pw*2, 0); + pa[2] = pa[1] + QPoint(0, h+pw*2); + pa[3] = pa[0] + QPoint(0, h+pw*2); + return pa; +} + +/* + Draws the rectangle on painter \a p. +*/ +void QtCanvasRectangle::drawShape(QPainter & p) +{ + p.drawRect((int)x(), (int)y(), w, h); +} + + +/* + \class QtCanvasEllipse qtcanvas.h + \brief The QtCanvasEllipse class provides an ellipse or ellipse segment on a QtCanvas. + + A canvas item that paints an ellipse or ellipse segment with a QBrush. + The ellipse's height, width, start angle and angle length can be set + at construction time. The size can be changed at runtime with + setSize(), and the angles can be changed (if you're displaying an + ellipse segment rather than a whole ellipse) with setAngles(). + + Note that angles are specified in 16ths of a degree. + + \target anglediagram + \img qcanvasellipse.png Ellipse + + If a start angle and length angle are set then an ellipse segment + will be drawn. The start angle is the angle that goes from zero in a + counter-clockwise direction (shown in green in the diagram). The + length angle is the angle from the start angle in a + counter-clockwise direction (shown in blue in the diagram). The blue + segment is the segment of the ellipse that would be drawn. If no + start angle and length angle are specified the entire ellipse is + drawn. + + The ellipse can be drawn on a painter with drawShape(). + + Like any other canvas item ellipses can be moved with move() and + moveBy(), or by setting coordinates with setX(), setY() and setZ(). + + Note: QtCanvasEllipse does not use the pen. +*/ + +/* + Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas. +*/ +QtCanvasEllipse::QtCanvasEllipse(QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(32), h(32), + a1(0), a2(360*16) +{ +} + +/* + Constructs a \a width by \a height pixel ellipse, centered at + (0, 0) on \a canvas. +*/ +QtCanvasEllipse::QtCanvasEllipse(int width, int height, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height), + a1(0), a2(360*16) +{ +} + +// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars +// ### it's how QPainter does it, so QtCanvas does too for consistency. If it's +// ### a good idea, it should be added to QPainter, not just to QtCanvas. Warwick +/* + Constructs a \a width by \a height pixel ellipse, centered at + (0, 0) on \a canvas. Only a segment of the ellipse is drawn, + starting at angle \a startangle, and extending for angle \a angle + (the angle length). + + Note that angles are specified in sixteenths of a degree. +*/ +QtCanvasEllipse::QtCanvasEllipse(int width, int height, + int startangle, int angle, QtCanvas* canvas) : + QtCanvasPolygonalItem(canvas), + w(width), h(height), + a1(startangle), a2(angle) +{ +} + +/* + Destroys the ellipse. +*/ +QtCanvasEllipse::~QtCanvasEllipse() +{ + hide(); +} + +/* + Returns the width of the ellipse. +*/ +int QtCanvasEllipse::width() const +{ + return w; +} + +/* + Returns the height of the ellipse. +*/ +int QtCanvasEllipse::height() const +{ + return h; +} + +/* + Sets the \a width and \a height of the ellipse. +*/ +void QtCanvasEllipse::setSize(int width, int height) +{ + if (w != width || h != height) { + removeFromChunks(); + w = width; + h = height; + addToChunks(); + } +} + +/* + \fn int QtCanvasEllipse::angleStart() const + + Returns the start angle in 16ths of a degree. Initially + this will be 0. + + \sa setAngles(), angleLength() +*/ + +/* + \fn int QtCanvasEllipse::angleLength() const + + Returns the length angle (the extent of the ellipse segment) in + 16ths of a degree. Initially this will be 360 * 16 (a complete + ellipse). + + \sa setAngles(), angleStart() +*/ + +/* + Sets the angles for the ellipse. The start angle is \a start and + the extent of the segment is \a length (the angle length) from the + \a start. The angles are specified in 16ths of a degree. By + default the ellipse will start at 0 and have an angle length of + 360 * 16 (a complete ellipse). + + \sa angleStart(), angleLength() +*/ +void QtCanvasEllipse::setAngles(int start, int length) +{ + if (a1 != start || a2 != length) { + removeFromChunks(); + a1 = start; + a2 = length; + addToChunks(); + } +} + +/* + \reimp +*/ +QPolygon QtCanvasEllipse::areaPoints() const +{ + QPainterPath path; + path.arcTo(QRectF(x()-w/2.0+0.5-1, y()-h/2.0+0.5-1, w+3, h+3), a1/16., a2/16.); + return path.toFillPolygon().toPolygon(); +} + +/* + Draws the ellipse, centered at x(), y() using the painter \a p. + + Note that QtCanvasEllipse does not support an outline (the pen is + always NoPen). +*/ +void QtCanvasEllipse::drawShape(QPainter & p) +{ + p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-: + if (!a1 && a2 == 360*16) { + p.drawEllipse(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h); + } else { + p.drawPie(int(x()-w/2.0+0.5), int(y()-h/2.0+0.5), w, h, a1, a2); + } +} + + +/* + \class QtCanvasText + \brief The QtCanvasText class provides a text object on a QtCanvas. + + A canvas text item has text with font, color and alignment + attributes. The text and font can be set in the constructor or set + or changed later with setText() and setFont(). The color is set + with setColor() and the alignment with setTextFlags(). The text + item's bounding rectangle is retrieved with boundingRect(). + + The text can be drawn on a painter with draw(). + + Like any other canvas item text items can be moved with + QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting + coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and + QtCanvasItem::setZ(). +*/ + +/* + Constructs a QtCanvasText with the text "\", on \a canvas. +*/ +QtCanvasText::QtCanvasText(QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(""), flags(0) +{ + setRect(); +} + +// ### add textflags to the constructor? Lars +/* + Constructs a QtCanvasText with the text \a t, on canvas \a canvas. +*/ +QtCanvasText::QtCanvasText(const QString& t, QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(t), flags(0) +{ + setRect(); +} + +// ### see above +/* + Constructs a QtCanvasText with the text \a t and font \a f, on the + canvas \a canvas. +*/ +QtCanvasText::QtCanvasText(const QString& t, QFont f, QtCanvas* canvas) : + QtCanvasItem(canvas), + txt(t), flags(0), + fnt(f) +{ + setRect(); +} + +/* + Destroys the canvas text item. +*/ +QtCanvasText::~QtCanvasText() +{ + removeFromChunks(); +} + +/* + Returns the bounding rectangle of the text. +*/ +QRect QtCanvasText::boundingRect() const { return brect; } + +void QtCanvasText::setRect() +{ + brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt); +} + +/* + \fn int QtCanvasText::textFlags() const + + Returns the currently set alignment flags. + + \sa setTextFlags() Qt::AlignmentFlag Qt::TextFlag +*/ + + +/* + Sets the alignment flags to \a f. These are a bitwise OR of the + flags available to QPainter::drawText() -- see the + \l{Qt::AlignmentFlag}s and \l{Qt::TextFlag}s. + + \sa setFont() setColor() +*/ +void QtCanvasText::setTextFlags(int f) +{ + if (flags != f) { + removeFromChunks(); + flags = f; + setRect(); + addToChunks(); + } +} + +/* + Returns the text item's text. + + \sa setText() +*/ +QString QtCanvasText::text() const +{ + return txt; +} + + +/* + Sets the text item's text to \a t. The text may contain newlines. + + \sa text(), setFont(), setColor() setTextFlags() +*/ +void QtCanvasText::setText(const QString& t) +{ + if (txt != t) { + removeFromChunks(); + txt = t; + setRect(); + addToChunks(); + } +} + +/* + Returns the font in which the text is drawn. + + \sa setFont() +*/ +QFont QtCanvasText::font() const +{ + return fnt; +} + +/* + Sets the font in which the text is drawn to font \a f. + + \sa font() +*/ +void QtCanvasText::setFont(const QFont& f) +{ + if (f != fnt) { + removeFromChunks(); + fnt = f; + setRect(); + addToChunks(); + } +} + +/* + Returns the color of the text. + + \sa setColor() +*/ +QColor QtCanvasText::color() const +{ + return col; +} + +/* + Sets the color of the text to the color \a c. + + \sa color(), setFont() +*/ +void QtCanvasText::setColor(const QColor& c) +{ + col = c; + changeChunks(); +} + + +/* + \reimp +*/ +void QtCanvasText::moveBy(double dx, double dy) +{ + int idx = int(x()+dx)-int(x()); + int idy = int(y()+dy)-int(y()); + if (idx || idy) { + removeFromChunks(); + } + myx+= dx; + myy+= dy; + if (idx || idy) { + brect.translate(idx, idy); + addToChunks(); + } +} + +/* + Draws the text using the painter \a painter. +*/ +void QtCanvasText::draw(QPainter& painter) +{ + painter.setFont(fnt); + painter.setPen(col); + painter.drawText(painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt); +} + +/* + \reimp +*/ +void QtCanvasText::changeChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->setChangedChunk(i, j); + } + } + } +} + +/* + Adds the text item to the appropriate chunks. +*/ +void QtCanvasText::addToChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->addItemToChunk(this, i, j); + } + } + } +} + +/* + Removes the text item from the appropriate chunks. +*/ +void QtCanvasText::removeFromChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = brect.top()/chunksize; j <= brect.bottom()/chunksize; j++) { + for (int i = brect.left()/chunksize; i <= brect.right()/chunksize; i++) { + canvas()->removeItemFromChunk(this, i, j); + } + } + } +} + + +/* + Returns 0 (QtCanvasItem::Rtti_Item). + + Make your derived classes return their own values for rtti(), so + that you can distinguish between objects returned by + QtCanvas::at(). You should use values greater than 1000 to allow + for extensions to this class. + + Overuse of this functionality can damage its extensibility. For + example, once you have identified a base class of a QtCanvasItem + found by QtCanvas::at(), cast it to that type and call meaningful + methods rather than acting upon the object based on its rtti + value. + + For example: + + \code + QtCanvasItem* item; + // Find an item, e.g. with QtCanvasItem::collisions(). + ... + if (item->rtti() == MySprite::RTTI) { + MySprite* s = (MySprite*)item; + if (s->isDamagable()) s->loseHitPoints(1000); + if (s->isHot()) myself->loseHitPoints(1000); + ... + } + \endcode +*/ +int QtCanvasItem::rtti() const { return RTTI; } +int QtCanvasItem::RTTI = Rtti_Item; + +/* + Returns 1 (QtCanvasItem::Rtti_Sprite). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasSprite::rtti() const { return RTTI; } +int QtCanvasSprite::RTTI = Rtti_Sprite; + +/* + Returns 2 (QtCanvasItem::Rtti_PolygonalItem). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasPolygonalItem::rtti() const { return RTTI; } +int QtCanvasPolygonalItem::RTTI = Rtti_PolygonalItem; + +/* + Returns 3 (QtCanvasItem::Rtti_Text). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasText::rtti() const { return RTTI; } +int QtCanvasText::RTTI = Rtti_Text; + +/* + Returns 4 (QtCanvasItem::Rtti_Polygon). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasPolygon::rtti() const { return RTTI; } +int QtCanvasPolygon::RTTI = Rtti_Polygon; + +/* + Returns 5 (QtCanvasItem::Rtti_Rectangle). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasRectangle::rtti() const { return RTTI; } +int QtCanvasRectangle::RTTI = Rtti_Rectangle; + +/* + Returns 6 (QtCanvasItem::Rtti_Ellipse). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasEllipse::rtti() const { return RTTI; } +int QtCanvasEllipse::RTTI = Rtti_Ellipse; + +/* + Returns 7 (QtCanvasItem::Rtti_Line). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasLine::rtti() const { return RTTI; } +int QtCanvasLine::RTTI = Rtti_Line; + +/* + Returns 8 (QtCanvasItem::Rtti_Spline). + + \sa QtCanvasItem::rtti() +*/ +int QtCanvasSpline::rtti() const { return RTTI; } +int QtCanvasSpline::RTTI = Rtti_Spline; + +/* + Constructs a QtCanvasSprite which uses images from the + QtCanvasPixmapArray \a a. + + The sprite in initially positioned at (0, 0) on \a canvas, using + frame 0. +*/ +QtCanvasSprite::QtCanvasSprite(QtCanvasPixmapArray* a, QtCanvas* canvas) : + QtCanvasItem(canvas), + frm(0), + anim_val(0), + anim_state(0), + anim_type(0), + images(a) +{ +} + + +/* + Set the array of images used for displaying the sprite to the + QtCanvasPixmapArray \a a. + + If the current frame() is larger than the number of images in \a + a, the current frame will be reset to 0. +*/ +void QtCanvasSprite::setSequence(QtCanvasPixmapArray* a) +{ + bool isvisible = isVisible(); + if (isvisible && images) + hide(); + images = a; + if (frm >= (int)images->count()) + frm = 0; + if (isvisible) + show(); +} + +/* +\internal + +Marks any chunks the sprite touches as changed. +*/ +void QtCanvasSprite::changeChunks() +{ + if (isVisible() && canvas()) { + int chunksize = canvas()->chunkSize(); + for (int j = topEdge()/chunksize; j <= bottomEdge()/chunksize; j++) { + for (int i = leftEdge()/chunksize; i <= rightEdge()/chunksize; i++) { + canvas()->setChangedChunk(i, j); + } + } + } +} + +/* + Destroys the sprite and removes it from the canvas. Does \e not + delete the images. +*/ +QtCanvasSprite::~QtCanvasSprite() +{ + removeFromChunks(); +} + +/* + Sets the animation frame used for displaying the sprite to \a f, + an index into the QtCanvasSprite's QtCanvasPixmapArray. The call + will be ignored if \a f is larger than frameCount() or smaller + than 0. + + \sa frame() move() +*/ +void QtCanvasSprite::setFrame(int f) +{ + move(x(), y(), f); +} + +/* + \enum QtCanvasSprite::FrameAnimationType + + This enum is used to identify the different types of frame + animation offered by QtCanvasSprite. + + \value Cycle at each advance the frame number will be incremented by + 1 (modulo the frame count). + \value Oscillate at each advance the frame number will be + incremented by 1 up to the frame count then decremented to by 1 to + 0, repeating this sequence forever. +*/ + +/* + Sets the animation characteristics for the sprite. + + For \a type == \c Cycle, the frames will increase by \a step + at each advance, modulo the frameCount(). + + For \a type == \c Oscillate, the frames will increase by \a step + at each advance, up to the frameCount(), then decrease by \a step + back to 0, repeating forever. + + The \a state parameter is for internal use. +*/ +void QtCanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state) +{ + anim_val = step; + anim_type = type; + anim_state = state; + setAnimated(true); +} + +/* + Extends the default QtCanvasItem implementation to provide the + functionality of setFrameAnimation(). + + The \a phase is 0 or 1: see QtCanvasItem::advance() for details. + + \sa QtCanvasItem::advance() setVelocity() +*/ +void QtCanvasSprite::advance(int phase) +{ + if (phase == 1) { + int nf = frame(); + if (anim_type == Oscillate) { + if (anim_state) + nf += anim_val; + else + nf -= anim_val; + if (nf < 0) { + nf = abs(anim_val); + anim_state = !anim_state; + } else if (nf >= frameCount()) { + nf = frameCount()-1-abs(anim_val); + anim_state = !anim_state; + } + } else { + nf = (nf + anim_val + frameCount()) % frameCount(); + } + move(x()+xVelocity(), y()+yVelocity(), nf); + } +} + + +/* + \fn int QtCanvasSprite::frame() const + + Returns the index of the current animation frame in the + QtCanvasSprite's QtCanvasPixmapArray. + + \sa setFrame(), move() +*/ + +/* + \fn int QtCanvasSprite::frameCount() const + + Returns the number of frames in the QtCanvasSprite's + QtCanvasPixmapArray. +*/ + + +/* + Moves the sprite to (\a x, \a y). +*/ +void QtCanvasSprite::move(double x, double y) { QtCanvasItem::move(x, y); } + +/* + \fn void QtCanvasSprite::move(double nx, double ny, int nf) + + Moves the sprite to (\a nx, \a ny) and sets the current + frame to \a nf. \a nf will be ignored if it is larger than + frameCount() or smaller than 0. +*/ +void QtCanvasSprite::move(double nx, double ny, int nf) +{ + if (isVisible() && canvas()) { + hide(); + QtCanvasItem::move(nx, ny); + if (nf >= 0 && nf < frameCount()) + frm = nf; + show(); + } else { + QtCanvasItem::move(nx, ny); + if (nf >= 0 && nf < frameCount()) + frm = nf; + } +} + + +class QPoint; + +class QtPolygonScanner { +public: + virtual ~QtPolygonScanner() {} + void scan(const QPolygon& pa, bool winding, int index = 0, int npoints = -1); + void scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable); + enum Edge { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + void scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges); + virtual void processSpans(int n, QPoint* point, int* width) = 0; +}; + + +// Based on Xserver code miFillGeneralPoly... +/* + * + * Written by Brian Kelleher; Oct. 1985 + * + * Routine to fill a polygon. Two fill rules are + * supported: frWINDING and frEVENODD. + * + * See fillpoly.h for a complete description of the algorithm. + */ + +/* + * These are the data structures needed to scan + * convert regions. Two different scan conversion + * methods are available -- the even-odd method, and + * the winding number method. + * The even-odd rule states that a point is inside + * the polygon if a ray drawn from that point in any + * direction will pass through an odd number of + * path segments. + * By the winding number rule, a point is decided + * to be inside the polygon if a ray drawn from that + * point in any direction passes through a different + * number of clockwise and counterclockwise path + * segments. + * + * These data structures are adapted somewhat from + * the algorithm in (Foley/Van Dam) for scan converting + * polygons. + * The basic algorithm is to start at the top (smallest y) + * of the polygon, stepping down to the bottom of + * the polygon by incrementing the y coordinate. We + * keep a list of edges which the current scanline crosses, + * sorted by x. This list is called the Active Edge Table (AET) + * As we change the y-coordinate, we update each entry in + * in the active edge table to reflect the edges new xcoord. + * This list must be sorted at each scanline in case + * two edges intersect. + * We also keep a data structure known as the Edge Table (ET), + * which keeps track of all the edges which the current + * scanline has not yet reached. The ET is basically a + * list of ScanLineList structures containing a list of + * edges which are entered at a given scanline. There is one + * ScanLineList per scanline at which an edge is entered. + * When we enter a new edge, we move it from the ET to the AET. + * + * From the AET, we can implement the even-odd rule as in + * (Foley/Van Dam). + * The winding number rule is a little trickier. We also + * keep the EdgeTableEntries in the AET linked by the + * nextWETE (winding EdgeTableEntry) link. This allows + * the edges to be linked just as before for updating + * purposes, but only uses the edges linked by the nextWETE + * link as edges representing spans of the polygon to + * drawn (as with the even-odd rule). + */ + +/* $XConsortium: miscanfill.h, v 1.5 94/04/17 20:27:50 dpw Exp $ */ +/* + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + + +/* + * scanfill.h + * + * Written by Brian Kelleher; Jan 1985 + * + * This file contains a few macros to help track + * the edge of a filled object. The object is assumed + * to be filled in scanline order, and thus the + * algorithm used is an extension of Bresenham's line + * drawing algorithm which assumes that y is always the + * major axis. + * Since these pieces of code are the same for any filled shape, + * it is more convenient to gather the library in one + * place, but since these pieces of code are also in + * the inner loops of output primitives, procedure call + * overhead is out of the question. + * See the author for a derivation if needed. + */ + +/* + * In scan converting polygons, we want to choose those pixels + * which are inside the polygon. Thus, we add .5 to the starting + * x coordinate for both left and right edges. Now we choose the + * first pixel which is inside the pgon for the left edge and the + * first pixel which is outside the pgon for the right edge. + * Draw the left pixel, but not the right. + * + * How to add .5 to the starting x coordinate: + * If the edge is moving to the right, then subtract dy from the + * error term from the general form of the algorithm. + * If the edge is moving to the left, then add dy to the error term. + * + * The reason for the difference between edges moving to the left + * and edges moving to the right is simple: If an edge is moving + * to the right, then we want the algorithm to flip immediately. + * If it is moving to the left, then we don't want it to flip until + * we traverse an entire pixel. + */ +#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) { \ + int dx; /* local storage */ \ +\ + /* \ + * if the edge is horizontal, then it is ignored \ + * and assumed not to be processed. Otherwise, do this stuff. \ + */ \ + if ((dy) != 0) { \ + xStart = (x1); \ + dx = (x2) - xStart; \ + if (dx < 0) { \ + m = dx / (dy); \ + m1 = m - 1; \ + incr1 = -2 * dx + 2 * (dy) * m1; \ + incr2 = -2 * dx + 2 * (dy) * m; \ + d = 2 * m * (dy) - 2 * dx - 2 * (dy); \ + } else { \ + m = dx / (dy); \ + m1 = m + 1; \ + incr1 = 2 * dx - 2 * (dy) * m1; \ + incr2 = 2 * dx - 2 * (dy) * m; \ + d = -2 * m * (dy) + 2 * dx; \ + } \ + } \ +} + +#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) { \ + if (m1 > 0) { \ + if (d > 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } else {\ + if (d >= 0) { \ + minval += m1; \ + d += incr1; \ + } \ + else { \ + minval += m; \ + d += incr2; \ + } \ + } \ +} + + +/* + * This structure contains all of the information needed + * to run the bresenham algorithm. + * The variables may be hardcoded into the declarations + * instead of using this structure to make use of + * register declarations. + */ +typedef struct { + int minor; /* minor axis */ + int d; /* decision variable */ + int m, m1; /* slope and slope+1 */ + int incr1, incr2; /* error increments */ +} BRESINFO; + + +#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \ + BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, \ + bres.m, bres.m1, bres.incr1, bres.incr2) + +#define BRESINCRPGONSTRUCT(bres) \ + BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2) + + +typedef struct _EdgeTableEntry { + int ymax; /* ycoord at which we exit this edge. */ + BRESINFO bres; /* Bresenham info to run the edge */ + struct _EdgeTableEntry *next; /* next in the list */ + struct _EdgeTableEntry *back; /* for insertion sort */ + struct _EdgeTableEntry *nextWETE; /* for winding num rule */ + int ClockWise; /* flag for winding number rule */ +} EdgeTableEntry; + + +typedef struct _ScanLineList{ + int scanline; /* the scanline represented */ + EdgeTableEntry *edgelist; /* header node */ + struct _ScanLineList *next; /* next in the list */ +} ScanLineList; + + +typedef struct { + int ymax; /* ymax for the polygon */ + int ymin; /* ymin for the polygon */ + ScanLineList scanlines; /* header node */ +} EdgeTable; + + +/* + * Here is a struct to help with storage allocation + * so we can allocate a big chunk at a time, and then take + * pieces from this heap when we need to. + */ +#define SLLSPERBLOCK 25 + +typedef struct _ScanLineListBlock { + ScanLineList SLLs[SLLSPERBLOCK]; + struct _ScanLineListBlock *next; +} ScanLineListBlock; + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * + * a few macros for the inner loops of the fill code where + * performance considerations don't allow a procedure call. + * + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The winding number rule is in effect, so we must notify + * the caller when the edge has been removed so he + * can reorder the Winding Active Edge Table. + */ +#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + fixWAET = 1; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres); \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + + +/* + * Evaluate the given edge at the given scanline. + * If the edge has expired, then we leave it and fix up + * the active edge table; otherwise, we increment the + * x value to be ready for the next scanline. + * The even-odd rule is in effect. + */ +#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) { \ + if (pAET->ymax == y) { /* leaving this edge */ \ + pPrevAET->next = pAET->next; \ + pAET = pPrevAET->next; \ + if (pAET) \ + pAET->back = pPrevAET; \ + } \ + else { \ + BRESINCRPGONSTRUCT(pAET->bres) \ + pPrevAET = pAET; \ + pAET = pAET->next; \ + } \ +} + +/*********************************************************** + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#define MAXINT 0x7fffffff +#define MININT -MAXINT + +/* + * fillUtils.c + * + * Written by Brian Kelleher; Oct. 1985 + * + * This module contains all of the utility functions + * needed to scan convert a polygon. + * + */ +/* + * InsertEdgeInET + * + * Insert the given edge into the edge table. + * First we must find the correct bucket in the + * Edge table, then find the right slot in the + * bucket. Finally, we can insert it. + * + */ +static bool +miInsertEdgeInET(EdgeTable *ET, EdgeTableEntry *ETE, + int scanline, ScanLineListBlock **SLLBlock, int *iSLLBlock) +{ + register EdgeTableEntry *start, *prev; + register ScanLineList *pSLL, *pPrevSLL; + ScanLineListBlock *tmpSLLBlock; + + /* + * find the right bucket to put the edge into + */ + pPrevSLL = &ET->scanlines; + pSLL = pPrevSLL->next; + while (pSLL && (pSLL->scanline < scanline)) + { + pPrevSLL = pSLL; + pSLL = pSLL->next; + } + + /* + * reassign pSLL (pointer to ScanLineList) if necessary + */ + if ((!pSLL) || (pSLL->scanline > scanline)) + { + if (*iSLLBlock > SLLSPERBLOCK-1) + { + tmpSLLBlock = + (ScanLineListBlock *)malloc(sizeof(ScanLineListBlock)); + if (!tmpSLLBlock) + return false; + (*SLLBlock)->next = tmpSLLBlock; + tmpSLLBlock->next = 0; + *SLLBlock = tmpSLLBlock; + *iSLLBlock = 0; + } + pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]); + + pSLL->next = pPrevSLL->next; + pSLL->edgelist = 0; + pPrevSLL->next = pSLL; + } + pSLL->scanline = scanline; + + /* + * now insert the edge in the right bucket + */ + prev = 0; + start = pSLL->edgelist; + while (start && (start->bres.minor < ETE->bres.minor)) + { + prev = start; + start = start->next; + } + ETE->next = start; + + if (prev) + prev->next = ETE; + else + pSLL->edgelist = ETE; + return true; +} + +/* + * CreateEdgeTable + * + * This routine creates the edge table for + * scan converting polygons. + * The Edge Table (ET) looks like: + * + * EdgeTable + * -------- + * | ymax | ScanLineLists + * |scanline|-->------------>-------------->... + * -------- |scanline| |scanline| + * |edgelist| |edgelist| + * --------- --------- + * | | + * | | + * V V + * list of ETEs list of ETEs + * + * where ETE is an EdgeTableEntry data structure, + * and there is one ScanLineList per scanline at + * which an edge is initially entered. + * + */ + +typedef struct { +#if defined(Q_OS_MAC) + int y, x; +#else + int x, y; +#endif + +} DDXPointRec, *DDXPointPtr; + +/* + * Clean up our act. + */ +static void +miFreeStorage(ScanLineListBlock *pSLLBlock) +{ + register ScanLineListBlock *tmpSLLBlock; + + while (pSLLBlock) + { + tmpSLLBlock = pSLLBlock->next; + free(pSLLBlock); + pSLLBlock = tmpSLLBlock; + } +} + +static bool +miCreateETandAET(int count, DDXPointPtr pts, EdgeTable *ET, + EdgeTableEntry *AET, EdgeTableEntry *pETEs, ScanLineListBlock *pSLLBlock) +{ + register DDXPointPtr top, bottom; + register DDXPointPtr PrevPt, CurrPt; + int iSLLBlock = 0; + + int dy; + + if (count < 2) return true; + + /* + * initialize the Active Edge Table + */ + AET->next = 0; + AET->back = 0; + AET->nextWETE = 0; + AET->bres.minor = MININT; + + /* + * initialize the Edge Table. + */ + ET->scanlines.next = 0; + ET->ymax = MININT; + ET->ymin = MAXINT; + pSLLBlock->next = 0; + + PrevPt = &pts[count-1]; + + /* + * for each vertex in the array of points. + * In this loop we are dealing with two vertices at + * a time -- these make up one edge of the polygon. + */ + while (count--) + { + CurrPt = pts++; + + /* + * find out which point is above and which is below. + */ + if (PrevPt->y > CurrPt->y) + { + bottom = PrevPt, top = CurrPt; + pETEs->ClockWise = 0; + } + else + { + bottom = CurrPt, top = PrevPt; + pETEs->ClockWise = 1; + } + + /* + * don't add horizontal edges to the Edge table. + */ + if (bottom->y != top->y) + { + pETEs->ymax = bottom->y-1; /* -1 so we don't get last scanline */ + + /* + * initialize integer edge algorithm + */ + dy = bottom->y - top->y; + BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres) + + if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock)) + { + miFreeStorage(pSLLBlock->next); + return false; + } + + ET->ymax = qMax(ET->ymax, PrevPt->y); + ET->ymin = qMin(ET->ymin, PrevPt->y); + pETEs++; + } + + PrevPt = CurrPt; + } + return true; +} + +/* + * loadAET + * + * This routine moves EdgeTableEntries from the + * EdgeTable into the Active Edge Table, + * leaving them sorted by smaller x coordinate. + * + */ + +static void +miloadAET(EdgeTableEntry *AET, EdgeTableEntry *ETEs) +{ + register EdgeTableEntry *pPrevAET; + register EdgeTableEntry *tmp; + + pPrevAET = AET; + AET = AET->next; + while (ETEs) + { + while (AET && (AET->bres.minor < ETEs->bres.minor)) + { + pPrevAET = AET; + AET = AET->next; + } + tmp = ETEs->next; + ETEs->next = AET; + if (AET) + AET->back = ETEs; + ETEs->back = pPrevAET; + pPrevAET->next = ETEs; + pPrevAET = ETEs; + + ETEs = tmp; + } +} + +/* + * computeWAET + * + * This routine links the AET by the + * nextWETE (winding EdgeTableEntry) link for + * use by the winding number rule. The final + * Active Edge Table (AET) might look something + * like: + * + * AET + * ---------- --------- --------- + * |ymax | |ymax | |ymax | + * | ... | |... | |... | + * |next |->|next |->|next |->... + * |nextWETE| |nextWETE| |nextWETE| + * --------- --------- ^-------- + * | | | + * V-------------------> V---> ... + * + */ +static void +micomputeWAET(EdgeTableEntry *AET) +{ + register EdgeTableEntry *pWETE; + register int inside = 1; + register int isInside = 0; + + AET->nextWETE = 0; + pWETE = AET; + AET = AET->next; + while (AET) + { + if (AET->ClockWise) + isInside++; + else + isInside--; + + if ((!inside && !isInside) || + (inside && isInside)) + { + pWETE->nextWETE = AET; + pWETE = AET; + inside = !inside; + } + AET = AET->next; + } + pWETE->nextWETE = 0; +} + +/* + * InsertionSort + * + * Just a simple insertion sort using + * pointers and back pointers to sort the Active + * Edge Table. + * + */ + +static int +miInsertionSort(EdgeTableEntry *AET) +{ + register EdgeTableEntry *pETEchase; + register EdgeTableEntry *pETEinsert; + register EdgeTableEntry *pETEchaseBackTMP; + register int changed = 0; + + AET = AET->next; + while (AET) + { + pETEinsert = AET; + pETEchase = AET; + while (pETEchase->back->bres.minor > AET->bres.minor) + pETEchase = pETEchase->back; + + AET = AET->next; + if (pETEchase != pETEinsert) + { + pETEchaseBackTMP = pETEchase->back; + pETEinsert->back->next = AET; + if (AET) + AET->back = pETEinsert->back; + pETEinsert->next = pETEchase; + pETEchase->back->next = pETEinsert; + pETEchase->back = pETEinsert; + pETEinsert->back = pETEchaseBackTMP; + changed = 1; + } + } + return changed; +} + +/* + \overload +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints) +{ + scan(pa, winding, index, npoints, true); +} + +/* + \overload + + If \a stitchable is false, the right and bottom edges of the + polygon are included. This causes adjacent polygons to overlap. +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable) +{ + scan(pa, winding, index, npoints, + stitchable ? Edge(Left+Top) : Edge(Left+Right+Top+Bottom)); +} + +/* + Calls processSpans() for all scanlines of the polygon defined by + \a npoints starting at \a index in \a pa. + + If \a winding is true, the Winding algorithm rather than the + Odd-Even rule is used. + + The \a edges is any bitwise combination of: + \list + \i QtPolygonScanner::Left + \i QtPolygonScanner::Right + \i QtPolygonScanner::Top + \i QtPolygonScanner::Bottom + \endlist + \a edges determines which edges are included. + + \warning The edges feature does not work properly. + +*/ +void QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges) +{ + + + DDXPointPtr ptsIn = (DDXPointPtr)pa.data(); + ptsIn += index; + register EdgeTableEntry *pAET; /* the Active Edge Table */ + register int y; /* the current scanline */ + register int nPts = 0; /* number of pts in buffer */ + register EdgeTableEntry *pWETE; /* Winding Edge Table */ + register ScanLineList *pSLL; /* Current ScanLineList */ + register DDXPointPtr ptsOut; /* ptr to output buffers */ + int *width; + DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */ + int FirstWidth[NUMPTSTOBUFFER]; + EdgeTableEntry *pPrevAET; /* previous AET entry */ + EdgeTable ET; /* Edge Table header node */ + EdgeTableEntry AET; /* Active ET header node */ + EdgeTableEntry *pETEs; /* Edge Table Entries buff */ + ScanLineListBlock SLLBlock; /* header for ScanLineList */ + int fixWAET = 0; + int edge_l = (edges & Left) ? 1 : 0; + int edge_r = (edges & Right) ? 1 : 0; + int edge_t = 1; //#### (edges & Top) ? 1 : 0; + int edge_b = (edges & Bottom) ? 1 : 0; + + if (npoints == -1) + npoints = pa.size(); + + if (npoints < 3) + return; + + if(!(pETEs = (EdgeTableEntry *) + malloc(sizeof(EdgeTableEntry) * npoints))) + return; + ptsOut = FirstPoint; + width = FirstWidth; + if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock)) + { + free(pETEs); + return; + } + pSLL = ET.scanlines.next; + + if (!winding) + { + /* + * for each scanline + */ + for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + + /* + * for each active edge + */ + while (pAET) + { + ptsOut->x = pAET->bres.minor + 1 - edge_l; + ptsOut++->y = y; + *width++ = pAET->next->bres.minor - pAET->bres.minor + - 1 + edge_l + edge_r; + nPts++; + + /* + * send out the buffer when its full + */ + if (nPts == NUMPTSTOBUFFER) + { + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + EVALUATEEDGEEVENODD(pAET, pPrevAET, y) + } + miInsertionSort(&AET); + } + } + else /* default to WindingNumber */ + { + /* + * for each scanline + */ + for (y = ET.ymin+1-edge_t; y < ET.ymax+edge_b; y++) + { + /* + * Add a new edge to the active edge table when we + * get to the next edge. + */ + if (pSLL && y == pSLL->scanline) + { + miloadAET(&AET, pSLL->edgelist); + micomputeWAET(&AET); + pSLL = pSLL->next; + } + pPrevAET = &AET; + pAET = AET.next; + pWETE = pAET; + + /* + * for each active edge + */ + while (pAET) + { + /* + * if the next edge in the active edge table is + * also the next edge in the winding active edge + * table. + */ + if (pWETE == pAET) + { + ptsOut->x = pAET->bres.minor + 1 - edge_l; + ptsOut++->y = y; + *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r; + nPts++; + + /* + * send out the buffer + */ + if (nPts == NUMPTSTOBUFFER) + { + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + ptsOut = FirstPoint; + width = FirstWidth; + nPts = 0; + } + + pWETE = pWETE->nextWETE; + while (pWETE != pAET) { + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) + } + pWETE = pWETE->nextWETE; + } + EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) + } + + /* + * reevaluate the Winding active edge table if we + * just had to resort it or if we just exited an edge. + */ + if (miInsertionSort(&AET) || fixWAET) + { + micomputeWAET(&AET); + fixWAET = 0; + } + } + } + + /* + * Get any spans that we missed by buffering + */ + + + processSpans(nPts, (QPoint*)FirstPoint, FirstWidth); + free(pETEs); + miFreeStorage(SLLBlock.next); +} +/***** END OF X11-based CODE *****/ + + + + + +class QtCanvasPolygonScanner : public QtPolygonScanner { + QPolygonalProcessor& processor; +public: + QtCanvasPolygonScanner(QPolygonalProcessor& p) : + processor(p) + { + } + void processSpans(int n, QPoint* point, int* width) + { + processor.doSpans(n, point, width); + } +}; + +void QtCanvasPolygonalItem::scanPolygon(const QPolygon& pa, int winding, QPolygonalProcessor& process) const +{ + QtCanvasPolygonScanner scanner(process); + scanner.scan(pa, winding); +} diff --git a/examples/canvas_variant/qtcanvas.h b/examples/canvas_variant/qtcanvas.h new file mode 100644 index 0000000..e6d90db --- /dev/null +++ b/examples/canvas_variant/qtcanvas.h @@ -0,0 +1,778 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#ifndef QTCANVAS_H +#define QTCANVAS_H + +#include +#include +#include +#include +#include + +class QtCanvasSprite; +class QtCanvasPolygonalItem; +class QtCanvasRectangle; +class QtCanvasPolygon; +class QtCanvasEllipse; +class QtCanvasText; +class QtCanvasLine; +class QtCanvasChunk; +class QtCanvas; +class QtCanvasItem; +class QtCanvasView; +class QtCanvasPixmap; + +typedef QList QtCanvasItemList; + + +class QtCanvasItemExtra; + +class QtCanvasItem +{ +public: + QtCanvasItem(QtCanvas* canvas); + virtual ~QtCanvasItem(); + + double x() const + { return myx; } + double y() const + { return myy; } + double z() const + { return myz; } // (depth) + + virtual void moveBy(double dx, double dy); + void move(double x, double y); + void setX(double a) { move(a,y()); } + void setY(double a) { move(x(),a); } + void setZ(double a) { myz=a; changeChunks(); } + + bool animated() const; + virtual void setAnimated(bool y); + virtual void setVelocity(double vx, double vy); + void setXVelocity(double vx) { setVelocity(vx,yVelocity()); } + void setYVelocity(double vy) { setVelocity(xVelocity(),vy); } + double xVelocity() const; + double yVelocity() const; + virtual void advance(int stage); + + virtual bool collidesWith(const QtCanvasItem*) const=0; + + QtCanvasItemList collisions(bool exact /* NO DEFAULT */) const; + + virtual void setCanvas(QtCanvas*); + + virtual void draw(QPainter&)=0; + + void show(); + void hide(); + + virtual void setVisible(bool yes); + bool isVisible() const + { return (bool)vis; } + virtual void setSelected(bool yes); + bool isSelected() const + { return (bool)sel; } + virtual void setEnabled(bool yes); + bool isEnabled() const + { return (bool)ena; } + virtual void setActive(bool yes); + bool isActive() const + { return (bool)act; } + bool visible() const + { return (bool)vis; } + bool selected() const + { return (bool)sel; } + bool enabled() const + { return (bool)ena; } + bool active() const + { return (bool)act; } + + enum RttiValues { + Rtti_Item = 0, + Rtti_Sprite = 1, + Rtti_PolygonalItem = 2, + Rtti_Text = 3, + Rtti_Polygon = 4, + Rtti_Rectangle = 5, + Rtti_Ellipse = 6, + Rtti_Line = 7, + Rtti_Spline = 8 + }; + + virtual int rtti() const; + static int RTTI; + + virtual QRect boundingRect() const=0; + virtual QRect boundingRectAdvanced() const; + + QtCanvas* canvas() const + { return cnv; } + +protected: + void update() { changeChunks(); } + +private: + // For friendly subclasses... + + friend class QtCanvasPolygonalItem; + friend class QtCanvasSprite; + friend class QtCanvasRectangle; + friend class QtCanvasPolygon; + friend class QtCanvasEllipse; + friend class QtCanvasText; + friend class QtCanvasLine; + + virtual QPolygon chunks() const; + virtual void addToChunks(); + virtual void removeFromChunks(); + virtual void changeChunks(); + virtual bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const = 0; + // End of friend stuff + + QtCanvas* cnv; + static QtCanvas* current_canvas; + double myx,myy,myz; + QtCanvasItemExtra *ext; + QtCanvasItemExtra& extra(); + uint ani:1; + uint vis:1; + uint val:1; + uint sel:1; + uint ena:1; + uint act:1; +}; + + +class QtCanvasData; + +class QtCanvas : public QObject +{ + Q_OBJECT +public: + QtCanvas(QObject* parent = 0); + QtCanvas(int w, int h); + QtCanvas(QPixmap p, int h, int v, int tilewidth, int tileheight); + + virtual ~QtCanvas(); + + virtual void setTiles(QPixmap tiles, int h, int v, + int tilewidth, int tileheight); + virtual void setBackgroundPixmap(const QPixmap& p); + QPixmap backgroundPixmap() const; + + virtual void setBackgroundColor(const QColor& c); + QColor backgroundColor() const; + + virtual void setTile(int x, int y, int tilenum); + int tile(int x, int y) const + { return grid[x+y*htiles]; } + + int tilesHorizontally() const + { return htiles; } + int tilesVertically() const + { return vtiles; } + + int tileWidth() const + { return tilew; } + int tileHeight() const + { return tileh; } + + virtual void resize(int width, int height); + int width() const + { return awidth; } + int height() const + { return aheight; } + QSize size() const + { return QSize(awidth,aheight); } + QRect rect() const + { return QRect(0, 0, awidth, aheight); } + bool onCanvas(int x, int y) const + { return x>=0 && y>=0 && x=0 && y>=0 && x &pixmaps, const QPolygon &hotspots = QPolygon()); + ~QtCanvasPixmapArray(); + +#ifndef QT_NO_IMAGEIO + bool readPixmaps(const QString& datafilenamepattern, int framecount=0); + bool readCollisionMasks(const QString& filenamepattern); +#endif + + // deprecated + bool operator!(); // Failure check. + bool isValid() const; + + QtCanvasPixmap* image(int i) const + { return img ? img[i] : 0; } + void setImage(int i, QtCanvasPixmap* p); + uint count() const + { return (uint)framecount; } + +private: + Q_DISABLE_COPY(QtCanvasPixmapArray) + +#ifndef QT_NO_IMAGEIO + bool readPixmaps(const QString& datafilenamepattern, int framecount, bool maskonly); +#endif + + void reset(); + int framecount; + QtCanvasPixmap** img; +}; + + +class QtCanvasSprite : public QtCanvasItem +{ +public: + QtCanvasSprite(QtCanvasPixmapArray* array, QtCanvas* canvas); + + void setSequence(QtCanvasPixmapArray* seq); + + virtual ~QtCanvasSprite(); + + void move(double x, double y); + virtual void move(double x, double y, int frame); + void setFrame(int); + enum FrameAnimationType { Cycle, Oscillate }; + virtual void setFrameAnimation(FrameAnimationType=Cycle, int step=1, int state=0); + int frame() const + { return frm; } + int frameCount() const + { return images->count(); } + + int rtti() const; + static int RTTI; + + bool collidesWith(const QtCanvasItem*) const; + + QRect boundingRect() const; + + // is there a reason for these to be protected? Lars +//protected: + + int width() const; + int height() const; + + int leftEdge() const; + int topEdge() const; + int rightEdge() const; + int bottomEdge() const; + + int leftEdge(int nx) const; + int topEdge(int ny) const; + int rightEdge(int nx) const; + int bottomEdge(int ny) const; + QtCanvasPixmap* image() const + { return images->image(frm); } + virtual QtCanvasPixmap* imageAdvanced() const; + QtCanvasPixmap* image(int f) const + { return images->image(f); } + virtual void advance(int stage); + +public: + void draw(QPainter& painter); + +private: + Q_DISABLE_COPY(QtCanvasSprite) + + void addToChunks(); + void removeFromChunks(); + void changeChunks(); + + int frm; + ushort anim_val; + uint anim_state:2; + uint anim_type:14; + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + friend bool qt_testCollision(const QtCanvasSprite* s1, + const QtCanvasSprite* s2); + + QtCanvasPixmapArray* images; +}; + +class QPolygonalProcessor; + +class QtCanvasPolygonalItem : public QtCanvasItem +{ +public: + QtCanvasPolygonalItem(QtCanvas* canvas); + virtual ~QtCanvasPolygonalItem(); + + bool collidesWith(const QtCanvasItem*) const; + + virtual void setPen(QPen p); + virtual void setBrush(QBrush b); + + QPen pen() const + { return pn; } + QBrush brush() const + { return br; } + + virtual QPolygon areaPoints() const=0; + virtual QPolygon areaPointsAdvanced() const; + QRect boundingRect() const; + + int rtti() const; + static int RTTI; + +protected: + void draw(QPainter &); + virtual void drawShape(QPainter &) = 0; + + bool winding() const; + void setWinding(bool); + + void invalidate(); + bool isValid() const + { return (bool)val; } + +private: + void scanPolygon(const QPolygon& pa, int winding, + QPolygonalProcessor& process) const; + QPolygon chunks() const; + + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + QBrush br; + QPen pn; + uint wind:1; +}; + + +class QtCanvasRectangle : public QtCanvasPolygonalItem +{ +public: + QtCanvasRectangle(QtCanvas* canvas); + QtCanvasRectangle(const QRect&, QtCanvas* canvas); + QtCanvasRectangle(int x, int y, int width, int height, QtCanvas* canvas); + + ~QtCanvasRectangle(); + + int width() const; + int height() const; + void setSize(int w, int h); + QSize size() const + { return QSize(w,h); } + QPolygon areaPoints() const; + QRect rect() const + { return QRect(int(x()),int(y()),w,h); } + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + QPolygon chunks() const; + +private: + bool collidesWith( const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + + int w, h; +}; + + +class QtCanvasPolygon : public QtCanvasPolygonalItem +{ +public: + QtCanvasPolygon(QtCanvas* canvas); + ~QtCanvasPolygon(); + void setPoints(QPolygon); + QPolygon points() const; + void moveBy(double dx, double dy); + + QPolygon areaPoints() const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + QPolygon poly; +}; + + +class QtCanvasSpline : public QtCanvasPolygon +{ +public: + QtCanvasSpline(QtCanvas* canvas); + ~QtCanvasSpline(); + + void setControlPoints(QPolygon, bool closed=true); + QPolygon controlPoints() const; + bool closed() const; + + int rtti() const; + static int RTTI; + +private: + void recalcPoly(); + QPolygon bez; + bool cl; +}; + + +class QtCanvasLine : public QtCanvasPolygonalItem +{ +public: + QtCanvasLine(QtCanvas* canvas); + ~QtCanvasLine(); + void setPoints(int x1, int y1, int x2, int y2); + + QPoint startPoint() const + { return QPoint(x1,y1); } + QPoint endPoint() const + { return QPoint(x2,y2); } + + int rtti() const; + static int RTTI; + + void setPen(QPen p); + void moveBy(double dx, double dy); + +protected: + void drawShape(QPainter &); + QPolygon areaPoints() const; + +private: + int x1,y1,x2,y2; +}; + + +class QtCanvasEllipse : public QtCanvasPolygonalItem +{ + +public: + QtCanvasEllipse(QtCanvas* canvas); + QtCanvasEllipse(int width, int height, QtCanvas* canvas); + QtCanvasEllipse(int width, int height, int startangle, int angle, + QtCanvas* canvas); + + ~QtCanvasEllipse(); + + int width() const; + int height() const; + void setSize(int w, int h); + void setAngles(int start, int length); + int angleStart() const + { return a1; } + int angleLength() const + { return a2; } + QPolygon areaPoints() const; + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + void drawShape(QPainter &); + +private: + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; + int w, h; + int a1, a2; +}; + + +class QtCanvasTextExtra; + +class QtCanvasText : public QtCanvasItem +{ +public: + QtCanvasText(QtCanvas* canvas); + QtCanvasText(const QString&, QtCanvas* canvas); + QtCanvasText(const QString&, QFont, QtCanvas* canvas); + + virtual ~QtCanvasText(); + + void setText(const QString&); + void setFont(const QFont&); + void setColor(const QColor&); + QString text() const; + QFont font() const; + QColor color() const; + + void moveBy(double dx, double dy); + + int textFlags() const + { return flags; } + void setTextFlags(int); + + QRect boundingRect() const; + + bool collidesWith(const QtCanvasItem*) const; + + int rtti() const; + static int RTTI; + +protected: + virtual void draw(QPainter&); + +private: + Q_DISABLE_COPY(QtCanvasText) + + void addToChunks(); + void removeFromChunks(); + void changeChunks(); + + void setRect(); + QRect brect; + QString txt; + int flags; + QFont fnt; + QColor col; + QtCanvasTextExtra* extra; + + bool collidesWith(const QtCanvasSprite*, + const QtCanvasPolygonalItem*, + const QtCanvasRectangle*, + const QtCanvasEllipse*, + const QtCanvasText*) const; +}; + +#endif // QTCANVAS_H diff --git a/examples/decoration/CMakeLists.txt b/examples/decoration/CMakeLists.txt new file mode 100644 index 0000000..8eac86a --- /dev/null +++ b/examples/decoration/CMakeLists.txt @@ -0,0 +1,15 @@ +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(example_name decoration) + +SET(KIT_SRCS + main.cpp + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) diff --git a/examples/decoration/decoration.qdoc b/examples/decoration/decoration.qdoc new file mode 100644 index 0000000..1c60177 --- /dev/null +++ b/examples/decoration/decoration.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-decoration.html + \title Decoration Example + + This example demonstrates how to decorate the existing + QtDoublePropertyManager class with additional responsibilities. + + \image decoration.png + + It also shows how to write respective editor factory for decorated manager + by delegating common responsibilities of undecorated base manager to the aggregated + QtDoubleSpinBoxFactory member. + + The source files can be found in examples/decoration directory of the package. +*/ diff --git a/examples/decoration/main.cpp b/examples/decoration/main.cpp new file mode 100644 index 0000000..1146cd1 --- /dev/null +++ b/examples/decoration/main.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include +#include +#include "qtpropertybrowser.h" +#include "qteditorfactory.h" +#include "qttreepropertybrowser.h" + +class DecoratedDoublePropertyManager : public QtDoublePropertyManager +{ + Q_OBJECT +public: + DecoratedDoublePropertyManager(QObject *parent = 0); + ~DecoratedDoublePropertyManager(); + + QString prefix(const QtProperty *property) const; + QString suffix(const QtProperty *property) const; +public Q_SLOTS: + void setPrefix(QtProperty *property, const QString &prefix); + void setSuffix(QtProperty *property, const QString &suffix); +Q_SIGNALS: + void prefixChanged(QtProperty *property, const QString &prefix); + void suffixChanged(QtProperty *property, const QString &suffix); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + struct Data { + QString prefix; + QString suffix; + }; + QMap propertyToData; +}; + +DecoratedDoublePropertyManager::DecoratedDoublePropertyManager(QObject *parent) + : QtDoublePropertyManager(parent) +{ +} + +DecoratedDoublePropertyManager::~DecoratedDoublePropertyManager() +{ +} + +QString DecoratedDoublePropertyManager::prefix(const QtProperty *property) const +{ + if (!propertyToData.contains(property)) + return QString(); + return propertyToData[property].prefix; +} + +QString DecoratedDoublePropertyManager::suffix(const QtProperty *property) const +{ + if (!propertyToData.contains(property)) + return QString(); + return propertyToData[property].suffix; +} + +void DecoratedDoublePropertyManager::setPrefix(QtProperty *property, const QString &prefix) +{ + if (!propertyToData.contains(property)) + return; + + DecoratedDoublePropertyManager::Data data = propertyToData[property]; + if (data.prefix == prefix) + return; + + data.prefix = prefix; + propertyToData[property] = data; + + emit propertyChanged(property); + emit prefixChanged(property, prefix); +} + +void DecoratedDoublePropertyManager::setSuffix(QtProperty *property, const QString &suffix) +{ + if (!propertyToData.contains(property)) + return; + + DecoratedDoublePropertyManager::Data data = propertyToData[property]; + if (data.suffix == suffix) + return; + + data.suffix = suffix; + propertyToData[property] = data; + + emit propertyChanged(property); + emit suffixChanged(property, suffix); +} + +QString DecoratedDoublePropertyManager::valueText(const QtProperty *property) const +{ + QString text = QtDoublePropertyManager::valueText(property); + if (!propertyToData.contains(property)) + return text; + + DecoratedDoublePropertyManager::Data data = propertyToData[property]; + text = data.prefix + text + data.suffix; + + return text; +} + +void DecoratedDoublePropertyManager::initializeProperty(QtProperty *property) +{ + propertyToData[property] = DecoratedDoublePropertyManager::Data(); + QtDoublePropertyManager::initializeProperty(property); +} + +void DecoratedDoublePropertyManager::uninitializeProperty(QtProperty *property) +{ + propertyToData.remove(property); + QtDoublePropertyManager::uninitializeProperty(property); +} + + +class DecoratedDoubleSpinBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + DecoratedDoubleSpinBoxFactory(QObject *parent = 0); + ~DecoratedDoubleSpinBoxFactory(); +protected: + void connectPropertyManager(DecoratedDoublePropertyManager *manager); + QWidget *createEditor(DecoratedDoublePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(DecoratedDoublePropertyManager *manager); +private slots: + + void slotPrefixChanged(QtProperty *property, const QString &prefix); + void slotSuffixChanged(QtProperty *property, const QString &prefix); + void slotEditorDestroyed(QObject *object); +private: + /* We delegate responsibilities for QtDoublePropertyManager, which is a base class + of DecoratedDoublePropertyManager to appropriate QtDoubleSpinBoxFactory */ + QtDoubleSpinBoxFactory *originalFactory; + QMap > createdEditors; + QMap editorToProperty; +}; + +DecoratedDoubleSpinBoxFactory::DecoratedDoubleSpinBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + originalFactory = new QtDoubleSpinBoxFactory(this); +} + +DecoratedDoubleSpinBoxFactory::~DecoratedDoubleSpinBoxFactory() +{ + // not need to delete editors because they will be deleted by originalFactory in its destructor +} + +void DecoratedDoubleSpinBoxFactory::connectPropertyManager(DecoratedDoublePropertyManager *manager) +{ + originalFactory->addPropertyManager(manager); + connect(manager, SIGNAL(prefixChanged(QtProperty *, const QString &)), this, SLOT(slotPrefixChanged(QtProperty *, const QString &))); + connect(manager, SIGNAL(suffixChanged(QtProperty *, const QString &)), this, SLOT(slotSuffixChanged(QtProperty *, const QString &))); +} + +QWidget *DecoratedDoubleSpinBoxFactory::createEditor(DecoratedDoublePropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QtAbstractEditorFactoryBase *base = originalFactory; + QWidget *w = base->createEditor(property, parent); + if (!w) + return 0; + + QDoubleSpinBox *spinBox = qobject_cast(w); + if (!spinBox) + return 0; + + spinBox->setPrefix(manager->prefix(property)); + spinBox->setSuffix(manager->suffix(property)); + + createdEditors[property].append(spinBox); + editorToProperty[spinBox] = property; + + return spinBox; +} + +void DecoratedDoubleSpinBoxFactory::disconnectPropertyManager(DecoratedDoublePropertyManager *manager) +{ + originalFactory->removePropertyManager(manager); + disconnect(manager, SIGNAL(prefixChanged(QtProperty *, const QString &)), this, SLOT(slotPrefixChanged(QtProperty *, const QString &))); + disconnect(manager, SIGNAL(suffixChanged(QtProperty *, const QString &)), this, SLOT(slotSuffixChanged(QtProperty *, const QString &))); +} + +void DecoratedDoubleSpinBoxFactory::slotPrefixChanged(QtProperty *property, const QString &prefix) +{ + if (!createdEditors.contains(property)) + return; + + DecoratedDoublePropertyManager *manager = propertyManager(property); + if (!manager) + return; + + QList editors = createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->setPrefix(prefix); + } +} + +void DecoratedDoubleSpinBoxFactory::slotSuffixChanged(QtProperty *property, const QString &prefix) +{ + if (!createdEditors.contains(property)) + return; + + DecoratedDoublePropertyManager *manager = propertyManager(property); + if (!manager) + return; + + QList editors = createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->setSuffix(prefix); + } +} + +void DecoratedDoubleSpinBoxFactory::slotEditorDestroyed(QObject *object) +{ + QMap::ConstIterator itEditor = + editorToProperty.constBegin(); + while (itEditor != editorToProperty.constEnd()) { + if (itEditor.key() == object) { + QDoubleSpinBox *editor = itEditor.key(); + QtProperty *property = itEditor.value(); + editorToProperty.remove(editor); + createdEditors[property].removeAll(editor); + if (createdEditors[property].isEmpty()) + createdEditors.remove(property); + return; + } + itEditor++; + } +} + + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QtDoublePropertyManager *undecoratedManager = new QtDoublePropertyManager(); + QtProperty *undecoratedProperty = undecoratedManager->addProperty("Undecorated"); + undecoratedManager->setValue(undecoratedProperty, 123.45); + + DecoratedDoublePropertyManager *decoratedManager = new DecoratedDoublePropertyManager(); + QtProperty *decoratedProperty = decoratedManager->addProperty("Decorated"); + decoratedManager->setPrefix(decoratedProperty, "speed: "); + decoratedManager->setSuffix(decoratedProperty, " km/h"); + decoratedManager->setValue(decoratedProperty, 123.45); + + QtDoubleSpinBoxFactory *undecoratedFactory = new QtDoubleSpinBoxFactory(); + DecoratedDoubleSpinBoxFactory *decoratedFactory = new DecoratedDoubleSpinBoxFactory(); + + QtTreePropertyBrowser *editor = new QtTreePropertyBrowser(); + editor->setFactoryForManager(undecoratedManager, undecoratedFactory); + editor->setFactoryForManager(decoratedManager, decoratedFactory); + editor->addProperty(undecoratedProperty); + editor->addProperty(decoratedProperty); + editor->show(); + + int ret = app.exec(); + + delete decoratedFactory; + delete decoratedManager; + delete undecoratedFactory; + delete undecoratedManager; + delete editor; + + return ret; +} + +#include "main.moc" diff --git a/examples/demo/CMakeLists.txt b/examples/demo/CMakeLists.txt new file mode 100644 index 0000000..4660375 --- /dev/null +++ b/examples/demo/CMakeLists.txt @@ -0,0 +1,19 @@ + +SET(example_name demo) + +SET(KIT_SRCS + main.cpp + ) + +SET(KIT_resources + demo.qrc + ) + +IF(QT5_FOUND) + QT5_ADD_RESOURCES(KIT_QRC_SRCS ${KIT_resources}) +ELSE() + QT4_ADD_RESOURCES(KIT_QRC_SRCS ${KIT_resources}) +ENDIF() + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS} ${KIT_QRC_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) diff --git a/examples/demo/demo.pro b/examples/demo/demo.pro new file mode 100644 index 0000000..8e44044 --- /dev/null +++ b/examples/demo/demo.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +DEPENDPATH += . +INCLUDEPATH += . + +include(../../src/qtpropertybrowser.pri) +# Input +SOURCES += main.cpp +RESOURCES += demo.qrc diff --git a/examples/demo/demo.qdoc b/examples/demo/demo.qdoc new file mode 100644 index 0000000..e9b3bed --- /dev/null +++ b/examples/demo/demo.qdoc @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-demo.html + \title Demo Example + + This example shows how to customize a property browser widget's + appearance and behavior. + + \image demo.png + + The various property browsers presented in this example display + the same set of properties, and are implementations of the + QtTreePropertyBrowser class and the QtGroupBoxPropertyBrowser + class, respectively. + + The example shows how a property browser's appearance and behavior can + be varied using the QtPropertyBrowser framework's property + managers and editor factories. + + The source files can be found in examples/demo directory of the package. + */ diff --git a/examples/demo/demo.qrc b/examples/demo/demo.qrc new file mode 100644 index 0000000..c6be0ce --- /dev/null +++ b/examples/demo/demo.qrc @@ -0,0 +1,8 @@ + + + images/up.png + images/down.png + images/right.png + images/left.png + + diff --git a/examples/demo/images/down.png b/examples/demo/images/down.png new file mode 100644 index 0000000000000000000000000000000000000000..29d1d4439a139c662aecca94b6f43a465cfb9cc6 GIT binary patch literal 594 zcmV-Y0j z)Xz`TU>wKswOeUBH_Vo3LZ*V4p&U4v;LVFDq!ObUNJtQHC_UYOy}c$4_Z z287Mpy&>Gkk3$;%;XTGD)-SARcb^V+y#l_lys$a@k{nD+qgKLE+C6xLudGK{sd70w zcE71nDjtqr6rQslcH!s21HbzIZLG4Ku(F%O+U^xp_O4>4nBl-LJ{^?W2788E7ww3c$dW3qz>Ki(HSZqJlD~5#;x#SD}gQ7 zgv0(;bxhbL9Yezjn5K`uZiTiRwq2=|ckJ6DkxX7Tsy45p8>IMse%D zf;Vqf6vh<#P(J!fv{R}3IKcTOvuzkL=(>--JPth;j^KP+u2DCF7oBg1O2Gjh4J~2iFzHobR$W9+ed&iz#gEmW3Y&v zZ*1~MdipzQPYUyy_~Z-G`o@qna0>{{%>1pCHZ|9c3=DO#|4^B1;VbnCc=)W9ww8m4 z5Dx_AeopgXzy^VPz%mf{RiNioWwhsV2m3k#e&uEe!zFx$pqOEPVH#0TK(sQx&BWn* zqx`Q2i^!>#*2L)b+g-FLjck4v377E{R4Ncv5S+PyuQ8$#$gRtDp0Op+s|WHmRvNvJ7zd!z7-kcBSVc z_~L*!C{+>EtN7Fen^`C#i@?y-J4A)lg+B1=;Kz4Z%7e*Tj#t0Sg%}Y4<*J=$W`T(0 zfIE{D;0dX|{tEQ*qfOrK&&#Me!Yy0cg-^T%RZ`vE@$xZX5m<3Tf(V+A=3BoNF8s|n z{9Yjiya48^fXk86pr+z#@Tn<20mDERSTVEWKfT8e{7KYRtIBHHAITZSk@xgqT>t<8 M07*qoM6N<$f+VOsGXMYp literal 0 HcmV?d00001 diff --git a/examples/demo/images/right.png b/examples/demo/images/right.png new file mode 100644 index 0000000000000000000000000000000000000000..34b91f09fa3aa8e0b329edc97054c11defba8016 GIT binary patch literal 655 zcmV;A0&x9_P)cJAZUz`NyfQlF14XgvfeqKhRM8=g$nIpi07v2Pm$T0PG z+s5t5ev&IuzPy9iGfJ_*Szxjq-U_S#@di#sMFxj0Y^u zs2IrSA-xLzn@Xv~L10-`OD_1{xQ}!;h8VCMGZhEg7)ve(nH0=Nlth##z-1t-sznz( zI^knI8`}k}=1Q^8xg4a|;6+&Z7-tr^27CjyRJH7a$AX8**swS%Z2U-ZpmSmP5GMj$ z1y+C!prEQ%7kpz7mY14W%oynFgWlfyOj9X{G^0Np=uW$J^8**~h`aaUdlDkgAkhJB z2loJbfPHrEakyVc$6#mx5^=)7buY9XEP!Q$7GNCj4jnzl`B(FVY;?m5b-|rNMD_xg pfC->zKW))&VdLne22{OJfIn@@R^)Beh^qhq002ovPDHLkV1l$X9{d0R literal 0 HcmV?d00001 diff --git a/examples/demo/images/up.png b/examples/demo/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..e4373122171599c88b78c884b927c6a8b4a90c6a GIT binary patch literal 692 zcmV;l0!#ggP)p2raNh0iv$(l~TMx4kdC6q9nEA|`**D{}k#dX8|6LB>7#;)I^Ped=4Hzs5}YJfl=IMqVOwV3TOn<`fg+FtutHTOl+p4ItW@S@UCRT$s#e2Vdg=lo5D}~>p3$197_jRp z=YhPc7Gm8z$3=Kf7AcnG)$Gyx5pjP)J5;=W_SftyqWmZ>V+N`!8lA3I}LdVVyM axbX+reAIe(fQ}9T0000 +#include +#include +#include +#include +#include +#include "qtpropertymanager.h" +#include "qteditorfactory.h" +#include "qttreepropertybrowser.h" +#include "qtbuttonpropertybrowser.h" +#include "qtgroupboxpropertybrowser.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QWidget *w = new QWidget(); + + QtBoolPropertyManager *boolManager = new QtBoolPropertyManager(w); + QtIntPropertyManager *intManager = new QtIntPropertyManager(w); + QtStringPropertyManager *stringManager = new QtStringPropertyManager(w); + QtSizePropertyManager *sizeManager = new QtSizePropertyManager(w); + QtRectPropertyManager *rectManager = new QtRectPropertyManager(w); + QtSizePolicyPropertyManager *sizePolicyManager = new QtSizePolicyPropertyManager(w); + QtEnumPropertyManager *enumManager = new QtEnumPropertyManager(w); + QtGroupPropertyManager *groupManager = new QtGroupPropertyManager(w); + + QtProperty *item0 = groupManager->addProperty("QObject"); + + QtProperty *item1 = stringManager->addProperty("objectName"); + item0->addSubProperty(item1); + + QtProperty *item2 = boolManager->addProperty("enabled"); + item0->addSubProperty(item2); + + QtProperty *item3 = rectManager->addProperty("geometry"); + item0->addSubProperty(item3); + + QtProperty *item4 = sizePolicyManager->addProperty("sizePolicy"); + item0->addSubProperty(item4); + + QtProperty *item5 = sizeManager->addProperty("sizeIncrement"); + item0->addSubProperty(item5); + + QtProperty *item7 = boolManager->addProperty("mouseTracking"); + item0->addSubProperty(item7); + + QtProperty *item8 = enumManager->addProperty("direction"); + QStringList enumNames; + enumNames << "Up" << "Right" << "Down" << "Left"; + enumManager->setEnumNames(item8, enumNames); + QMap enumIcons; + enumIcons[0] = QIcon(":/demo/images/up.png"); + enumIcons[1] = QIcon(":/demo/images/right.png"); + enumIcons[2] = QIcon(":/demo/images/down.png"); + enumIcons[3] = QIcon(":/demo/images/left.png"); + enumManager->setEnumIcons(item8, enumIcons); + item0->addSubProperty(item8); + + QtProperty *item9 = intManager->addProperty("value"); + intManager->setRange(item9, -100, 100); + item0->addSubProperty(item9); + + QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory(w); + QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(w); + QtSliderFactory *sliderFactory = new QtSliderFactory(w); + QtScrollBarFactory *scrollBarFactory = new QtScrollBarFactory(w); + QtLineEditFactory *lineEditFactory = new QtLineEditFactory(w); + QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory(w); + + QtAbstractPropertyBrowser *editor1 = new QtTreePropertyBrowser(); + editor1->setFactoryForManager(boolManager, checkBoxFactory); + editor1->setFactoryForManager(intManager, spinBoxFactory); + editor1->setFactoryForManager(stringManager, lineEditFactory); + editor1->setFactoryForManager(sizeManager->subIntPropertyManager(), spinBoxFactory); + editor1->setFactoryForManager(rectManager->subIntPropertyManager(), spinBoxFactory); + editor1->setFactoryForManager(sizePolicyManager->subIntPropertyManager(), spinBoxFactory); + editor1->setFactoryForManager(sizePolicyManager->subEnumPropertyManager(), comboBoxFactory); + editor1->setFactoryForManager(enumManager, comboBoxFactory); + + editor1->addProperty(item0); + + QtAbstractPropertyBrowser *editor2 = new QtTreePropertyBrowser(); + editor2->addProperty(item0); + + QtAbstractPropertyBrowser *editor3 = new QtGroupBoxPropertyBrowser(); + editor3->setFactoryForManager(boolManager, checkBoxFactory); + editor3->setFactoryForManager(intManager, spinBoxFactory); + editor3->setFactoryForManager(stringManager, lineEditFactory); + editor3->setFactoryForManager(sizeManager->subIntPropertyManager(), spinBoxFactory); + editor3->setFactoryForManager(rectManager->subIntPropertyManager(), spinBoxFactory); + editor3->setFactoryForManager(sizePolicyManager->subIntPropertyManager(), spinBoxFactory); + editor3->setFactoryForManager(sizePolicyManager->subEnumPropertyManager(), comboBoxFactory); + editor3->setFactoryForManager(enumManager, comboBoxFactory); + + editor3->addProperty(item0); + + QScrollArea *scroll3 = new QScrollArea(); + scroll3->setWidgetResizable(true); + scroll3->setWidget(editor3); + + QtAbstractPropertyBrowser *editor4 = new QtGroupBoxPropertyBrowser(); + editor4->setFactoryForManager(boolManager, checkBoxFactory); + editor4->setFactoryForManager(intManager, scrollBarFactory); + editor4->setFactoryForManager(stringManager, lineEditFactory); + editor4->setFactoryForManager(sizeManager->subIntPropertyManager(), spinBoxFactory); + editor4->setFactoryForManager(rectManager->subIntPropertyManager(), spinBoxFactory); + editor4->setFactoryForManager(sizePolicyManager->subIntPropertyManager(), sliderFactory); + editor4->setFactoryForManager(sizePolicyManager->subEnumPropertyManager(), comboBoxFactory); + editor4->setFactoryForManager(enumManager, comboBoxFactory); + + editor4->addProperty(item0); + + QScrollArea *scroll4 = new QScrollArea(); + scroll4->setWidgetResizable(true); + scroll4->setWidget(editor4); + + QtAbstractPropertyBrowser *editor5 = new QtButtonPropertyBrowser(); + editor5->setFactoryForManager(boolManager, checkBoxFactory); + editor5->setFactoryForManager(intManager, scrollBarFactory); + editor5->setFactoryForManager(stringManager, lineEditFactory); + editor5->setFactoryForManager(sizeManager->subIntPropertyManager(), spinBoxFactory); + editor5->setFactoryForManager(rectManager->subIntPropertyManager(), spinBoxFactory); + editor5->setFactoryForManager(sizePolicyManager->subIntPropertyManager(), sliderFactory); + editor5->setFactoryForManager(sizePolicyManager->subEnumPropertyManager(), comboBoxFactory); + editor5->setFactoryForManager(enumManager, comboBoxFactory); + + editor5->addProperty(item0); + + QScrollArea *scroll5 = new QScrollArea(); + scroll5->setWidgetResizable(true); + scroll5->setWidget(editor5); + + QGridLayout *layout = new QGridLayout(w); + QLabel *label1 = new QLabel("Editable Tree Property Browser"); + QLabel *label2 = new QLabel("Read Only Tree Property Browser, editor factories are not set"); + QLabel *label3 = new QLabel("Group Box Property Browser"); + QLabel *label4 = new QLabel("Group Box Property Browser with different editor factories"); + QLabel *label5 = new QLabel("Button Property Browser"); + label1->setWordWrap(true); + label2->setWordWrap(true); + label3->setWordWrap(true); + label4->setWordWrap(true); + label5->setWordWrap(true); + label1->setFrameShadow(QFrame::Sunken); + label2->setFrameShadow(QFrame::Sunken); + label3->setFrameShadow(QFrame::Sunken); + label4->setFrameShadow(QFrame::Sunken); + label5->setFrameShadow(QFrame::Sunken); + label1->setFrameShape(QFrame::Panel); + label2->setFrameShape(QFrame::Panel); + label3->setFrameShape(QFrame::Panel); + label4->setFrameShape(QFrame::Panel); + label5->setFrameShape(QFrame::Panel); + label1->setAlignment(Qt::AlignCenter); + label2->setAlignment(Qt::AlignCenter); + label3->setAlignment(Qt::AlignCenter); + label4->setAlignment(Qt::AlignCenter); + label5->setAlignment(Qt::AlignCenter); + + layout->addWidget(label1, 0, 0); + layout->addWidget(label2, 0, 1); + layout->addWidget(label3, 0, 2); + layout->addWidget(label4, 0, 3); + layout->addWidget(label5, 0, 4); + layout->addWidget(editor1, 1, 0); + layout->addWidget(editor2, 1, 1); + layout->addWidget(scroll3, 1, 2); + layout->addWidget(scroll4, 1, 3); + layout->addWidget(scroll5, 1, 4); + w->show(); + + int ret = app.exec(); + delete w; + return ret; +} diff --git a/examples/examples.pro b/examples/examples.pro new file mode 100644 index 0000000..4d0dad6 --- /dev/null +++ b/examples/examples.pro @@ -0,0 +1,7 @@ +###################################################################### +# Automatically generated by qmake (2.00a) Wed Jun 15 15:53:34 2005 +###################################################################### + +TEMPLATE = subdirs +SUBDIRS = simple canvas_variant canvas_typed demo decoration extension object_controller + diff --git a/examples/extension/CMakeLists.txt b/examples/extension/CMakeLists.txt new file mode 100644 index 0000000..83dcd06 --- /dev/null +++ b/examples/extension/CMakeLists.txt @@ -0,0 +1,15 @@ +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(example_name extension) + +SET(KIT_SRCS + main.cpp + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) \ No newline at end of file diff --git a/examples/extension/extension.pro b/examples/extension/extension.pro new file mode 100644 index 0000000..7a13249 --- /dev/null +++ b/examples/extension/extension.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +DEPENDPATH += . +INCLUDEPATH += . + +include(../../src/qtpropertybrowser.pri) +# Input +SOURCES += main.cpp + diff --git a/examples/extension/extension.qdoc b/examples/extension/extension.qdoc new file mode 100644 index 0000000..81b9474 --- /dev/null +++ b/examples/extension/extension.qdoc @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-extension.html + \title Extension Example + + This example demonstrates how to extend the + QtVariantPropertyManager class to handle additional property + types. + + \image extension.png + + The variant manager is extended to handle the QPointF type. + + The source files can be found in examples/extension directory of the package. +*/ diff --git a/examples/extension/main.cpp b/examples/extension/main.cpp new file mode 100644 index 0000000..2a4fd61 --- /dev/null +++ b/examples/extension/main.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include "qtvariantproperty.h" +#include "qteditorfactory.h" +#include "qttreepropertybrowser.h" + +class VariantManager : public QtVariantPropertyManager +{ + Q_OBJECT +public: + VariantManager(QObject *parent = 0); + ~VariantManager(); + + virtual QVariant value(const QtProperty *property) const; + virtual int valueType(int propertyType) const; + virtual bool isPropertyTypeSupported(int propertyType) const; + + QString valueText(const QtProperty *property) const; + +public slots: + virtual void setValue(QtProperty *property, const QVariant &val); +protected: + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private slots: + void slotValueChanged(QtProperty *property, const QVariant &value); + void slotPropertyDestroyed(QtProperty *property); +private: + struct Data { + QVariant value; + QtVariantProperty *x; + QtVariantProperty *y; + }; + QMap propertyToData; + QMap xToProperty; + QMap yToProperty; +}; + +VariantManager::VariantManager(QObject *parent) + : QtVariantPropertyManager(parent) +{ + connect(this, SIGNAL(valueChanged(QtProperty *, const QVariant &)), + this, SLOT(slotValueChanged(QtProperty *, const QVariant &))); + connect(this, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +VariantManager::~VariantManager() +{ + +} + +void VariantManager::slotValueChanged(QtProperty *property, const QVariant &value) +{ + if (xToProperty.contains(property)) { + QtProperty *pointProperty = xToProperty[property]; + QVariant v = this->value(pointProperty); + QPointF p = v.value(); + p.setX(value.value()); + setValue(pointProperty, p); + } else if (yToProperty.contains(property)) { + QtProperty *pointProperty = yToProperty[property]; + QVariant v = this->value(pointProperty); + QPointF p = v.value(); + p.setY(value.value()); + setValue(pointProperty, p); + } +} + +void VariantManager::slotPropertyDestroyed(QtProperty *property) +{ + if (xToProperty.contains(property)) { + QtProperty *pointProperty = xToProperty[property]; + propertyToData[pointProperty].x = 0; + xToProperty.remove(property); + } else if (yToProperty.contains(property)) { + QtProperty *pointProperty = yToProperty[property]; + propertyToData[pointProperty].y = 0; + yToProperty.remove(property); + } +} + +bool VariantManager::isPropertyTypeSupported(int propertyType) const +{ + if (propertyType == QVariant::PointF) + return true; + return QtVariantPropertyManager::isPropertyTypeSupported(propertyType); +} + +int VariantManager::valueType(int propertyType) const +{ + if (propertyType == QVariant::PointF) + return QVariant::PointF; + return QtVariantPropertyManager::valueType(propertyType); +} + +QVariant VariantManager::value(const QtProperty *property) const +{ + if (propertyToData.contains(property)) + return propertyToData[property].value; + return QtVariantPropertyManager::value(property); +} + +QString VariantManager::valueText(const QtProperty *property) const +{ + if (propertyToData.contains(property)) { + QVariant v = propertyToData[property].value; + QPointF p = v.value(); + return QString(tr("(%1, %2)").arg(QString::number(p.x())) + .arg(QString::number(p.y()))); + } + return QtVariantPropertyManager::valueText(property); +} + +void VariantManager::setValue(QtProperty *property, const QVariant &val) +{ + if (propertyToData.contains(property)) { + if (val.type() != QVariant::PointF && !val.canConvert(QVariant::PointF)) + return; + QPointF p = val.value(); + Data d = propertyToData[property]; + d.value = p; + if (d.x) + d.x->setValue(p.x()); + if (d.y) + d.y->setValue(p.y()); + propertyToData[property] = d; + emit propertyChanged(property); + emit valueChanged(property, p); + return; + } + QtVariantPropertyManager::setValue(property, val); +} + +void VariantManager::initializeProperty(QtProperty *property) +{ + if (propertyType(property) == QVariant::PointF) { + Data d; + + d.value = QPointF(0, 0); + + VariantManager *that = (VariantManager *)this; + + d.x = that->addProperty(QVariant::Double); + d.x->setPropertyName(tr("Position X")); + property->addSubProperty(d.x); + xToProperty[d.x] = property; + + d.y = that->addProperty(QVariant::Double); + d.y->setPropertyName(tr("Position Y")); + property->addSubProperty(d.y); + yToProperty[d.y] = property; + + propertyToData[property] = d; + } + QtVariantPropertyManager::initializeProperty(property); +} + +void VariantManager::uninitializeProperty(QtProperty *property) +{ + if (propertyToData.contains(property)) { + Data d = propertyToData[property]; + if (d.x) + xToProperty.remove(d.x); + if (d.y) + yToProperty.remove(d.y); + propertyToData.remove(property); + } + QtVariantPropertyManager::uninitializeProperty(property); +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + VariantManager *variantManager = new VariantManager(); + + QtVariantProperty *item = variantManager->addProperty(QVariant::PointF, + "PointF Property"); + item->setValue(QPointF(2.5, 13.13)); + + QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(); + + QtTreePropertyBrowser ed1; + QtVariantPropertyManager *varMan = variantManager; + ed1.setFactoryForManager(varMan, variantFactory); + ed1.addProperty(item); + + + ed1.show(); + + int ret = app.exec(); + + delete variantFactory; + delete variantManager; + + return ret; +} + +#include "main.moc" diff --git a/examples/object_controller/CMakeLists.txt b/examples/object_controller/CMakeLists.txt new file mode 100644 index 0000000..f466fd5 --- /dev/null +++ b/examples/object_controller/CMakeLists.txt @@ -0,0 +1,16 @@ +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(example_name object_controller) + +SET(KIT_SRCS + main.cpp + objectcontroller.cpp + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) \ No newline at end of file diff --git a/examples/object_controller/main.cpp b/examples/object_controller/main.cpp new file mode 100644 index 0000000..96d6439 --- /dev/null +++ b/examples/object_controller/main.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "objectcontroller.h" + +class MyController : public QDialog +{ + Q_OBJECT +public: + MyController(QWidget *parent = 0); + ~MyController(); +private slots: + void createAndControl(); +private: + QComboBox *theClassCombo; + ObjectController *theController; + QStringList theClassNames; + QObject *theControlledObject; +}; + +MyController::MyController(QWidget *parent) + : QDialog(parent), theControlledObject(0) +{ + theClassCombo = new QComboBox(this); + QToolButton *button = new QToolButton(this); + theController = new ObjectController(this); + QDialogButtonBox *buttonBox = new QDialogButtonBox(this); + + connect(button, SIGNAL(clicked()), this, SLOT(createAndControl())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + button->setText(tr("Create And Control")); + buttonBox->setStandardButtons(QDialogButtonBox::Close); + + QVBoxLayout *layout = new QVBoxLayout(this); + QHBoxLayout *internalLayout = new QHBoxLayout(); + internalLayout->addWidget(theClassCombo); + internalLayout->addWidget(button); + layout->addLayout(internalLayout); + layout->addWidget(theController); + layout->addWidget(buttonBox); + + theClassNames.append(QLatin1String("QWidget")); + theClassNames.append(QLatin1String("QPushButton")); + theClassNames.append(QLatin1String("QDialogButtonBox")); + theClassNames.append(QLatin1String("QTreeWidget")); + theClassNames.append(QLatin1String("QCalendarWidget")); + theClassNames.append(QLatin1String("QAction")); + theClassNames.append(QLatin1String("QTimeLine")); + theClassNames.append(QLatin1String("QTextDocument")); + + theClassCombo->addItems(theClassNames); +} + +MyController::~MyController() +{ + if (theControlledObject) + delete theControlledObject; +} + +void MyController::createAndControl() +{ + QObject *newObject = 0; + QString className = theClassNames.at(theClassCombo->currentIndex()); + if (className == QLatin1String("QWidget")) + newObject = new QWidget(); + else if (className == QLatin1String("QPushButton")) + newObject = new QPushButton(); + else if (className == QLatin1String("QDialogButtonBox")) + newObject = new QDialogButtonBox(); + else if (className == QLatin1String("QTreeWidget")) + newObject = new QTreeWidget(); + else if (className == QLatin1String("QCalendarWidget")) + newObject = new QCalendarWidget(); + else if (className == QLatin1String("QAction")) + newObject = new QAction(0); + else if (className == QLatin1String("QTimeLine")) + newObject = new QTimeLine(); + else if (className == QLatin1String("QTextDocument")) + newObject = new QTextDocument(); + + if (!newObject) + return; + + QWidget *newWidget = qobject_cast(newObject); + if (newWidget) { + QRect r = newWidget->geometry(); + r.setSize(newWidget->sizeHint()); + r.setWidth(qMax(r.width(), 150)); + r.setHeight(qMax(r.height(), 50)); + r.moveCenter(QApplication::desktop()->geometry().center()); + newWidget->setGeometry(r); + newWidget->setWindowTitle(tr("Controlled Object: %1").arg(className)); + newWidget->show(); + } + + if (theControlledObject) + delete theControlledObject; + + theControlledObject = newObject; + theController->setObject(theControlledObject); +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + MyController *controller = new MyController(); + controller->show(); + + int ret = app.exec(); + + return ret; +} + +#include "main.moc" diff --git a/examples/object_controller/object_controller.qdoc b/examples/object_controller/object_controller.qdoc new file mode 100644 index 0000000..116a4d3 --- /dev/null +++ b/examples/object_controller/object_controller.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-object_controller.html + \title The Object Controller Example. + + \image object_controller.png + + This example implements a simple widget component which shows + QObject's and its subclasses' properties. The user can modify these properies interacively + and the object controller applies the changes to the controlled object. + The object controller is similar to the property editor used in QDesigner application. + To control the object just instantiate ObjectController, set controlled object (any QObject subclass) by + calling ObjectController::setObject() and show the controller. + + The source files can be found in examples/object_controller directory of the package. +*/ diff --git a/examples/object_controller/objectcontroller.cpp b/examples/object_controller/objectcontroller.cpp new file mode 100644 index 0000000..1c09b0a --- /dev/null +++ b/examples/object_controller/objectcontroller.cpp @@ -0,0 +1,391 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include +#include +#include +#include "objectcontroller.h" +#include "qtvariantproperty.h" +#include "qtgroupboxpropertybrowser.h" +#include "qttreepropertybrowser.h" +#include "qtpropertybrowser.h" + +class ObjectControllerPrivate +{ + ObjectController *q_ptr; + Q_DECLARE_PUBLIC(ObjectController) +public: + + void addClassProperties(const QMetaObject *metaObject); + void updateClassProperties(const QMetaObject *metaObject, bool recursive); + void saveExpandedState(); + void restoreExpandedState(); + void slotValueChanged(QtProperty *property, const QVariant &value); + int enumToInt(const QMetaEnum &metaEnum, int enumValue) const; + int intToEnum(const QMetaEnum &metaEnum, int intValue) const; + int flagToInt(const QMetaEnum &metaEnum, int flagValue) const; + int intToFlag(const QMetaEnum &metaEnum, int intValue) const; + bool isSubValue(int value, int subValue) const; + bool isPowerOf2(int value) const; + + QObject *m_object; + + QMap m_classToProperty; + QMap m_propertyToClass; + QMap m_propertyToIndex; + QMap > m_classToIndexToProperty; + + QMap m_propertyToExpanded; + + QList m_topLevelProperties; + + QtAbstractPropertyBrowser *m_browser; + QtVariantPropertyManager *m_manager; + QtVariantPropertyManager *m_readOnlyManager; +}; + +int ObjectControllerPrivate::enumToInt(const QMetaEnum &metaEnum, int enumValue) const +{ + QMap valueMap; // dont show multiple enum values which have the same values + int pos = 0; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value)) { + if (value == enumValue) + return pos; + valueMap[value] = pos++; + } + } + return -1; +} + +int ObjectControllerPrivate::intToEnum(const QMetaEnum &metaEnum, int intValue) const +{ + QMap valueMap; // dont show multiple enum values which have the same values + QList values; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value)) { + valueMap[value] = true; + values.append(value); + } + } + if (intValue >= values.count()) + return -1; + return values.at(intValue); +} + +bool ObjectControllerPrivate::isSubValue(int value, int subValue) const +{ + if (value == subValue) + return true; + int i = 0; + while (subValue) { + if (!(value & (1 << i))) { + if (subValue & 1) + return false; + } + i++; + subValue = subValue >> 1; + } + return true; +} + +bool ObjectControllerPrivate::isPowerOf2(int value) const +{ + while (value) { + if (value & 1) { + return value == 1; + } + value = value >> 1; + } + return false; +} + +int ObjectControllerPrivate::flagToInt(const QMetaEnum &metaEnum, int flagValue) const +{ + if (!flagValue) + return 0; + int intValue = 0; + QMap valueMap; // dont show multiple enum values which have the same values + int pos = 0; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value) && isPowerOf2(value)) { + if (isSubValue(flagValue, value)) + intValue |= (1 << pos); + valueMap[value] = pos++; + } + } + return intValue; +} + +int ObjectControllerPrivate::intToFlag(const QMetaEnum &metaEnum, int intValue) const +{ + QMap valueMap; // dont show multiple enum values which have the same values + QList values; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value) && isPowerOf2(value)) { + valueMap[value] = true; + values.append(value); + } + } + int flagValue = 0; + int temp = intValue; + int i = 0; + while (temp) { + if (i >= values.count()) + return -1; + if (temp & 1) + flagValue |= values.at(i); + i++; + temp = temp >> 1; + } + return flagValue; +} + +void ObjectControllerPrivate::updateClassProperties(const QMetaObject *metaObject, bool recursive) +{ + if (!metaObject) + return; + + if (recursive) + updateClassProperties(metaObject->superClass(), recursive); + + QtProperty *classProperty = m_classToProperty.value(metaObject); + if (!classProperty) + return; + + for (int idx = metaObject->propertyOffset(); idx < metaObject->propertyCount(); idx++) { + QMetaProperty metaProperty = metaObject->property(idx); + if (metaProperty.isReadable()) { + if (m_classToIndexToProperty.contains(metaObject) && m_classToIndexToProperty[metaObject].contains(idx)) { + QtVariantProperty *subProperty = m_classToIndexToProperty[metaObject][idx]; + if (metaProperty.isEnumType()) { + if (metaProperty.isFlagType()) + subProperty->setValue(flagToInt(metaProperty.enumerator(), metaProperty.read(m_object).toInt())); + else + subProperty->setValue(enumToInt(metaProperty.enumerator(), metaProperty.read(m_object).toInt())); + } else { + subProperty->setValue(metaProperty.read(m_object)); + } + } + } + } +} + +void ObjectControllerPrivate::addClassProperties(const QMetaObject *metaObject) +{ + if (!metaObject) + return; + + addClassProperties(metaObject->superClass()); + + QtProperty *classProperty = m_classToProperty.value(metaObject); + if (!classProperty) { + QString className = QLatin1String(metaObject->className()); + classProperty = m_manager->addProperty(QtVariantPropertyManager::groupTypeId(), className); + m_classToProperty[metaObject] = classProperty; + m_propertyToClass[classProperty] = metaObject; + + for (int idx = metaObject->propertyOffset(); idx < metaObject->propertyCount(); idx++) { + QMetaProperty metaProperty = metaObject->property(idx); + int type = metaProperty.userType(); + QtVariantProperty *subProperty = 0; + if (!metaProperty.isReadable()) { + subProperty = m_readOnlyManager->addProperty(QVariant::String, QLatin1String(metaProperty.name())); + subProperty->setValue(QLatin1String("< Non Readable >")); + } else if (metaProperty.isEnumType()) { + if (metaProperty.isFlagType()) { + subProperty = m_manager->addProperty(QtVariantPropertyManager::flagTypeId(), QLatin1String(metaProperty.name())); + QMetaEnum metaEnum = metaProperty.enumerator(); + QMap valueMap; + QStringList flagNames; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value) && isPowerOf2(value)) { + valueMap[value] = true; + flagNames.append(QLatin1String(metaEnum.key(i))); + } + subProperty->setAttribute(QLatin1String("flagNames"), flagNames); + subProperty->setValue(flagToInt(metaEnum, metaProperty.read(m_object).toInt())); + } + } else { + subProperty = m_manager->addProperty(QtVariantPropertyManager::enumTypeId(), QLatin1String(metaProperty.name())); + QMetaEnum metaEnum = metaProperty.enumerator(); + QMap valueMap; // dont show multiple enum values which have the same values + QStringList enumNames; + for (int i = 0; i < metaEnum.keyCount(); i++) { + int value = metaEnum.value(i); + if (!valueMap.contains(value)) { + valueMap[value] = true; + enumNames.append(QLatin1String(metaEnum.key(i))); + } + } + subProperty->setAttribute(QLatin1String("enumNames"), enumNames); + subProperty->setValue(enumToInt(metaEnum, metaProperty.read(m_object).toInt())); + } + } else if (m_manager->isPropertyTypeSupported(type)) { + if (!metaProperty.isWritable()) + subProperty = m_readOnlyManager->addProperty(type, QLatin1String(metaProperty.name()) + QLatin1String(" (Non Writable)")); + if (!metaProperty.isDesignable()) + subProperty = m_readOnlyManager->addProperty(type, QLatin1String(metaProperty.name()) + QLatin1String(" (Non Designable)")); + else + subProperty = m_manager->addProperty(type, QLatin1String(metaProperty.name())); + subProperty->setValue(metaProperty.read(m_object)); + } else { + subProperty = m_readOnlyManager->addProperty(QVariant::String, QLatin1String(metaProperty.name())); + subProperty->setValue(QLatin1String("< Unknown Type >")); + subProperty->setEnabled(false); + } + classProperty->addSubProperty(subProperty); + m_propertyToIndex[subProperty] = idx; + m_classToIndexToProperty[metaObject][idx] = subProperty; + } + } else { + updateClassProperties(metaObject, false); + } + + m_topLevelProperties.append(classProperty); + m_browser->addProperty(classProperty); +} + +void ObjectControllerPrivate::saveExpandedState() +{ + +} + +void ObjectControllerPrivate::restoreExpandedState() +{ + +} + +void ObjectControllerPrivate::slotValueChanged(QtProperty *property, const QVariant &value) +{ + if (!m_propertyToIndex.contains(property)) + return; + + int idx = m_propertyToIndex.value(property); + + const QMetaObject *metaObject = m_object->metaObject(); + QMetaProperty metaProperty = metaObject->property(idx); + if (metaProperty.isEnumType()) { + if (metaProperty.isFlagType()) + metaProperty.write(m_object, intToFlag(metaProperty.enumerator(), value.toInt())); + else + metaProperty.write(m_object, intToEnum(metaProperty.enumerator(), value.toInt())); + } else { + metaProperty.write(m_object, value); + } + + updateClassProperties(metaObject, true); +} + +/////////////////// + +ObjectController::ObjectController(QWidget *parent) + : QWidget(parent) +{ + d_ptr = new ObjectControllerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_object = 0; +/* + QScrollArea *scroll = new QScrollArea(this); + scroll->setWidgetResizable(true); + + d_ptr->m_browser = new QtGroupBoxPropertyBrowser(this); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(scroll); + scroll->setWidget(d_ptr->m_browser); +*/ + QtTreePropertyBrowser *browser = new QtTreePropertyBrowser(this); + browser->setRootIsDecorated(false); + d_ptr->m_browser = browser; + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(d_ptr->m_browser); + + d_ptr->m_readOnlyManager = new QtVariantPropertyManager(this); + d_ptr->m_manager = new QtVariantPropertyManager(this); + QtVariantEditorFactory *factory = new QtVariantEditorFactory(this); + d_ptr->m_browser->setFactoryForManager(d_ptr->m_manager, factory); + + connect(d_ptr->m_manager, SIGNAL(valueChanged(QtProperty *, const QVariant &)), + this, SLOT(slotValueChanged(QtProperty *, const QVariant &))); +} + +ObjectController::~ObjectController() +{ + delete d_ptr; +} + +void ObjectController::setObject(QObject *object) +{ + if (d_ptr->m_object == object) + return; + + if (d_ptr->m_object) { + d_ptr->saveExpandedState(); + QListIterator it(d_ptr->m_topLevelProperties); + while (it.hasNext()) { + d_ptr->m_browser->removeProperty(it.next()); + } + d_ptr->m_topLevelProperties.clear(); + } + + d_ptr->m_object = object; + + if (!d_ptr->m_object) + return; + + d_ptr->addClassProperties(d_ptr->m_object->metaObject()); + + d_ptr->restoreExpandedState(); +} + +QObject *ObjectController::object() const +{ + return d_ptr->m_object; +} + +#include "moc_objectcontroller.cpp" diff --git a/examples/object_controller/objectcontroller.h b/examples/object_controller/objectcontroller.h new file mode 100644 index 0000000..f470b63 --- /dev/null +++ b/examples/object_controller/objectcontroller.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#ifndef OBJECTCONTROLLER_H +#define OBJECTCONTROLLER_H + +#include + +class ObjectControllerPrivate; + +class ObjectController : public QWidget +{ + Q_OBJECT +public: + ObjectController(QWidget *parent = 0); + ~ObjectController(); + + void setObject(QObject *object); + QObject *object() const; + +private: + ObjectControllerPrivate *d_ptr; + Q_DECLARE_PRIVATE(ObjectController) + Q_DISABLE_COPY(ObjectController) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QVariant &)) +}; + +#endif diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt new file mode 100644 index 0000000..a3e5d3d --- /dev/null +++ b/examples/simple/CMakeLists.txt @@ -0,0 +1,9 @@ + +SET(example_name simple) + +SET(KIT_SRCS + main.cpp + ) + +ADD_EXECUTABLE(${example_name} ${KIT_SRCS}) +TARGET_LINK_LIBRARIES(${example_name} ${PROJECT_NAME} ${QT_TARGETS}) diff --git a/examples/simple/main.cpp b/examples/simple/main.cpp new file mode 100644 index 0000000..69b6059 --- /dev/null +++ b/examples/simple/main.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +#include +#include +#include +#include "qtpropertymanager.h" +#include "qtvariantproperty.h" +#include "qttreepropertybrowser.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QtVariantPropertyManager *variantManager = new QtVariantPropertyManager(); + + int i = 0; + QtProperty *topItem = variantManager->addProperty(QtVariantPropertyManager::groupTypeId(), + QString::number(i++) + QLatin1String(" Group Property")); + + QtVariantProperty *item = variantManager->addProperty(QVariant::Bool, QString::number(i++) + QLatin1String(" Bool Property")); + item->setValue(true); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Int, QString::number(i++) + QLatin1String(" Int Property")); + item->setValue(20); + item->setAttribute(QLatin1String("minimum"), 0); + item->setAttribute(QLatin1String("maximum"), 100); + item->setAttribute(QLatin1String("singleStep"), 10); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Double, QString::number(i++) + QLatin1String(" Double Property")); + item->setValue(1.2345); + item->setAttribute(QLatin1String("singleStep"), 0.1); + item->setAttribute(QLatin1String("decimals"), 3); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::String, QString::number(i++) + QLatin1String(" String Property")); + item->setValue("Value"); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Date, QString::number(i++) + QLatin1String(" Date Property")); + item->setValue(QDate::currentDate().addDays(2)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Time, QString::number(i++) + QLatin1String(" Time Property")); + item->setValue(QTime::currentTime()); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::DateTime, QString::number(i++) + QLatin1String(" DateTime Property")); + item->setValue(QDateTime::currentDateTime()); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::KeySequence, QString::number(i++) + QLatin1String(" KeySequence Property")); + item->setValue(QKeySequence(Qt::ControlModifier | Qt::Key_Q)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Char, QString::number(i++) + QLatin1String(" Char Property")); + item->setValue(QChar(386)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Locale, QString::number(i++) + QLatin1String(" Locale Property")); + item->setValue(QLocale(QLocale::Polish, QLocale::Poland)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Point, QString::number(i++) + QLatin1String(" Point Property")); + item->setValue(QPoint(10, 10)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::PointF, QString::number(i++) + QLatin1String(" PointF Property")); + item->setValue(QPointF(1.2345, -1.23451)); + item->setAttribute(QLatin1String("decimals"), 3); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Size, QString::number(i++) + QLatin1String(" Size Property")); + item->setValue(QSize(20, 20)); + item->setAttribute(QLatin1String("minimum"), QSize(10, 10)); + item->setAttribute(QLatin1String("maximum"), QSize(30, 30)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::SizeF, QString::number(i++) + QLatin1String(" SizeF Property")); + item->setValue(QSizeF(1.2345, 1.2345)); + item->setAttribute(QLatin1String("decimals"), 3); + item->setAttribute(QLatin1String("minimum"), QSizeF(0.12, 0.34)); + item->setAttribute(QLatin1String("maximum"), QSizeF(20.56, 20.78)); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Rect, QString::number(i++) + QLatin1String(" Rect Property")); + item->setValue(QRect(10, 10, 20, 20)); + topItem->addSubProperty(item); + item->setAttribute(QLatin1String("constraint"), QRect(0, 0, 50, 50)); + + item = variantManager->addProperty(QVariant::RectF, QString::number(i++) + QLatin1String(" RectF Property")); + item->setValue(QRectF(1.2345, 1.2345, 1.2345, 1.2345)); + topItem->addSubProperty(item); + item->setAttribute(QLatin1String("constraint"), QRectF(0, 0, 50, 50)); + item->setAttribute(QLatin1String("decimals"), 3); + + item = variantManager->addProperty(QtVariantPropertyManager::enumTypeId(), + QString::number(i++) + QLatin1String(" Enum Property")); + QStringList enumNames; + enumNames << "Enum0" << "Enum1" << "Enum2"; + item->setAttribute(QLatin1String("enumNames"), enumNames); + item->setValue(1); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QtVariantPropertyManager::flagTypeId(), + QString::number(i++) + QLatin1String(" Flag Property")); + QStringList flagNames; + flagNames << "Flag0" << "Flag1" << "Flag2"; + item->setAttribute(QLatin1String("flagNames"), flagNames); + item->setValue(5); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::SizePolicy, QString::number(i++) + QLatin1String(" SizePolicy Property")); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Font, QString::number(i++) + QLatin1String(" Font Property")); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Cursor, QString::number(i++) + QLatin1String(" Cursor Property")); + topItem->addSubProperty(item); + + item = variantManager->addProperty(QVariant::Color, QString::number(i++) + QLatin1String(" Color Property")); + topItem->addSubProperty(item); + + QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(); + + QtTreePropertyBrowser *variantEditor = new QtTreePropertyBrowser(); + variantEditor->setFactoryForManager(variantManager, variantFactory); + variantEditor->addProperty(topItem); + variantEditor->setPropertiesWithoutValueMarked(true); + variantEditor->setRootIsDecorated(false); + + variantEditor->show(); + + int ret = app.exec(); + + delete variantManager; + delete variantFactory; + delete variantEditor; + + return ret; +} diff --git a/examples/simple/simple.qdoc b/examples/simple/simple.qdoc new file mode 100644 index 0000000..a2d7801 --- /dev/null +++ b/examples/simple/simple.qdoc @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/*! + \page qtpropertybrowser-example-simple.html + \title Simple Tree Property Browser Example + + \image simple.png + + This example shows how to present various properties using a + simple tree property browser, i.e. an implementation of the + QtTreePropertyBrowser class. + + The source files can be found in examples/simple directory of the package. +*/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..75f1145 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,116 @@ +# Tell CMake to run moc when necessary: +#set(CMAKE_AUTOMOC ON) + +# As moc files are generated in the binary dir, tell CMake +# to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +SET(KIT_SRCS + qtbuttonpropertybrowser.cpp + qtbuttonpropertybrowser.h + qteditorfactory.cpp + qteditorfactory.h + qtgroupboxpropertybrowser.cpp + qtgroupboxpropertybrowser.h + qtpropertybrowser.cpp + qtpropertybrowser.h + qtpropertybrowserutils.cpp + qtpropertybrowserutils_p.h + qtpropertymanager.cpp + qtpropertymanager.h + qttreepropertybrowser.cpp + qttreepropertybrowser.h + qtvariantproperty.cpp + qtvariantproperty.h + ) + +SET(KIT_MOC_SRCS + qtpropertybrowserutils_p.h + ) + +SET(KIT_UI_FORMS + ) + +SET(KIT_resources + qtpropertybrowser.qrc + ) + +if(QT5_FOUND) + QT5_WRAP_UI(KIT_UI_CPP ${KIT_UI_FORMS}) + QT5_WRAP_CPP(KIT_MOC_CPP ${KIT_MOC_SRCS}) + QT5_ADD_RESOURCES(KIT_QRC_SRCS ${KIT_resources}) +ELSE() + QT4_WRAP_UI(KIT_UI_CPP ${KIT_UI_FORMS}) + QT4_WRAP_CPP(KIT_MOC_CPP ${KIT_MOC_SRCS}) + QT4_ADD_RESOURCES(KIT_QRC_SRCS ${KIT_resources}) +ENDIF() + +SET(libname ${PROJECT_NAME}) +ADD_LIBRARY(${libname} STATIC + ${KIT_SRCS} + ${KIT_UI_CPP} + ${KIT_MOC_CPP} + ${KIT_QRC_SRCS} + ) + +target_include_directories(${libname} PRIVATE ${QT_INCLUDE_DIRS}) +target_compile_definitions(${libname} PRIVATE ${QT_COMPILE_DEFS}) +set_target_properties(${libname} PROPERTIES POSITION_INDEPENDENT_CODE ON) + + +SET(${PROJECT_NAME}_LINK_LIBRARIES ${QT_LIBRARIES}) +TARGET_LINK_LIBRARIES( + ${libname} + ${${PROJECT_NAME}_LINK_LIBRARIES} + ) + +# List of header that should go though moc +SET(KIT_HEADERS_MOC_SRCS + qtbuttonpropertybrowser.h + qteditorfactory.h + qtgroupboxpropertybrowser.h + qtpropertybrowser.h + qtpropertybrowserutils_p.h + qtpropertymanager.h + qttreepropertybrowser.h + qtvariantproperty.h + ) + +SET(KIT_MOC_CPP) +FOREACH(file ${KIT_HEADERS_MOC_SRCS}) + get_filename_component(filename_we ${file} NAME_WE) + SET(output_file moc_${filename_we}.cpp) + + IF(QT5_FOUND) + QT5_GENERATE_MOC(${file} ${output_file}) + ELSE() + QT4_GENERATE_MOC(${file} ${output_file}) + ENDIF() + + LIST(APPEND KIT_MOC_CPP ${output_file}) +ENDFOREACH() + +# List of cpp files that should go though moc +SET(KIT_CPP_MOC_SRCS + qteditorfactory.cpp + qtpropertymanager.cpp + qttreepropertybrowser.cpp + ) + +FOREACH(file ${KIT_CPP_MOC_SRCS}) + get_filename_component(filename_we ${file} NAME_WE) + SET(output_file ${filename_we}.moc) + + if(QT5_FOUND) + QT5_GENERATE_MOC(${file} ${output_file}) + ELSE() + QT4_GENERATE_MOC(${file} ${output_file}) + ENDIF() + + LIST(APPEND KIT_MOC_CPP ${output_file}) +ENDFOREACH() + +add_custom_target(${libname}GenerateMoc + DEPENDS ${KIT_MOC_CPP} + ) +add_dependencies(${libname} ${libname}GenerateMoc) diff --git a/src/QtAbstractEditorFactoryBase b/src/QtAbstractEditorFactoryBase new file mode 100644 index 0000000..ab4e710 --- /dev/null +++ b/src/QtAbstractEditorFactoryBase @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/src/QtAbstractPropertyBrowser b/src/QtAbstractPropertyBrowser new file mode 100644 index 0000000..ab4e710 --- /dev/null +++ b/src/QtAbstractPropertyBrowser @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/src/QtAbstractPropertyManager b/src/QtAbstractPropertyManager new file mode 100644 index 0000000..ab4e710 --- /dev/null +++ b/src/QtAbstractPropertyManager @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/src/QtBoolPropertyManager b/src/QtBoolPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtBoolPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtBrowserItem b/src/QtBrowserItem new file mode 100644 index 0000000..ab4e710 --- /dev/null +++ b/src/QtBrowserItem @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/src/QtButtonPropertyBrowser b/src/QtButtonPropertyBrowser new file mode 100644 index 0000000..56e0897 --- /dev/null +++ b/src/QtButtonPropertyBrowser @@ -0,0 +1 @@ +#include "qtbuttonpropertybrowser.h" diff --git a/src/QtCharEditorFactory b/src/QtCharEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtCharEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtCharPropertyManager b/src/QtCharPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtCharPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtCheckBoxFactory b/src/QtCheckBoxFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtCheckBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtColorEditorFactory b/src/QtColorEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtColorEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtColorPropertyManager b/src/QtColorPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtColorPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtCursorEditorFactory b/src/QtCursorEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtCursorEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtCursorPropertyManager b/src/QtCursorPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtCursorPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtDateEditFactory b/src/QtDateEditFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtDateEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtDatePropertyManager b/src/QtDatePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtDatePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtDateTimeEditFactory b/src/QtDateTimeEditFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtDateTimeEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtDateTimePropertyManager b/src/QtDateTimePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtDateTimePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtDoublePropertyManager b/src/QtDoublePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtDoublePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtDoubleSpinBoxFactory b/src/QtDoubleSpinBoxFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtDoubleSpinBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtEnumEditorFactory b/src/QtEnumEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtEnumEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtEnumPropertyManager b/src/QtEnumPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtEnumPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtFlagPropertyManager b/src/QtFlagPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtFlagPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtFontEditorFactory b/src/QtFontEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtFontEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtFontPropertyManager b/src/QtFontPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtFontPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtGroupBoxPropertyBrowser b/src/QtGroupBoxPropertyBrowser new file mode 100644 index 0000000..27964c0 --- /dev/null +++ b/src/QtGroupBoxPropertyBrowser @@ -0,0 +1 @@ +#include "qtgroupboxpropertybrowser.h" diff --git a/src/QtGroupPropertyManager b/src/QtGroupPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtGroupPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtIntPropertyManager b/src/QtIntPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtIntPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtKeySequenceEditorFactory b/src/QtKeySequenceEditorFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtKeySequenceEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtKeySequencePropertyManager b/src/QtKeySequencePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtKeySequencePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtLineEditFactory b/src/QtLineEditFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtLineEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtLocalePropertyManager b/src/QtLocalePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtLocalePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtPointFPropertyManager b/src/QtPointFPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtPointFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtPointPropertyManager b/src/QtPointPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtPointPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtProperty b/src/QtProperty new file mode 100644 index 0000000..ab4e710 --- /dev/null +++ b/src/QtProperty @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/src/QtRectFPropertyManager b/src/QtRectFPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtRectFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtRectPropertyManager b/src/QtRectPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtRectPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtScrollBarFactory b/src/QtScrollBarFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtScrollBarFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtSizeFPropertyManager b/src/QtSizeFPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtSizeFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtSizePolicyPropertyManager b/src/QtSizePolicyPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtSizePolicyPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtSizePropertyManager b/src/QtSizePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtSizePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtSliderFactory b/src/QtSliderFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtSliderFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtSpinBoxFactory b/src/QtSpinBoxFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtSpinBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtStringPropertyManager b/src/QtStringPropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtStringPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtTimeEditFactory b/src/QtTimeEditFactory new file mode 100644 index 0000000..75f35ad --- /dev/null +++ b/src/QtTimeEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/src/QtTimePropertyManager b/src/QtTimePropertyManager new file mode 100644 index 0000000..1842e43 --- /dev/null +++ b/src/QtTimePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/src/QtTreePropertyBrowser b/src/QtTreePropertyBrowser new file mode 100644 index 0000000..aab106c --- /dev/null +++ b/src/QtTreePropertyBrowser @@ -0,0 +1 @@ +#include "qttreepropertybrowser.h" diff --git a/src/QtVariantEditorFactory b/src/QtVariantEditorFactory new file mode 100644 index 0000000..8118190 --- /dev/null +++ b/src/QtVariantEditorFactory @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/src/QtVariantProperty b/src/QtVariantProperty new file mode 100644 index 0000000..8118190 --- /dev/null +++ b/src/QtVariantProperty @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/src/QtVariantPropertyManager b/src/QtVariantPropertyManager new file mode 100644 index 0000000..8118190 --- /dev/null +++ b/src/QtVariantPropertyManager @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/src/images/cursor-arrow.png b/src/images/cursor-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a69ef4eb6158503c7c67c916aea86e65fdc81f72 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2@N{tusfe>Zw~@EOfQR+sk_Oou&-fjcEVpj<&$!X3+c~E>J1*JV{%`IM zZgwlFFx}G*?uS>UO1?P#t>x;u4TbmfTg6u$kN3O$qTon=;uum9xAyEt-c|zv=78lpdFvQ2-{H`#GwYbpmwD+D`;ru|4Sly6 z!u|DfH10id;EhPQ?!aC0$4imznq|kzoDQ|k!q4v~%5Ey$Ds|~#XU>6Rfw7|8GuZ2t ub|G literal 0 HcmV?d00001 diff --git a/src/images/cursor-closedhand.png b/src/images/cursor-closedhand.png new file mode 100644 index 0000000000000000000000000000000000000000..b78dd1dac5a827fa698f1993718f22c282019505 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?iy0WWJ3*My{N(AiKtWee z7sn8d^J_1j=3-FbaJl&N|NMQrDg{-+lDl3uEOL6iyxxIhJsV5IbqBU04WsP|EmPEI sG+%tqc!TF=!@MiLH_pW!e7ccq3Qxekuk)up2O7xW>FVdQ&MBb@057mJumAu6 literal 0 HcmV?d00001 diff --git a/src/images/cursor-cross.png b/src/images/cursor-cross.png new file mode 100644 index 0000000000000000000000000000000000000000..fe38e744805f61abefcc555e07d399725c3fd7e6 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz=_jGX#;h346aDZ1vLF6&ZrWY3kt_3XM>{%UFuy$6%gUPvacWfgV8N9#n V_FPj?S`E~~;OXk;vd$@?2>`R9Dxv@Y literal 0 HcmV?d00001 diff --git a/src/images/cursor-forbidden.png b/src/images/cursor-forbidden.png new file mode 100644 index 0000000000000000000000000000000000000000..2b08c4e2a3cafc992459a8f484e6c8e6c3e74857 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMr#O@pN$vsfa5*w~^P`kb@;)w}Ny_&ztDY@kvlRIOU`3qAsV; zTo+`Y{*UQrTXjS>sKxPHd$OYcrG25=M&E9}?0tT1Yy7oqDMB$hU$uYoMqOpDnka4~ qygb6(Z%*F(qx}EZO#W7L6_Nk?#pi+cGI+ZBxvX&}>iqs75rOW+zd6_S+ yS|~6}xHpZ9x7T5rLt)D22WQ%mvv4FO#t=)GwlEX literal 0 HcmV?d00001 diff --git a/src/images/cursor-ibeam.png b/src/images/cursor-ibeam.png new file mode 100644 index 0000000000000000000000000000000000000000..097fc5fa7287da71ffd907b3a11adbda4516aca6 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqgTe~DWM4f DkdQT7 literal 0 HcmV?d00001 diff --git a/src/images/cursor-sizeall.png b/src/images/cursor-sizeall.png new file mode 100644 index 0000000000000000000000000000000000000000..69f13eb347a6c299e06844729a14f657b282fe8f GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq(Z!J(8lD*)YR5__{zaA2hTRnmr4+Dcjj*u+}&-pB%i42~uelF{r5}E+x CJu-a& literal 0 HcmV?d00001 diff --git a/src/images/cursor-sizef.png b/src/images/cursor-sizef.png new file mode 100644 index 0000000000000000000000000000000000000000..3b127a05d34b48a2f4f9b9cc77b681c6b43afcdd GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqD?iBlbDB7>)^pUXO@geCxo C3NS_h literal 0 HcmV?d00001 diff --git a/src/images/cursor-sizeh.png b/src/images/cursor-sizeh.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f40cbc3d77c566c11c32c0631b4f94f44dd441 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq&V7RQzr9bPir8&?922WQ%mvv4FO#sHOC(Zx> literal 0 HcmV?d00001 diff --git a/src/images/cursor-sizev.png b/src/images/cursor-sizev.png new file mode 100644 index 0000000000000000000000000000000000000000..1edbab27a5b05555aaf515931f69ad6bf7e417f0 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqM`U1 literal 0 HcmV?d00001 diff --git a/src/images/cursor-wait.png b/src/images/cursor-wait.png new file mode 100644 index 0000000000000000000000000000000000000000..69056c479e9b2f009e366dfd71999a7c74f97620 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2^mK6y;h346;J_ZVI&3Y=<%J7po~YS&;ev>WRgqc?pLgqp;N^OZ7kj)9 zt2OYRn)8&w&_Fn;AZ-e>OWiG&j$ap41wJoZG>h5TTvwNMesusN!}2q-U)D!*O8^aL N@O1TaS?83{1OR$JJ0<`C literal 0 HcmV?d00001 diff --git a/src/images/cursor-whatsthis.png b/src/images/cursor-whatsthis.png new file mode 100644 index 0000000000000000000000000000000000000000..b47601c3780eec780fdae43bab7481bbfebdddae GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*Z*JuL59RLGuTpf8VsdEq+OT~Tw87C;38om{D5bL fNHFu^21W*zFY +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtButtonPropertyBrowserPrivate +{ + QtButtonPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtButtonPropertyBrowser) +public: + + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + + void slotEditorDestroyed(); + void slotUpdate(); + void slotToggled(bool checked); + + struct WidgetItem + { + WidgetItem() : widget(0), label(0), widgetLabel(0), + button(0), container(0), layout(0), /*line(0), */parent(0), expanded(false) { } + QWidget *widget; // can be null + QLabel *label; // main label with property name + QLabel *widgetLabel; // label substitute showing the current value if there is no widget + QToolButton *button; // expandable button for items with children + QWidget *container; // container which is expanded when the button is clicked + QGridLayout *layout; // layout in container + WidgetItem *parent; + QList children; + bool expanded; + }; +private: + void updateLater(); + void updateItem(WidgetItem *item); + void insertRow(QGridLayout *layout, int row) const; + void removeRow(QGridLayout *layout, int row) const; + int gridRow(WidgetItem *item) const; + int gridSpan(WidgetItem *item) const; + void setExpanded(WidgetItem *item, bool expanded); + QToolButton *createButton(QWidget *panret = 0) const; + + QMap m_indexToItem; + QMap m_itemToIndex; + QMap m_widgetToItem; + QMap m_buttonToItem; + QGridLayout *m_mainLayout; + QList m_children; + QList m_recreateQueue; +}; + +QToolButton *QtButtonPropertyBrowserPrivate::createButton(QWidget *parent) const +{ + QToolButton *button = new QToolButton(parent); + button->setCheckable(true); + button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setArrowType(Qt::DownArrow); + button->setIconSize(QSize(3, 16)); + /* + QIcon icon; + icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowDown), QIcon::Normal, QIcon::Off); + icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowUp), QIcon::Normal, QIcon::On); + button->setIcon(icon); + */ + return button; +} + +int QtButtonPropertyBrowserPrivate::gridRow(WidgetItem *item) const +{ + QList siblings; + if (item->parent) + siblings = item->parent->children; + else + siblings = m_children; + + int row = 0; + QListIterator it(siblings); + while (it.hasNext()) { + WidgetItem *sibling = it.next(); + if (sibling == item) + return row; + row += gridSpan(sibling); + } + return -1; +} + +int QtButtonPropertyBrowserPrivate::gridSpan(WidgetItem *item) const +{ + if (item->container && item->expanded) + return 2; + return 1; +} + +void QtButtonPropertyBrowserPrivate::init(QWidget *parent) +{ + m_mainLayout = new QGridLayout(); + parent->setLayout(m_mainLayout); + QLayoutItem *item = new QSpacerItem(0, 0, + QSizePolicy::Fixed, QSizePolicy::Expanding); + m_mainLayout->addItem(item, 0, 0); +} + +void QtButtonPropertyBrowserPrivate::slotEditorDestroyed() +{ + QWidget *editor = qobject_cast(q_ptr->sender()); + if (!editor) + return; + if (!m_widgetToItem.contains(editor)) + return; + m_widgetToItem[editor]->widget = 0; + m_widgetToItem.remove(editor); +} + +void QtButtonPropertyBrowserPrivate::slotUpdate() +{ + QListIterator itItem(m_recreateQueue); + while (itItem.hasNext()) { + WidgetItem *item = itItem.next(); + + WidgetItem *parent = item->parent; + QWidget *w = 0; + QGridLayout *l = 0; + const int oldRow = gridRow(item); + if (parent) { + w = parent->container; + l = parent->layout; + } else { + w = q_ptr; + l = m_mainLayout; + } + + int span = 1; + if (!item->widget && !item->widgetLabel) + span = 2; + item->label = new QLabel(w); + item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + l->addWidget(item->label, oldRow, 0, 1, span); + + updateItem(item); + } + m_recreateQueue.clear(); +} + +void QtButtonPropertyBrowserPrivate::setExpanded(WidgetItem *item, bool expanded) +{ + if (item->expanded == expanded) + return; + + if (!item->container) + return; + + item->expanded = expanded; + const int row = gridRow(item); + WidgetItem *parent = item->parent; + QGridLayout *l = 0; + if (parent) + l = parent->layout; + else + l = m_mainLayout; + + if (expanded) { + insertRow(l, row + 1); + l->addWidget(item->container, row + 1, 0, 1, 2); + item->container->show(); + } else { + l->removeWidget(item->container); + item->container->hide(); + removeRow(l, row + 1); + } + + item->button->setChecked(expanded); + item->button->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow); +} + +void QtButtonPropertyBrowserPrivate::slotToggled(bool checked) +{ + WidgetItem *item = m_buttonToItem.value(q_ptr->sender()); + if (!item) + return; + + setExpanded(item, checked); + + if (checked) + emit q_ptr->expanded(m_itemToIndex.value(item)); + else + emit q_ptr->collapsed(m_itemToIndex.value(item)); +} + +void QtButtonPropertyBrowserPrivate::updateLater() +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); +} + +void QtButtonPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + WidgetItem *afterItem = m_indexToItem.value(afterIndex); + WidgetItem *parentItem = m_indexToItem.value(index->parent()); + + WidgetItem *newItem = new WidgetItem(); + newItem->parent = parentItem; + + QGridLayout *layout = 0; + QWidget *parentWidget = 0; + int row = -1; + if (!afterItem) { + row = 0; + if (parentItem) + parentItem->children.insert(0, newItem); + else + m_children.insert(0, newItem); + } else { + row = gridRow(afterItem) + gridSpan(afterItem); + if (parentItem) + parentItem->children.insert(parentItem->children.indexOf(afterItem) + 1, newItem); + else + m_children.insert(m_children.indexOf(afterItem) + 1, newItem); + } + + if (!parentItem) { + layout = m_mainLayout; + parentWidget = q_ptr; + } else { + if (!parentItem->container) { + m_recreateQueue.removeAll(parentItem); + WidgetItem *grandParent = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + const int oldRow = gridRow(parentItem); + if (grandParent) { + w = grandParent->container; + l = grandParent->layout; + } else { + w = q_ptr; + l = m_mainLayout; + } + QFrame *container = new QFrame(); + container->setFrameShape(QFrame::Panel); + container->setFrameShadow(QFrame::Raised); + parentItem->container = container; + parentItem->button = createButton(); + m_buttonToItem[parentItem->button] = parentItem; + q_ptr->connect(parentItem->button, SIGNAL(toggled(bool)), q_ptr, SLOT(slotToggled(bool))); + parentItem->layout = new QGridLayout(); + container->setLayout(parentItem->layout); + if (parentItem->label) { + l->removeWidget(parentItem->label); + delete parentItem->label; + parentItem->label = 0; + } + int span = 1; + if (!parentItem->widget && !parentItem->widgetLabel) + span = 2; + l->addWidget(parentItem->button, oldRow, 0, 1, span); + updateItem(parentItem); + } + layout = parentItem->layout; + parentWidget = parentItem->container; + } + + newItem->label = new QLabel(parentWidget); + newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + newItem->widget = createEditor(index->property(), parentWidget); + if (newItem->widget) { + QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); + m_widgetToItem[newItem->widget] = newItem; + } else if (index->property()->hasValue()) { + newItem->widgetLabel = new QLabel(parentWidget); + newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + } + + insertRow(layout, row); + int span = 1; + if (newItem->widget) + layout->addWidget(newItem->widget, row, 1); + else if (newItem->widgetLabel) + layout->addWidget(newItem->widgetLabel, row, 1); + else + span = 2; + layout->addWidget(newItem->label, row, 0, span, 1); + + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + updateItem(newItem); +} + +void QtButtonPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + + WidgetItem *parentItem = item->parent; + + const int row = gridRow(item); + + if (parentItem) + parentItem->children.removeAt(parentItem->children.indexOf(item)); + else + m_children.removeAt(m_children.indexOf(item)); + + const int colSpan = gridSpan(item); + + m_buttonToItem.remove(item->button); + + if (item->widget) + delete item->widget; + if (item->label) + delete item->label; + if (item->widgetLabel) + delete item->widgetLabel; + if (item->button) + delete item->button; + if (item->container) + delete item->container; + + if (!parentItem) { + removeRow(m_mainLayout, row); + if (colSpan > 1) + removeRow(m_mainLayout, row); + } else if (parentItem->children.count() != 0) { + removeRow(parentItem->layout, row); + if (colSpan > 1) + removeRow(parentItem->layout, row); + } else { + const WidgetItem *grandParent = parentItem->parent; + QGridLayout *l = 0; + if (grandParent) { + l = grandParent->layout; + } else { + l = m_mainLayout; + } + + const int parentRow = gridRow(parentItem); + const int parentSpan = gridSpan(parentItem); + + l->removeWidget(parentItem->button); + l->removeWidget(parentItem->container); + delete parentItem->button; + delete parentItem->container; + parentItem->button = 0; + parentItem->container = 0; + parentItem->layout = 0; + if (!m_recreateQueue.contains(parentItem)) + m_recreateQueue.append(parentItem); + if (parentSpan > 1) + removeRow(l, parentRow + 1); + + updateLater(); + } + m_recreateQueue.removeAll(item); + + delete item; +} + +void QtButtonPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r >= row) { + itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtButtonPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r > row) { + itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtButtonPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtButtonPropertyBrowserPrivate::updateItem(WidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + if (item->button) { + QFont font = item->button->font(); + font.setUnderline(property->isModified()); + item->button->setFont(font); + item->button->setText(property->propertyName()); + item->button->setToolTip(property->toolTip()); + item->button->setStatusTip(property->statusTip()); + item->button->setWhatsThis(property->whatsThis()); + item->button->setEnabled(property->isEnabled()); + } + if (item->label) { + QFont font = item->label->font(); + font.setUnderline(property->isModified()); + item->label->setFont(font); + item->label->setText(property->propertyName()); + item->label->setToolTip(property->toolTip()); + item->label->setStatusTip(property->statusTip()); + item->label->setWhatsThis(property->whatsThis()); + item->label->setEnabled(property->isEnabled()); + } + if (item->widgetLabel) { + QFont font = item->widgetLabel->font(); + font.setUnderline(false); + item->widgetLabel->setFont(font); + item->widgetLabel->setText(property->valueText()); + item->widgetLabel->setToolTip(property->valueText()); + item->widgetLabel->setEnabled(property->isEnabled()); + } + if (item->widget) { + QFont font = item->widget->font(); + font.setUnderline(false); + item->widget->setFont(font); + item->widget->setEnabled(property->isEnabled()); + item->widget->setToolTip(property->valueText()); + } +} + + + +/*! + \class QtButtonPropertyBrowser + + \brief The QtButtonPropertyBrowser class provides a drop down QToolButton + based property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtButtonPropertyBrowser provides drop down button for all nested + properties, i.e. subproperties are enclosed by a container associated with + the drop down button. The parent property's name is displayed as button text. For example: + + \image qtbuttonpropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtButtonPropertyBrowser + class. The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + \fn void QtButtonPropertyBrowser::collapsed(QtBrowserItem *item) + + This signal is emitted when the \a item is collapsed. + + \sa expanded(), setExpanded() +*/ + +/*! + \fn void QtButtonPropertyBrowser::expanded(QtBrowserItem *item) + + This signal is emitted when the \a item is expanded. + + \sa collapsed(), setExpanded() +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtButtonPropertyBrowser::QtButtonPropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtButtonPropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtButtonPropertyBrowser::~QtButtonPropertyBrowser() +{ + const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); + for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) + delete it.key(); + delete d_ptr; +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +/*! + Sets the \a item to either collapse or expanded, depending on the value of \a expanded. + + \sa isExpanded(), expanded(), collapsed() +*/ + +void QtButtonPropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) +{ + QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); + if (itm) + d_ptr->setExpanded(itm, expanded); +} + +/*! + Returns true if the \a item is expanded; otherwise returns false. + + \sa setExpanded() +*/ + +bool QtButtonPropertyBrowser::isExpanded(QtBrowserItem *item) const +{ + QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); + if (itm) + return itm->expanded; + return false; +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtbuttonpropertybrowser.cpp" diff --git a/src/qtbuttonpropertybrowser.h b/src/qtbuttonpropertybrowser.h new file mode 100644 index 0000000..c46a458 --- /dev/null +++ b/src/qtbuttonpropertybrowser.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTBUTTONPROPERTYBROWSER_H +#define QTBUTTONPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtButtonPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtButtonPropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT +public: + + QtButtonPropertyBrowser(QWidget *parent = 0); + ~QtButtonPropertyBrowser(); + + void setExpanded(QtBrowserItem *item, bool expanded); + bool isExpanded(QtBrowserItem *item) const; + +Q_SIGNALS: + + void collapsed(QtBrowserItem *item); + void expanded(QtBrowserItem *item); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtButtonPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtButtonPropertyBrowser) + Q_DISABLE_COPY(QtButtonPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) + Q_PRIVATE_SLOT(d_func(), void slotToggled(bool)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qteditorfactory.cpp b/src/qteditorfactory.cpp new file mode 100644 index 0000000..a2ef86c --- /dev/null +++ b/src/qteditorfactory.cpp @@ -0,0 +1,2579 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qteditorfactory.h" +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +// Set a hard coded left margin to account for the indentation +// of the tree view icon when switching to an editor + +static inline void setupTreeViewEditorMargin(QLayout *lt) +{ + enum { DecorationMargin = 4 }; + if (QApplication::layoutDirection() == Qt::LeftToRight) + lt->setContentsMargins(DecorationMargin, 0, 0, 0); + else + lt->setContentsMargins(0, 0, DecorationMargin, 0); +} + +// ---------- EditorFactoryPrivate : +// Base class for editor factory private classes. Manages mapping of properties to editors and vice versa. + +template +class EditorFactoryPrivate +{ +public: + + typedef QList EditorList; + typedef QMap PropertyToEditorListMap; + typedef QMap EditorToPropertyMap; + + Editor *createEditor(QtProperty *property, QWidget *parent); + void initializeEditor(QtProperty *property, Editor *e); + void slotEditorDestroyed(QObject *object); + + PropertyToEditorListMap m_createdEditors; + EditorToPropertyMap m_editorToProperty; +}; + +template +Editor *EditorFactoryPrivate::createEditor(QtProperty *property, QWidget *parent) +{ + Editor *editor = new Editor(parent); + initializeEditor(property, editor); + return editor; +} + +template +void EditorFactoryPrivate::initializeEditor(QtProperty *property, Editor *editor) +{ + typename PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + it = m_createdEditors.insert(property, EditorList()); + it.value().append(editor); + m_editorToProperty.insert(editor, property); +} + +template +void EditorFactoryPrivate::slotEditorDestroyed(QObject *object) +{ + const typename EditorToPropertyMap::iterator ecend = m_editorToProperty.end(); + for (typename EditorToPropertyMap::iterator itEditor = m_editorToProperty.begin(); itEditor != ecend; ++itEditor) { + if (itEditor.key() == object) { + Editor *editor = itEditor.key(); + QtProperty *property = itEditor.value(); + const typename PropertyToEditorListMap::iterator pit = m_createdEditors.find(property); + if (pit != m_createdEditors.end()) { + pit.value().removeAll(editor); + if (pit.value().empty()) + m_createdEditors.erase(pit); + } + m_editorToProperty.erase(itEditor); + return; + } + } +} + +// ------------ QtSpinBoxFactory + +class QtSpinBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtSpinBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtSpinBoxFactory) +public: + + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + if (editor->value() != value) { + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } + } +} + +void QtSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtSpinBoxFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! + \class QtSpinBoxFactory + + \brief The QtSpinBoxFactory class provides QSpinBox widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtSpinBoxFactory::QtSpinBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtSpinBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtSpinBoxFactory::~QtSpinBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSpinBoxFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtSpinBoxFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QSpinBox *editor = d_ptr->createEditor(property, parent); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + editor->setKeyboardTracking(false); + + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSpinBoxFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtSliderFactory + +class QtSliderFactoryPrivate : public EditorFactoryPrivate +{ + QtSliderFactory *q_ptr; + Q_DECLARE_PUBLIC(QtSliderFactory) +public: + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtSliderFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor ) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! + \class QtSliderFactory + + \brief The QtSliderFactory class provides QSlider widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtSliderFactory::QtSliderFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtSliderFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtSliderFactory::~QtSliderFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSliderFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtSliderFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QSlider *editor = new QSlider(Qt::Horizontal, parent); + d_ptr->initializeEditor(property, editor); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSliderFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtSliderFactory + +class QtScrollBarFactoryPrivate : public EditorFactoryPrivate +{ + QtScrollBarFactory *q_ptr; + Q_DECLARE_PUBLIC(QtScrollBarFactory) +public: + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtScrollBarFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtScrollBarFactory + + \brief The QtScrollBarFactory class provides QScrollBar widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtScrollBarFactory::QtScrollBarFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtScrollBarFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtScrollBarFactory::~QtScrollBarFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtScrollBarFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtScrollBarFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QScrollBar *editor = new QScrollBar(Qt::Horizontal, parent); + d_ptr->initializeEditor(property, editor); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtScrollBarFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtCheckBoxFactory + +class QtCheckBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtCheckBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCheckBoxFactory) +public: + void slotPropertyChanged(QtProperty *property, bool value); + void slotSetValue(bool value); +}; + +void QtCheckBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, bool value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtBoolEdit *editor = itEditor.next(); + editor->blockCheckBoxSignals(true); + editor->setChecked(value); + editor->blockCheckBoxSignals(false); + } +} + +void QtCheckBoxFactoryPrivate::slotSetValue(bool value) +{ + QObject *object = q_ptr->sender(); + + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtBoolPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtCheckBoxFactory + + \brief The QtCheckBoxFactory class provides QCheckBox widgets for + properties created by QtBoolPropertyManager objects. + + \sa QtAbstractEditorFactory, QtBoolPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCheckBoxFactory::QtCheckBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCheckBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCheckBoxFactory::~QtCheckBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCheckBoxFactory::connectPropertyManager(QtBoolPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotPropertyChanged(QtProperty *, bool))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCheckBoxFactory::createEditor(QtBoolPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QtBoolEdit *editor = d_ptr->createEditor(property, parent); + editor->setChecked(manager->value(property)); + + connect(editor, SIGNAL(toggled(bool)), this, SLOT(slotSetValue(bool))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCheckBoxFactory::disconnectPropertyManager(QtBoolPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotPropertyChanged(QtProperty *, bool))); +} + +// QtDoubleSpinBoxFactory + +class QtDoubleSpinBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtDoubleSpinBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDoubleSpinBoxFactory) +public: + + void slotPropertyChanged(QtProperty *property, double value); + void slotRangeChanged(QtProperty *property, double min, double max); + void slotSingleStepChanged(QtProperty *property, double step); + void slotDecimalsChanged(QtProperty *property, int prec); + void slotSetValue(double value); +}; + +void QtDoubleSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, double value) +{ + QList editors = m_createdEditors[property]; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + if (editor->value() != value) { + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, + double min, double max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, double step) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotDecimalsChanged(QtProperty *property, int prec) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDecimals(prec); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotSetValue(double value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator itcend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != itcend; ++itEditor) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! \class QtDoubleSpinBoxFactory + + \brief The QtDoubleSpinBoxFactory class provides QDoubleSpinBox + widgets for properties created by QtDoublePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDoublePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDoubleSpinBoxFactory::QtDoubleSpinBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDoubleSpinBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDoubleSpinBoxFactory::~QtDoubleSpinBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDoubleSpinBoxFactory::connectPropertyManager(QtDoublePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotPropertyChanged(QtProperty *, double))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + connect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDoubleSpinBoxFactory::createEditor(QtDoublePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QDoubleSpinBox *editor = d_ptr->createEditor(property, parent); + editor->setSingleStep(manager->singleStep(property)); + editor->setDecimals(manager->decimals(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + editor->setKeyboardTracking(false); + + connect(editor, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDoubleSpinBoxFactory::disconnectPropertyManager(QtDoublePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotPropertyChanged(QtProperty *, double))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + disconnect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); +} + +// QtLineEditFactory + +class QtLineEditFactoryPrivate : public EditorFactoryPrivate +{ + QtLineEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtLineEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QString &value); + void slotRegExpChanged(QtProperty *property, const QRegExp ®Exp); + void slotSetValue(const QString &value); +}; + +void QtLineEditFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QString &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QLineEdit *editor = itEditor.next(); + if (editor->text() != value) + editor->setText(value); + } +} + +void QtLineEditFactoryPrivate::slotRegExpChanged(QtProperty *property, + const QRegExp ®Exp) +{ + if (!m_createdEditors.contains(property)) + return; + + QtStringPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QLineEdit *editor = itEditor.next(); + editor->blockSignals(true); + const QValidator *oldValidator = editor->validator(); + QValidator *newValidator = 0; + if (regExp.isValid()) { + newValidator = new QRegExpValidator(regExp, editor); + } + editor->setValidator(newValidator); + if (oldValidator) + delete oldValidator; + editor->blockSignals(false); + } +} + +void QtLineEditFactoryPrivate::slotSetValue(const QString &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtStringPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtLineEditFactory + + \brief The QtLineEditFactory class provides QLineEdit widgets for + properties created by QtStringPropertyManager objects. + + \sa QtAbstractEditorFactory, QtStringPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtLineEditFactory::QtLineEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtLineEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtLineEditFactory::~QtLineEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtLineEditFactory::connectPropertyManager(QtStringPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); + connect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtLineEditFactory::createEditor(QtStringPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + + QLineEdit *editor = d_ptr->createEditor(property, parent); + QRegExp regExp = manager->regExp(property); + if (regExp.isValid()) { + QValidator *validator = new QRegExpValidator(regExp, editor); + editor->setValidator(validator); + } + editor->setText(manager->value(property)); + + connect(editor, SIGNAL(textEdited(const QString &)), + this, SLOT(slotSetValue(const QString &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtLineEditFactory::disconnectPropertyManager(QtStringPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); + disconnect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); +} + +// QtDateEditFactory + +class QtDateEditFactoryPrivate : public EditorFactoryPrivate +{ + QtDateEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDateEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QDate &value); + void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); + void slotSetValue(const QDate &value); +}; + +void QtDateEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QDate &value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDate(value); + editor->blockSignals(false); + } +} + +void QtDateEditFactoryPrivate::slotRangeChanged(QtProperty *property, + const QDate &min, const QDate &max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDatePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDateRange(min, max); + editor->setDate(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDateEditFactoryPrivate::slotSetValue(const QDate &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDatePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtDateEditFactory + + \brief The QtDateEditFactory class provides QDateEdit widgets for + properties created by QtDatePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDatePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDateEditFactory::QtDateEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDateEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDateEditFactory::~QtDateEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateEditFactory::connectPropertyManager(QtDatePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDateEditFactory::createEditor(QtDatePropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QDateEdit *editor = d_ptr->createEditor(property, parent); + editor->setCalendarPopup(true); + editor->setDateRange(manager->minimum(property), manager->maximum(property)); + editor->setDate(manager->value(property)); + + connect(editor, SIGNAL(dateChanged(const QDate &)), + this, SLOT(slotSetValue(const QDate &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateEditFactory::disconnectPropertyManager(QtDatePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); +} + +// QtTimeEditFactory + +class QtTimeEditFactoryPrivate : public EditorFactoryPrivate +{ + QtTimeEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtTimeEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QTime &value); + void slotSetValue(const QTime &value); +}; + +void QtTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QTime &value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QTimeEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setTime(value); + editor->blockSignals(false); + } +} + +void QtTimeEditFactoryPrivate::slotSetValue(const QTime &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtTimePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtTimeEditFactory + + \brief The QtTimeEditFactory class provides QTimeEdit widgets for + properties created by QtTimePropertyManager objects. + + \sa QtAbstractEditorFactory, QtTimePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtTimeEditFactory::QtTimeEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtTimeEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtTimeEditFactory::~QtTimeEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtTimeEditFactory::connectPropertyManager(QtTimePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtTimeEditFactory::createEditor(QtTimePropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QTimeEdit *editor = d_ptr->createEditor(property, parent); + editor->setTime(manager->value(property)); + + connect(editor, SIGNAL(timeChanged(const QTime &)), + this, SLOT(slotSetValue(const QTime &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtTimeEditFactory::disconnectPropertyManager(QtTimePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); +} + +// QtDateTimeEditFactory + +class QtDateTimeEditFactoryPrivate : public EditorFactoryPrivate +{ + QtDateTimeEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDateTimeEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QDateTime &value); + void slotSetValue(const QDateTime &value); + +}; + +void QtDateTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QDateTime &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateTimeEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDateTime(value); + editor->blockSignals(false); + } +} + +void QtDateTimeEditFactoryPrivate::slotSetValue(const QDateTime &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDateTimePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtDateTimeEditFactory + + \brief The QtDateTimeEditFactory class provides QDateTimeEdit + widgets for properties created by QtDateTimePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDateTimePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDateTimeEditFactory::QtDateTimeEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDateTimeEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDateTimeEditFactory::~QtDateTimeEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateTimeEditFactory::connectPropertyManager(QtDateTimePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDateTimeEditFactory::createEditor(QtDateTimePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QDateTimeEdit *editor = d_ptr->createEditor(property, parent); + editor->setDateTime(manager->value(property)); + + connect(editor, SIGNAL(dateTimeChanged(const QDateTime &)), + this, SLOT(slotSetValue(const QDateTime &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateTimeEditFactory::disconnectPropertyManager(QtDateTimePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); +} + +// QtKeySequenceEditorFactory + +class QtKeySequenceEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtKeySequenceEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtKeySequenceEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QKeySequence &value); + void slotSetValue(const QKeySequence &value); +}; + +void QtKeySequenceEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QKeySequence &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtKeySequenceEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setKeySequence(value); + editor->blockSignals(false); + } +} + +void QtKeySequenceEditorFactoryPrivate::slotSetValue(const QKeySequence &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtKeySequencePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtKeySequenceEditorFactory + + \brief The QtKeySequenceEditorFactory class provides editor + widgets for properties created by QtKeySequencePropertyManager objects. + + \sa QtAbstractEditorFactory +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtKeySequenceEditorFactory::QtKeySequenceEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtKeySequenceEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtKeySequenceEditorFactory::~QtKeySequenceEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtKeySequenceEditorFactory::connectPropertyManager(QtKeySequencePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtKeySequenceEditorFactory::createEditor(QtKeySequencePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtKeySequenceEdit *editor = d_ptr->createEditor(property, parent); + editor->setKeySequence(manager->value(property)); + + connect(editor, SIGNAL(keySequenceChanged(const QKeySequence &)), + this, SLOT(slotSetValue(const QKeySequence &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtKeySequenceEditorFactory::disconnectPropertyManager(QtKeySequencePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); +} + +// QtCharEdit + +class QtCharEdit : public QWidget +{ + Q_OBJECT +public: + QtCharEdit(QWidget *parent = 0); + + QChar value() const; + bool eventFilter(QObject *o, QEvent *e); +public Q_SLOTS: + void setValue(const QChar &value); +Q_SIGNALS: + void valueChanged(const QChar &value); +protected: + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *); + bool event(QEvent *e); +private slots: + void slotClearChar(); +private: + void handleKeyEvent(QKeyEvent *e); + + QChar m_value; + QLineEdit *m_lineEdit; +}; + +QtCharEdit::QtCharEdit(QWidget *parent) + : QWidget(parent), m_lineEdit(new QLineEdit(this)) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_lineEdit); + layout->setMargin(0); + m_lineEdit->installEventFilter(this); + m_lineEdit->setReadOnly(true); + m_lineEdit->setFocusProxy(this); + setFocusPolicy(m_lineEdit->focusPolicy()); + setAttribute(Qt::WA_InputMethodEnabled); +} + +bool QtCharEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o == m_lineEdit && e->type() == QEvent::ContextMenu) { + QContextMenuEvent *c = static_cast(e); + QMenu *menu = m_lineEdit->createStandardContextMenu(); + QList actions = menu->actions(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + action->setShortcut(QKeySequence()); + QString actionString = action->text(); + const int pos = actionString.lastIndexOf(QLatin1Char('\t')); + if (pos > 0) + actionString = actionString.remove(pos, actionString.length() - pos); + action->setText(actionString); + } + QAction *actionBefore = 0; + if (actions.count() > 0) + actionBefore = actions[0]; + QAction *clearAction = new QAction(tr("Clear Char"), menu); + menu->insertAction(actionBefore, clearAction); + menu->insertSeparator(actionBefore); + clearAction->setEnabled(!m_value.isNull()); + connect(clearAction, SIGNAL(triggered()), this, SLOT(slotClearChar())); + menu->exec(c->globalPos()); + delete menu; + e->accept(); + return true; + } + + return QWidget::eventFilter(o, e); +} + +void QtCharEdit::slotClearChar() +{ + if (m_value.isNull()) + return; + setValue(QChar()); + emit valueChanged(m_value); +} + +void QtCharEdit::handleKeyEvent(QKeyEvent *e) +{ + const int key = e->key(); + switch (key) { + case Qt::Key_Control: + case Qt::Key_Shift: + case Qt::Key_Meta: + case Qt::Key_Alt: + case Qt::Key_Super_L: + case Qt::Key_Return: + return; + default: + break; + } + + const QString text = e->text(); + if (text.count() != 1) + return; + + const QChar c = text.at(0); + if (!c.isPrint()) + return; + + if (m_value == c) + return; + + m_value = c; + const QString str = m_value.isNull() ? QString() : QString(m_value); + m_lineEdit->setText(str); + e->accept(); + emit valueChanged(m_value); +} + +void QtCharEdit::setValue(const QChar &value) +{ + if (value == m_value) + return; + + m_value = value; + QString str = value.isNull() ? QString() : QString(value); + m_lineEdit->setText(str); +} + +QChar QtCharEdit::value() const +{ + return m_value; +} + +void QtCharEdit::focusInEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + m_lineEdit->selectAll(); + QWidget::focusInEvent(e); +} + +void QtCharEdit::focusOutEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + QWidget::focusOutEvent(e); +} + +void QtCharEdit::keyPressEvent(QKeyEvent *e) +{ + handleKeyEvent(e); + e->accept(); +} + +void QtCharEdit::keyReleaseEvent(QKeyEvent *e) +{ + m_lineEdit->event(e); +} + +void QtCharEdit::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +bool QtCharEdit::event(QEvent *e) +{ + switch(e->type()) { + case QEvent::Shortcut: + case QEvent::ShortcutOverride: + case QEvent::KeyRelease: + e->accept(); + return true; + default: + break; + } + return QWidget::event(e); +} + +// QtCharEditorFactory + +class QtCharEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtCharEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCharEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QChar &value); + void slotSetValue(const QChar &value); + +}; + +void QtCharEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QChar &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtCharEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtCharEditorFactoryPrivate::slotSetValue(const QChar &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtCharPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtCharEditorFactory + + \brief The QtCharEditorFactory class provides editor + widgets for properties created by QtCharPropertyManager objects. + + \sa QtAbstractEditorFactory +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCharEditorFactory::QtCharEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCharEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCharEditorFactory::~QtCharEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCharEditorFactory::connectPropertyManager(QtCharPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCharEditorFactory::createEditor(QtCharPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtCharEdit *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + + connect(editor, SIGNAL(valueChanged(const QChar &)), + this, SLOT(slotSetValue(const QChar &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCharEditorFactory::disconnectPropertyManager(QtCharPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); +} + +// QtEnumEditorFactory + +class QtEnumEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtEnumEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtEnumEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, int value); + void slotEnumNamesChanged(QtProperty *property, const QStringList &); + void slotEnumIconsChanged(QtProperty *property, const QMap &); + void slotSetValue(int value); +}; + +void QtEnumEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setCurrentIndex(value); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotEnumNamesChanged(QtProperty *property, + const QStringList &enumNames) +{ + if (!m_createdEditors.contains(property)) + return; + + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QMap enumIcons = manager->enumIcons(property); + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->clear(); + editor->addItems(enumNames); + const int nameCount = enumNames.count(); + for (int i = 0; i < nameCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotEnumIconsChanged(QtProperty *property, + const QMap &enumIcons) +{ + if (!m_createdEditors.contains(property)) + return; + + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + const QStringList enumNames = manager->enumNames(property); + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + const int nameCount = enumNames.count(); + for (int i = 0; i < nameCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtEnumEditorFactory + + \brief The QtEnumEditorFactory class provides QComboBox widgets for + properties created by QtEnumPropertyManager objects. + + \sa QtAbstractEditorFactory, QtEnumPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtEnumEditorFactory::QtEnumEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtEnumEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtEnumEditorFactory::~QtEnumEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtEnumEditorFactory::connectPropertyManager(QtEnumPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtEnumEditorFactory::createEditor(QtEnumPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QComboBox *editor = d_ptr->createEditor(property, parent); + editor->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + editor->setMinimumContentsLength(1); + editor->view()->setTextElideMode(Qt::ElideRight); + QStringList enumNames = manager->enumNames(property); + editor->addItems(enumNames); + QMap enumIcons = manager->enumIcons(property); + const int enumNamesCount = enumNames.count(); + for (int i = 0; i < enumNamesCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + + connect(editor, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtEnumEditorFactory::disconnectPropertyManager(QtEnumPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); +} + +// QtCursorEditorFactory + +Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) + +class QtCursorEditorFactoryPrivate +{ + QtCursorEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCursorEditorFactory) +public: + QtCursorEditorFactoryPrivate(); + + void slotPropertyChanged(QtProperty *property, const QCursor &cursor); + void slotEnumChanged(QtProperty *property, int value); + void slotEditorDestroyed(QObject *object); + + QtEnumEditorFactory *m_enumEditorFactory; + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToEnum; + QMap m_enumToProperty; + QMap > m_enumToEditors; + QMap m_editorToEnum; + bool m_updatingEnum; +}; + +QtCursorEditorFactoryPrivate::QtCursorEditorFactoryPrivate() + : m_updatingEnum(false) +{ + +} + +void QtCursorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, const QCursor &cursor) +{ + // update enum property + QtProperty *enumProp = m_propertyToEnum.value(property); + if (!enumProp) + return; + + m_updatingEnum = true; + m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(cursor)); + m_updatingEnum = false; +} + +void QtCursorEditorFactoryPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (m_updatingEnum) + return; + // update cursor property + QtProperty *prop = m_enumToProperty.value(property); + if (!prop) + return; + QtCursorPropertyManager *cursorManager = q_ptr->propertyManager(prop); + if (!cursorManager) + return; +#ifndef QT_NO_CURSOR + cursorManager->setValue(prop, QCursor(cursorDatabase()->valueToCursor(value))); +#endif +} + +void QtCursorEditorFactoryPrivate::slotEditorDestroyed(QObject *object) +{ + // remove from m_editorToEnum map; + // remove from m_enumToEditors map; + // if m_enumToEditors doesn't contains more editors delete enum property; + const QMap::ConstIterator ecend = m_editorToEnum.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToEnum.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QWidget *editor = itEditor.key(); + QtProperty *enumProp = itEditor.value(); + m_editorToEnum.remove(editor); + m_enumToEditors[enumProp].removeAll(editor); + if (m_enumToEditors[enumProp].isEmpty()) { + m_enumToEditors.remove(enumProp); + QtProperty *property = m_enumToProperty.value(enumProp); + m_enumToProperty.remove(enumProp); + m_propertyToEnum.remove(property); + delete enumProp; + } + return; + } +} + +/*! + \class QtCursorEditorFactory + + \brief The QtCursorEditorFactory class provides QComboBox widgets for + properties created by QtCursorPropertyManager objects. + + \sa QtAbstractEditorFactory, QtCursorPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCursorEditorFactory::QtCursorEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCursorEditorFactoryPrivate(); + d_ptr->q_ptr = this; + + d_ptr->m_enumEditorFactory = new QtEnumEditorFactory(this); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + d_ptr->m_enumEditorFactory->addPropertyManager(d_ptr->m_enumPropertyManager); +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCursorEditorFactory::~QtCursorEditorFactory() +{ + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCursorEditorFactory::connectPropertyManager(QtCursorPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCursorEditorFactory::createEditor(QtCursorPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QtProperty *enumProp = 0; + if (d_ptr->m_propertyToEnum.contains(property)) { + enumProp = d_ptr->m_propertyToEnum[property]; + } else { + enumProp = d_ptr->m_enumPropertyManager->addProperty(property->propertyName()); + d_ptr->m_enumPropertyManager->setEnumNames(enumProp, cursorDatabase()->cursorShapeNames()); + d_ptr->m_enumPropertyManager->setEnumIcons(enumProp, cursorDatabase()->cursorShapeIcons()); +#ifndef QT_NO_CURSOR + d_ptr->m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(manager->value(property))); +#endif + d_ptr->m_propertyToEnum[property] = enumProp; + d_ptr->m_enumToProperty[enumProp] = property; + } + QtAbstractEditorFactoryBase *af = d_ptr->m_enumEditorFactory; + QWidget *editor = af->createEditor(enumProp, parent); + d_ptr->m_enumToEditors[enumProp].append(editor); + d_ptr->m_editorToEnum[editor] = enumProp; + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCursorEditorFactory::disconnectPropertyManager(QtCursorPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); +} + +// QtColorEditWidget + +class QtColorEditWidget : public QWidget { + Q_OBJECT + +public: + QtColorEditWidget(QWidget *parent); + + bool eventFilter(QObject *obj, QEvent *ev); + +public Q_SLOTS: + void setValue(const QColor &value); + +Q_SIGNALS: + void valueChanged(const QColor &value); + +protected: + void paintEvent(QPaintEvent *); + +private Q_SLOTS: + void buttonClicked(); + +private: + QColor m_color; + QLabel *m_pixmapLabel; + QLabel *m_label; + QToolButton *m_button; +}; + +QtColorEditWidget::QtColorEditWidget(QWidget *parent) : + QWidget(parent), + m_pixmapLabel(new QLabel), + m_label(new QLabel), + m_button(new QToolButton) +{ + QHBoxLayout *lt = new QHBoxLayout(this); + setupTreeViewEditorMargin(lt); + lt->setSpacing(0); + lt->addWidget(m_pixmapLabel); + lt->addWidget(m_label); + lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(20); + setFocusProxy(m_button); + setFocusPolicy(m_button->focusPolicy()); + m_button->setText(tr("...")); + m_button->installEventFilter(this); + connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); + lt->addWidget(m_button); + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(m_color))); + m_label->setText(QtPropertyBrowserUtils::colorValueText(m_color)); +} + +void QtColorEditWidget::setValue(const QColor &c) +{ + if (m_color != c) { + m_color = c; + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(c))); + m_label->setText(QtPropertyBrowserUtils::colorValueText(c)); + } +} + +void QtColorEditWidget::buttonClicked() +{ + bool ok = false; + QRgb oldRgba = m_color.rgba(); + QRgb newRgba = QColorDialog::getRgba(oldRgba, &ok, this); + if (ok && newRgba != oldRgba) { + setValue(QColor::fromRgba(newRgba)); + emit valueChanged(m_color); + } +} + +bool QtColorEditWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == m_button) { + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate + switch (static_cast(ev)->key()) { + case Qt::Key_Escape: + case Qt::Key_Enter: + case Qt::Key_Return: + ev->ignore(); + return true; + default: + break; + } + } + break; + default: + break; + } + } + return QWidget::eventFilter(obj, ev); +} + +void QtColorEditWidget::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +// QtColorEditorFactoryPrivate + +class QtColorEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtColorEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtColorEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QColor &value); + void slotSetValue(const QColor &value); +}; + +void QtColorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QColor &value) +{ + const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + return; + QListIterator itEditor(it.value()); + + while (itEditor.hasNext()) + itEditor.next()->setValue(value); +} + +void QtColorEditorFactoryPrivate::slotSetValue(const QColor &value) +{ + QObject *object = q_ptr->sender(); + const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtColorPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtColorEditorFactory + + \brief The QtColorEditorFactory class provides color editing for + properties created by QtColorPropertyManager objects. + + \sa QtAbstractEditorFactory, QtColorPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtColorEditorFactory::QtColorEditorFactory(QObject *parent) : + QtAbstractEditorFactory(parent), + d_ptr(new QtColorEditorFactoryPrivate()) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtColorEditorFactory::~QtColorEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtColorEditorFactory::connectPropertyManager(QtColorPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), + this, SLOT(slotPropertyChanged(QtProperty*,QColor))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtColorEditorFactory::createEditor(QtColorPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtColorEditWidget *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(QColor)), this, SLOT(slotSetValue(QColor))); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtColorEditorFactory::disconnectPropertyManager(QtColorPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), this, SLOT(slotPropertyChanged(QtProperty*,QColor))); +} + +// QtFontEditWidget + +class QtFontEditWidget : public QWidget { + Q_OBJECT + +public: + QtFontEditWidget(QWidget *parent); + + bool eventFilter(QObject *obj, QEvent *ev); + +public Q_SLOTS: + void setValue(const QFont &value); + +Q_SIGNALS: + void valueChanged(const QFont &value); + +protected: + void paintEvent(QPaintEvent *); + +private Q_SLOTS: + void buttonClicked(); + +private: + QFont m_font; + QLabel *m_pixmapLabel; + QLabel *m_label; + QToolButton *m_button; +}; + +QtFontEditWidget::QtFontEditWidget(QWidget *parent) : + QWidget(parent), + m_pixmapLabel(new QLabel), + m_label(new QLabel), + m_button(new QToolButton) +{ + QHBoxLayout *lt = new QHBoxLayout(this); + setupTreeViewEditorMargin(lt); + lt->setSpacing(0); + lt->addWidget(m_pixmapLabel); + lt->addWidget(m_label); + lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(20); + setFocusProxy(m_button); + setFocusPolicy(m_button->focusPolicy()); + m_button->setText(tr("...")); + m_button->installEventFilter(this); + connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); + lt->addWidget(m_button); + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(m_font)); + m_label->setText(QtPropertyBrowserUtils::fontValueText(m_font)); +} + +void QtFontEditWidget::setValue(const QFont &f) +{ + if (m_font != f) { + m_font = f; + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(f)); + m_label->setText(QtPropertyBrowserUtils::fontValueText(f)); + } +} + +void QtFontEditWidget::buttonClicked() +{ + bool ok = false; + QFont newFont = QFontDialog::getFont(&ok, m_font, this, tr("Select Font")); + if (ok && newFont != m_font) { + QFont f = m_font; + // prevent mask for unchanged attributes, don't change other attributes (like kerning, etc...) + if (m_font.family() != newFont.family()) + f.setFamily(newFont.family()); + if (m_font.pointSize() != newFont.pointSize()) + f.setPointSize(newFont.pointSize()); + if (m_font.bold() != newFont.bold()) + f.setBold(newFont.bold()); + if (m_font.italic() != newFont.italic()) + f.setItalic(newFont.italic()); + if (m_font.underline() != newFont.underline()) + f.setUnderline(newFont.underline()); + if (m_font.strikeOut() != newFont.strikeOut()) + f.setStrikeOut(newFont.strikeOut()); + setValue(f); + emit valueChanged(m_font); + } +} + +bool QtFontEditWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == m_button) { + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate + switch (static_cast(ev)->key()) { + case Qt::Key_Escape: + case Qt::Key_Enter: + case Qt::Key_Return: + ev->ignore(); + return true; + default: + break; + } + } + break; + default: + break; + } + } + return QWidget::eventFilter(obj, ev); +} + +void QtFontEditWidget::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +// QtFontEditorFactoryPrivate + +class QtFontEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtFontEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtFontEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QFont &value); + void slotSetValue(const QFont &value); +}; + +void QtFontEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QFont &value) +{ + const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + return; + QListIterator itEditor(it.value()); + + while (itEditor.hasNext()) + itEditor.next()->setValue(value); +} + +void QtFontEditorFactoryPrivate::slotSetValue(const QFont &value) +{ + QObject *object = q_ptr->sender(); + const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtFontPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtFontEditorFactory + + \brief The QtFontEditorFactory class provides font editing for + properties created by QtFontPropertyManager objects. + + \sa QtAbstractEditorFactory, QtFontPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtFontEditorFactory::QtFontEditorFactory(QObject *parent) : + QtAbstractEditorFactory(parent), + d_ptr(new QtFontEditorFactoryPrivate()) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtFontEditorFactory::~QtFontEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtFontEditorFactory::connectPropertyManager(QtFontPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), + this, SLOT(slotPropertyChanged(QtProperty*,QFont))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtFontEditorFactory::createEditor(QtFontPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtFontEditWidget *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(QFont)), this, SLOT(slotSetValue(QFont))); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtFontEditorFactory::disconnectPropertyManager(QtFontPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), this, SLOT(slotPropertyChanged(QtProperty*,QFont))); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qteditorfactory.cpp" +#include "qteditorfactory.moc" diff --git a/src/qteditorfactory.h b/src/qteditorfactory.h new file mode 100644 index 0000000..5fe7cab --- /dev/null +++ b/src/qteditorfactory.h @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTEDITORFACTORY_H +#define QTEDITORFACTORY_H + +#include "qtpropertymanager.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtSpinBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSpinBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtSpinBoxFactory(QObject *parent = 0); + ~QtSpinBoxFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtSpinBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSpinBoxFactory) + Q_DISABLE_COPY(QtSpinBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtSliderFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSliderFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtSliderFactory(QObject *parent = 0); + ~QtSliderFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtSliderFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSliderFactory) + Q_DISABLE_COPY(QtSliderFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtScrollBarFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtScrollBarFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtScrollBarFactory(QObject *parent = 0); + ~QtScrollBarFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtScrollBarFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtScrollBarFactory) + Q_DISABLE_COPY(QtScrollBarFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCheckBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCheckBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCheckBoxFactory(QObject *parent = 0); + ~QtCheckBoxFactory(); +protected: + void connectPropertyManager(QtBoolPropertyManager *manager); + QWidget *createEditor(QtBoolPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtBoolPropertyManager *manager); +private: + QtCheckBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCheckBoxFactory) + Q_DISABLE_COPY(QtCheckBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(bool)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDoubleSpinBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDoubleSpinBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDoubleSpinBoxFactory(QObject *parent = 0); + ~QtDoubleSpinBoxFactory(); +protected: + void connectPropertyManager(QtDoublePropertyManager *manager); + QWidget *createEditor(QtDoublePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDoublePropertyManager *manager); +private: + QtDoubleSpinBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDoubleSpinBoxFactory) + Q_DISABLE_COPY(QtDoubleSpinBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(double)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtLineEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtLineEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtLineEditFactory(QObject *parent = 0); + ~QtLineEditFactory(); +protected: + void connectPropertyManager(QtStringPropertyManager *manager); + QWidget *createEditor(QtStringPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtStringPropertyManager *manager); +private: + QtLineEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtLineEditFactory) + Q_DISABLE_COPY(QtLineEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegExp &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDateEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDateEditFactory(QObject *parent = 0); + ~QtDateEditFactory(); +protected: + void connectPropertyManager(QtDatePropertyManager *manager); + QWidget *createEditor(QtDatePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDatePropertyManager *manager); +private: + QtDateEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateEditFactory) + Q_DISABLE_COPY(QtDateEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, + const QDate &, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtTimeEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTimeEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtTimeEditFactory(QObject *parent = 0); + ~QtTimeEditFactory(); +protected: + void connectPropertyManager(QtTimePropertyManager *manager); + QWidget *createEditor(QtTimePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtTimePropertyManager *manager); +private: + QtTimeEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTimeEditFactory) + Q_DISABLE_COPY(QtTimeEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDateTimeEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateTimeEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDateTimeEditFactory(QObject *parent = 0); + ~QtDateTimeEditFactory(); +protected: + void connectPropertyManager(QtDateTimePropertyManager *manager); + QWidget *createEditor(QtDateTimePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDateTimePropertyManager *manager); +private: + QtDateTimeEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateTimeEditFactory) + Q_DISABLE_COPY(QtDateTimeEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtKeySequenceEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtKeySequenceEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtKeySequenceEditorFactory(QObject *parent = 0); + ~QtKeySequenceEditorFactory(); +protected: + void connectPropertyManager(QtKeySequencePropertyManager *manager); + QWidget *createEditor(QtKeySequencePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtKeySequencePropertyManager *manager); +private: + QtKeySequenceEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtKeySequenceEditorFactory) + Q_DISABLE_COPY(QtKeySequenceEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCharEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCharEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCharEditorFactory(QObject *parent = 0); + ~QtCharEditorFactory(); +protected: + void connectPropertyManager(QtCharPropertyManager *manager); + QWidget *createEditor(QtCharPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtCharPropertyManager *manager); +private: + QtCharEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCharEditorFactory) + Q_DISABLE_COPY(QtCharEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtEnumEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtEnumEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtEnumEditorFactory(QObject *parent = 0); + ~QtEnumEditorFactory(); +protected: + void connectPropertyManager(QtEnumPropertyManager *manager); + QWidget *createEditor(QtEnumPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtEnumPropertyManager *manager); +private: + QtEnumEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtEnumEditorFactory) + Q_DISABLE_COPY(QtEnumEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, + const QStringList &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, + const QMap &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCursorEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCursorEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCursorEditorFactory(QObject *parent = 0); + ~QtCursorEditorFactory(); +protected: + void connectPropertyManager(QtCursorPropertyManager *manager); + QWidget *createEditor(QtCursorPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtCursorPropertyManager *manager); +private: + QtCursorEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCursorEditorFactory) + Q_DISABLE_COPY(QtCursorEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QCursor &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtColorEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtColorEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtColorEditorFactory(QObject *parent = 0); + ~QtColorEditorFactory(); +protected: + void connectPropertyManager(QtColorPropertyManager *manager); + QWidget *createEditor(QtColorPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtColorPropertyManager *manager); +private: + QtColorEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtColorEditorFactory) + Q_DISABLE_COPY(QtColorEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QColor &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QColor &)) +}; + +class QtFontEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFontEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtFontEditorFactory(QObject *parent = 0); + ~QtFontEditorFactory(); +protected: + void connectPropertyManager(QtFontPropertyManager *manager); + QWidget *createEditor(QtFontPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtFontPropertyManager *manager); +private: + QtFontEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFontEditorFactory) + Q_DISABLE_COPY(QtFontEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QFont &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QFont &)) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qtgroupboxpropertybrowser.cpp b/src/qtgroupboxpropertybrowser.cpp new file mode 100644 index 0000000..caf074d --- /dev/null +++ b/src/qtgroupboxpropertybrowser.cpp @@ -0,0 +1,536 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qtgroupboxpropertybrowser.h" +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtGroupBoxPropertyBrowserPrivate +{ + QtGroupBoxPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser) +public: + + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + + void slotEditorDestroyed(); + void slotUpdate(); + + struct WidgetItem + { + WidgetItem() : widget(0), label(0), widgetLabel(0), + groupBox(0), layout(0), line(0), parent(0) { } + QWidget *widget; // can be null + QLabel *label; + QLabel *widgetLabel; + QGroupBox *groupBox; + QGridLayout *layout; + QFrame *line; + WidgetItem *parent; + QList children; + }; +private: + void updateLater(); + void updateItem(WidgetItem *item); + void insertRow(QGridLayout *layout, int row) const; + void removeRow(QGridLayout *layout, int row) const; + + bool hasHeader(WidgetItem *item) const; + + QMap m_indexToItem; + QMap m_itemToIndex; + QMap m_widgetToItem; + QGridLayout *m_mainLayout; + QList m_children; + QList m_recreateQueue; +}; + +void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent) +{ + m_mainLayout = new QGridLayout(); + parent->setLayout(m_mainLayout); + QLayoutItem *item = new QSpacerItem(0, 0, + QSizePolicy::Fixed, QSizePolicy::Expanding); + m_mainLayout->addItem(item, 0, 0); +} + +void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed() +{ + QWidget *editor = qobject_cast(q_ptr->sender()); + if (!editor) + return; + if (!m_widgetToItem.contains(editor)) + return; + m_widgetToItem[editor]->widget = 0; + m_widgetToItem.remove(editor); +} + +void QtGroupBoxPropertyBrowserPrivate::slotUpdate() +{ + QListIterator itItem(m_recreateQueue); + while (itItem.hasNext()) { + WidgetItem *item = itItem.next(); + + WidgetItem *par = item->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(item); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(item); + if (hasHeader(par)) + oldRow += 2; + } + + if (item->widget) { + item->widget->setParent(w); + } else if (item->widgetLabel) { + item->widgetLabel->setParent(w); + } else { + item->widgetLabel = new QLabel(w); + item->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + item->widgetLabel->setTextFormat(Qt::PlainText); + } + int span = 1; + if (item->widget) + l->addWidget(item->widget, oldRow, 1, 1, 1); + else if (item->widgetLabel) + l->addWidget(item->widgetLabel, oldRow, 1, 1, 1); + else + span = 2; + item->label = new QLabel(w); + item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + l->addWidget(item->label, oldRow, 0, 1, span); + + updateItem(item); + } + m_recreateQueue.clear(); +} + +void QtGroupBoxPropertyBrowserPrivate::updateLater() +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); +} + +void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + WidgetItem *afterItem = m_indexToItem.value(afterIndex); + WidgetItem *parentItem = m_indexToItem.value(index->parent()); + + WidgetItem *newItem = new WidgetItem(); + newItem->parent = parentItem; + + QGridLayout *layout = 0; + QWidget *parentWidget = 0; + int row = -1; + if (!afterItem) { + row = 0; + if (parentItem) + parentItem->children.insert(0, newItem); + else + m_children.insert(0, newItem); + } else { + if (parentItem) { + row = parentItem->children.indexOf(afterItem) + 1; + parentItem->children.insert(row, newItem); + } else { + row = m_children.indexOf(afterItem) + 1; + m_children.insert(row, newItem); + } + } + if (parentItem && hasHeader(parentItem)) + row += 2; + + if (!parentItem) { + layout = m_mainLayout; + parentWidget = q_ptr;; + } else { + if (!parentItem->groupBox) { + m_recreateQueue.removeAll(parentItem); + WidgetItem *par = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(parentItem); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(parentItem); + if (hasHeader(par)) + oldRow += 2; + } + parentItem->groupBox = new QGroupBox(w); + parentItem->layout = new QGridLayout(); + parentItem->groupBox->setLayout(parentItem->layout); + if (parentItem->label) { + l->removeWidget(parentItem->label); + delete parentItem->label; + parentItem->label = 0; + } + if (parentItem->widget) { + l->removeWidget(parentItem->widget); + parentItem->widget->setParent(parentItem->groupBox); + parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2); + parentItem->line = new QFrame(parentItem->groupBox); + } else if (parentItem->widgetLabel) { + l->removeWidget(parentItem->widgetLabel); + delete parentItem->widgetLabel; + parentItem->widgetLabel = 0; + } + if (parentItem->line) { + parentItem->line->setFrameShape(QFrame::HLine); + parentItem->line->setFrameShadow(QFrame::Sunken); + parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2); + } + l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2); + updateItem(parentItem); + } + layout = parentItem->layout; + parentWidget = parentItem->groupBox; + } + + newItem->label = new QLabel(parentWidget); + newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + newItem->widget = createEditor(index->property(), parentWidget); + if (!newItem->widget) { + newItem->widgetLabel = new QLabel(parentWidget); + newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + newItem->widgetLabel->setTextFormat(Qt::PlainText); + } else { + QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); + m_widgetToItem[newItem->widget] = newItem; + } + + insertRow(layout, row); + int span = 1; + if (newItem->widget) + layout->addWidget(newItem->widget, row, 1); + else if (newItem->widgetLabel) + layout->addWidget(newItem->widgetLabel, row, 1); + else + span = 2; + layout->addWidget(newItem->label, row, 0, 1, span); + + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + updateItem(newItem); +} + +void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + + WidgetItem *parentItem = item->parent; + + int row = -1; + + if (parentItem) { + row = parentItem->children.indexOf(item); + parentItem->children.removeAt(row); + if (hasHeader(parentItem)) + row += 2; + } else { + row = m_children.indexOf(item); + m_children.removeAt(row); + } + + if (item->widget) + delete item->widget; + if (item->label) + delete item->label; + if (item->widgetLabel) + delete item->widgetLabel; + if (item->groupBox) + delete item->groupBox; + + if (!parentItem) { + removeRow(m_mainLayout, row); + } else if (parentItem->children.count() != 0) { + removeRow(parentItem->layout, row); + } else { + WidgetItem *par = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(parentItem); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(parentItem); + if (hasHeader(par)) + oldRow += 2; + } + + if (parentItem->widget) { + parentItem->widget->hide(); + parentItem->widget->setParent(0); + } else if (parentItem->widgetLabel) { + parentItem->widgetLabel->hide(); + parentItem->widgetLabel->setParent(0); + } else { + //parentItem->widgetLabel = new QLabel(w); + } + l->removeWidget(parentItem->groupBox); + delete parentItem->groupBox; + parentItem->groupBox = 0; + parentItem->line = 0; + parentItem->layout = 0; + if (!m_recreateQueue.contains(parentItem)) + m_recreateQueue.append(parentItem); + updateLater(); + } + m_recreateQueue.removeAll(item); + + delete item; +} + +void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r >= row) { + itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r > row) { + itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const +{ + if (item->widget) + return true; + return false; +} + +void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + if (item->groupBox) { + QFont font = item->groupBox->font(); + font.setUnderline(property->isModified()); + item->groupBox->setFont(font); + item->groupBox->setTitle(property->propertyName()); + item->groupBox->setToolTip(property->toolTip()); + item->groupBox->setStatusTip(property->statusTip()); + item->groupBox->setWhatsThis(property->whatsThis()); + item->groupBox->setEnabled(property->isEnabled()); + } + if (item->label) { + QFont font = item->label->font(); + font.setUnderline(property->isModified()); + item->label->setFont(font); + item->label->setText(property->propertyName()); + item->label->setToolTip(property->toolTip()); + item->label->setStatusTip(property->statusTip()); + item->label->setWhatsThis(property->whatsThis()); + item->label->setEnabled(property->isEnabled()); + } + if (item->widgetLabel) { + QFont font = item->widgetLabel->font(); + font.setUnderline(false); + item->widgetLabel->setFont(font); + item->widgetLabel->setText(property->valueText()); + item->widgetLabel->setToolTip(property->valueText()); + item->widgetLabel->setEnabled(property->isEnabled()); + } + if (item->widget) { + QFont font = item->widget->font(); + font.setUnderline(false); + item->widget->setFont(font); + item->widget->setEnabled(property->isEnabled()); + item->widget->setToolTip(property->valueText()); + } + //item->setIcon(1, property->valueIcon()); +} + + + +/*! + \class QtGroupBoxPropertyBrowser + + \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox + based property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtGroupBoxPropertyBrowser provides group boxes for all nested + properties, i.e. subproperties are enclosed by a group box with + the parent property's name as its title. For example: + + \image qtgroupboxpropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtGroupBoxPropertyBrowser + class. The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtGroupBoxPropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser() +{ + const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); + for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) + delete it.key(); + delete d_ptr; +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtgroupboxpropertybrowser.cpp" diff --git a/src/qtgroupboxpropertybrowser.h b/src/qtgroupboxpropertybrowser.h new file mode 100644 index 0000000..b02e87d --- /dev/null +++ b/src/qtgroupboxpropertybrowser.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTGROUPBOXPROPERTYBROWSER_H +#define QTGROUPBOXPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtGroupBoxPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtGroupBoxPropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT +public: + + QtGroupBoxPropertyBrowser(QWidget *parent = 0); + ~QtGroupBoxPropertyBrowser(); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtGroupBoxPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtGroupBoxPropertyBrowser) + Q_DISABLE_COPY(QtGroupBoxPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qtpropertybrowser.cpp b/src/qtpropertybrowser.cpp new file mode 100644 index 0000000..2449fcb --- /dev/null +++ b/src/qtpropertybrowser.cpp @@ -0,0 +1,2048 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qtpropertybrowser.h" +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtPropertyPrivate +{ +public: + QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_modified(false), m_manager(manager) {} + QtProperty *q_ptr; + + QSet m_parentItems; + QList m_subItems; + + QString m_toolTip; + QString m_statusTip; + QString m_whatsThis; + QString m_name; + QString m_id; + bool m_enabled; + bool m_modified; + + QtAbstractPropertyManager * const m_manager; +}; + +class QtAbstractPropertyManagerPrivate +{ + QtAbstractPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtAbstractPropertyManager) +public: + void propertyDestroyed(QtProperty *property); + void propertyChanged(QtProperty *property) const; + void propertyRemoved(QtProperty *property, + QtProperty *parentProperty) const; + void propertyInserted(QtProperty *property, QtProperty *parentProperty, + QtProperty *afterProperty) const; + + QSet m_properties; +}; + +/*! + \class QtProperty + + \brief The QtProperty class encapsulates an instance of a property. + + Properties are created by objects of QtAbstractPropertyManager + subclasses; a manager can create properties of a given type, and + is used in conjunction with the QtAbstractPropertyBrowser class. A + property is always owned by the manager that created it, which can + be retrieved using the propertyManager() function. + + QtProperty contains the most common property attributes, and + provides functions for retrieving as well as setting their values: + + \table + \header \o Getter \o Setter + \row + \o propertyName() \o setPropertyName() + \row + \o statusTip() \o setStatusTip() + \row + \o toolTip() \o setToolTip() + \row + \o whatsThis() \o setWhatsThis() + \row + \o isEnabled() \o setEnabled() + \row + \o isModified() \o setModified() + \row + \o valueText() \o Nop + \row + \o valueIcon() \o Nop + \endtable + + It is also possible to nest properties: QtProperty provides the + addSubProperty(), insertSubProperty() and removeSubProperty() functions to + manipulate the set of subproperties. Use the subProperties() + function to retrieve a property's current set of subproperties. + Note that nested properties are not owned by the parent property, + i.e. each subproperty is owned by the manager that created it. + + \sa QtAbstractPropertyManager, QtBrowserItem +*/ + +/*! + Creates a property with the given \a manager. + + This constructor is only useful when creating a custom QtProperty + subclass (e.g. QtVariantProperty). To create a regular QtProperty + object, use the QtAbstractPropertyManager::addProperty() + function instead. + + \sa QtAbstractPropertyManager::addProperty() +*/ +QtProperty::QtProperty(QtAbstractPropertyManager *manager) +{ + d_ptr = new QtPropertyPrivate(manager); + d_ptr->q_ptr = this; +} + +/*! + Destroys this property. + + Note that subproperties are detached but not destroyed, i.e. they + can still be used in another context. + + \sa QtAbstractPropertyManager::clear() + +*/ +QtProperty::~QtProperty() +{ + QSetIterator itParent(d_ptr->m_parentItems); + while (itParent.hasNext()) { + QtProperty *property = itParent.next(); + property->d_ptr->m_manager->d_ptr->propertyRemoved(this, property); + } + + d_ptr->m_manager->d_ptr->propertyDestroyed(this); + + QListIterator itChild(d_ptr->m_subItems); + while (itChild.hasNext()) { + QtProperty *property = itChild.next(); + property->d_ptr->m_parentItems.remove(this); + } + + itParent.toFront(); + while (itParent.hasNext()) { + QtProperty *property = itParent.next(); + property->d_ptr->m_subItems.removeAll(this); + } + delete d_ptr; +} + +/*! + Returns the set of subproperties. + + Note that subproperties are not owned by \e this property, but by + the manager that created them. + + \sa insertSubProperty(), removeSubProperty() +*/ +QList QtProperty::subProperties() const +{ + return d_ptr->m_subItems; +} + +/*! + Returns a pointer to the manager that owns this property. +*/ +QtAbstractPropertyManager *QtProperty::propertyManager() const +{ + return d_ptr->m_manager; +} + +/*! + Returns the property's tool tip. + + \sa setToolTip() +*/ +QString QtProperty::toolTip() const +{ + return d_ptr->m_toolTip; +} + +/*! + Returns the property's status tip. + + \sa setStatusTip() +*/ +QString QtProperty::statusTip() const +{ + return d_ptr->m_statusTip; +} + +/*! + Returns the property's "What's This" help text. + + \sa setWhatsThis() +*/ +QString QtProperty::whatsThis() const +{ + return d_ptr->m_whatsThis; +} + +/*! + Returns the property's name. + + \sa setPropertyName() +*/ +QString QtProperty::propertyName() const +{ + return d_ptr->m_name; +} + +/*! + Returns the property's id. + + \sa setPropertyId() +*/ +QString QtProperty::propertyId() const +{ + return d_ptr->m_id; +} + +/*! + Returns whether the property is enabled. + + \sa setEnabled() +*/ +bool QtProperty::isEnabled() const +{ + return d_ptr->m_enabled; +} + +/*! + Returns whether the property is modified. + + \sa setModified() +*/ +bool QtProperty::isModified() const +{ + return d_ptr->m_modified; +} + +/*! + Returns whether the property has a value. + + \sa QtAbstractPropertyManager::hasValue() +*/ +bool QtProperty::hasValue() const +{ + return d_ptr->m_manager->hasValue(this); +} + +/*! + Returns an icon representing the current state of this property. + + If the given property type can not generate such an icon, this + function returns an invalid icon. + + \sa QtAbstractPropertyManager::valueIcon() +*/ +QIcon QtProperty::valueIcon() const +{ + return d_ptr->m_manager->valueIcon(this); +} + +/*! + Returns a string representing the current state of this property. + + If the given property type can not generate such a string, this + function returns an empty string. + + \sa QtAbstractPropertyManager::valueText() +*/ +QString QtProperty::valueText() const +{ + return d_ptr->m_manager->valueText(this); +} + +/*! + Returns True if this property is equal to \a otherProperty + + The list of parent or sub properties are not considered in the comparison. +*/ +bool QtProperty::compare(QtProperty* otherProperty)const +{ + return (this->propertyId() == otherProperty->propertyId() + && this->propertyName() == otherProperty->propertyName() + && this->toolTip() == otherProperty->toolTip() + && this->statusTip() == otherProperty->statusTip() + && this->whatsThis() == otherProperty->whatsThis() + && this->isEnabled() == otherProperty->isEnabled() + && this->isModified() == otherProperty->isModified()); +} + +/*! + Sets the property's tool tip to the given \a text. + + \sa toolTip() +*/ +void QtProperty::setToolTip(const QString &text) +{ + if (d_ptr->m_toolTip == text) + return; + + d_ptr->m_toolTip = text; + propertyChanged(); +} + +/*! + Sets the property's status tip to the given \a text. + + \sa statusTip() +*/ +void QtProperty::setStatusTip(const QString &text) +{ + if (d_ptr->m_statusTip == text) + return; + + d_ptr->m_statusTip = text; + propertyChanged(); +} + +/*! + Sets the property's "What's This" help text to the given \a text. + + \sa whatsThis() +*/ +void QtProperty::setWhatsThis(const QString &text) +{ + if (d_ptr->m_whatsThis == text) + return; + + d_ptr->m_whatsThis = text; + propertyChanged(); +} + +/*! + \fn void QtProperty::setPropertyName(const QString &name) + + Sets the property's name to the given \a name. + + \sa propertyName() +*/ +void QtProperty::setPropertyName(const QString &text) +{ + if (d_ptr->m_name == text) + return; + + d_ptr->m_name = text; + propertyChanged(); +} + +/*! + \fn void QtProperty::setPropertyId(const QString &id) + + Sets the property's id to the given \a id. + + \sa propertyId() +*/ +void QtProperty::setPropertyId(const QString &text) +{ + if (d_ptr->m_id == text) + return; + + d_ptr->m_id = text; +} + +/*! + Enables or disables the property according to the passed \a enable value. + + \sa isEnabled() +*/ +void QtProperty::setEnabled(bool enable) +{ + if (d_ptr->m_enabled == enable) + return; + + d_ptr->m_enabled = enable; + propertyChanged(); +} + +/*! + Sets the property's modified state according to the passed \a modified value. + + \sa isModified() +*/ +void QtProperty::setModified(bool modified) +{ + if (d_ptr->m_modified == modified) + return; + + d_ptr->m_modified = modified; + propertyChanged(); +} + +/*! + Returns whether the property is sub property. +*/ +bool QtProperty::isSubProperty()const +{ + return d_ptr->m_parentItems.count(); +} + +/*! + Appends the given \a property to this property's subproperties. + + If the given \a property already is added, this function does + nothing. + + \sa insertSubProperty(), removeSubProperty() +*/ +void QtProperty::addSubProperty(QtProperty *property) +{ + QtProperty *after = 0; + if (d_ptr->m_subItems.count() > 0) + after = d_ptr->m_subItems.last(); + insertSubProperty(property, after); +} + +/*! + \fn void QtProperty::insertSubProperty(QtProperty *property, QtProperty *precedingProperty) + + Inserts the given \a property after the specified \a + precedingProperty into this property's list of subproperties. If + \a precedingProperty is 0, the specified \a property is inserted + at the beginning of the list. + + If the given \a property already is inserted, this function does + nothing. + + \sa addSubProperty(), removeSubProperty() +*/ +void QtProperty::insertSubProperty(QtProperty *property, + QtProperty *afterProperty) +{ + if (!property) + return; + + if (property == this) + return; + + // traverse all children of item. if this item is a child of item then cannot add. + QList pendingList = property->subProperties(); + QMap visited; + while (!pendingList.isEmpty()) { + QtProperty *i = pendingList.first(); + if (i == this) + return; + pendingList.removeFirst(); + if (visited.contains(i)) + continue; + visited[i] = true; + pendingList += i->subProperties(); + } + + pendingList = subProperties(); + int pos = 0; + int newPos = 0; + QtProperty *properAfterProperty = 0; + while (pos < pendingList.count()) { + QtProperty *i = pendingList.at(pos); + if (i == property) + return; // if item is already inserted in this item then cannot add. + if (i == afterProperty) { + newPos = pos + 1; + properAfterProperty = afterProperty; + } + pos++; + } + + d_ptr->m_subItems.insert(newPos, property); + property->d_ptr->m_parentItems.insert(this); + + d_ptr->m_manager->d_ptr->propertyInserted(property, this, properAfterProperty); +} + +/*! + Removes the given \a property from the list of subproperties + without deleting it. + + \sa addSubProperty(), insertSubProperty() +*/ +void QtProperty::removeSubProperty(QtProperty *property) +{ + if (!property) + return; + + d_ptr->m_manager->d_ptr->propertyRemoved(property, this); + + QList pendingList = subProperties(); + int pos = 0; + while (pos < pendingList.count()) { + if (pendingList.at(pos) == property) { + d_ptr->m_subItems.removeAt(pos); + property->d_ptr->m_parentItems.remove(this); + + return; + } + pos++; + } +} + +/*! + \internal +*/ +void QtProperty::propertyChanged() +{ + d_ptr->m_manager->d_ptr->propertyChanged(this); +} + +//////////////////////////////// + +void QtAbstractPropertyManagerPrivate::propertyDestroyed(QtProperty *property) +{ + if (m_properties.contains(property)) { + emit q_ptr->propertyDestroyed(property); + q_ptr->uninitializeProperty(property); + m_properties.remove(property); + } +} + +void QtAbstractPropertyManagerPrivate::propertyChanged(QtProperty *property) const +{ + emit q_ptr->propertyChanged(property); +} + +void QtAbstractPropertyManagerPrivate::propertyRemoved(QtProperty *property, + QtProperty *parentProperty) const +{ + emit q_ptr->propertyRemoved(property, parentProperty); +} + +void QtAbstractPropertyManagerPrivate::propertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty) const +{ + emit q_ptr->propertyInserted(property, parentProperty, afterProperty); +} + +/*! + \class QtAbstractPropertyManager + + \brief The QtAbstractPropertyManager provides an interface for + property managers. + + A manager can create and manage properties of a given type, and is + used in conjunction with the QtAbstractPropertyBrowser class. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser + will use these associations to determine which factories it should + use to create the preferred editing widgets. + + The QtAbstractPropertyManager class provides common functionality + like creating a property using the addProperty() function, and + retrieving the properties created by the manager using the + properties() function. The class also provides signals that are + emitted when the manager's properties change: propertyInserted(), + propertyRemoved(), propertyChanged() and propertyDestroyed(). + + QtAbstractPropertyManager subclasses are supposed to provide their + own type specific API. Note that several ready-made + implementations are available: + + \list + \o QtBoolPropertyManager + \o QtColorPropertyManager + \o QtDatePropertyManager + \o QtDateTimePropertyManager + \o QtDoublePropertyManager + \o QtEnumPropertyManager + \o QtFlagPropertyManager + \o QtFontPropertyManager + \o QtGroupPropertyManager + \o QtIntPropertyManager + \o QtPointPropertyManager + \o QtRectPropertyManager + \o QtSizePropertyManager + \o QtSizePolicyPropertyManager + \o QtStringPropertyManager + \o QtTimePropertyManager + \o QtVariantPropertyManager + \endlist + + \sa QtAbstractEditorFactoryBase, QtAbstractPropertyBrowser, QtProperty +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyInserted(QtProperty *newProperty, + QtProperty *parentProperty, QtProperty *precedingProperty) + + This signal is emitted when a new subproperty is inserted into an + existing property, passing pointers to the \a newProperty, \a + parentProperty and \a precedingProperty as parameters. + + If \a precedingProperty is 0, the \a newProperty was inserted at + the beginning of the \a parentProperty's subproperties list. + + Note that signal is emitted only if the \a parentProperty is created + by this manager. + + \sa QtAbstractPropertyBrowser::itemInserted() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyChanged(QtProperty *property) + + This signal is emitted whenever a property's data changes, passing + a pointer to the \a property as parameter. + + Note that signal is only emitted for properties that are created by + this manager. + + \sa QtAbstractPropertyBrowser::itemChanged() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyRemoved(QtProperty *property, QtProperty *parent) + + This signal is emitted when a subproperty is removed, passing + pointers to the removed \a property and the \a parent property as + parameters. + + Note that signal is emitted only when the \a parent property is + created by this manager. + + \sa QtAbstractPropertyBrowser::itemRemoved() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyDestroyed(QtProperty *property) + + This signal is emitted when the specified \a property is about to + be destroyed. + + Note that signal is only emitted for properties that are created + by this manager. + + \sa clear(), uninitializeProperty() +*/ + +/*! + \fn void QtAbstractPropertyBrowser::currentItemChanged(QtBrowserItem *current) + + This signal is emitted when the current item changes. The current item is specified by \a current. + + \sa QtAbstractPropertyBrowser::setCurrentItem() +*/ + +/*! + Creates an abstract property manager with the given \a parent. +*/ +QtAbstractPropertyManager::QtAbstractPropertyManager(QObject *parent) + : QObject(parent) +{ + d_ptr = new QtAbstractPropertyManagerPrivate; + d_ptr->q_ptr = this; + +} + +/*! + Destroys the manager. All properties created by the manager are + destroyed. +*/ +QtAbstractPropertyManager::~QtAbstractPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Destroys all the properties that this manager has created. + + \sa propertyDestroyed(), uninitializeProperty() +*/ +void QtAbstractPropertyManager::clear() const +{ + while (!properties().isEmpty()) { + QSetIterator itProperty(properties()); + QtProperty *prop = itProperty.next(); + delete prop; + } +} + +/*! + Returns the set of properties created by this manager. + + \sa addProperty() +*/ +QSet QtAbstractPropertyManager::properties() const +{ + return d_ptr->m_properties; +} + +/*! + Returns whether the given \a property has a value. + + The default implementation of this function returns true. + + \sa QtProperty::hasValue() +*/ +bool QtAbstractPropertyManager::hasValue(const QtProperty *property) const +{ + Q_UNUSED(property) + return true; +} + +/*! + Returns an icon representing the current state of the given \a + property. + + The default implementation of this function returns an invalid + icon. + + \sa QtProperty::valueIcon() +*/ +QIcon QtAbstractPropertyManager::valueIcon(const QtProperty *property) const +{ + Q_UNUSED(property) + return QIcon(); +} + +/*! + Returns a string representing the current state of the given \a + property. + + The default implementation of this function returns an empty + string. + + \sa QtProperty::valueText() +*/ +QString QtAbstractPropertyManager::valueText(const QtProperty *property) const +{ + Q_UNUSED(property) + return QString(); +} + +/*! + Creates a property with the given \a name which then is owned by this manager. + + Internally, this function calls the createProperty() and + initializeProperty() functions. + + \sa initializeProperty(), properties() +*/ +QtProperty *QtAbstractPropertyManager::addProperty(const QString &name) +{ + QtProperty *property = createProperty(); + if (property) { + property->setPropertyName(name); + d_ptr->m_properties.insert(property); + initializeProperty(property); + } + return property; +} + +/*! + Return the QtProperty object matching \a id or Null if any. + + \sa addProperty(), setPropertyId(const QString&), properties() +*/ +QtProperty * QtAbstractPropertyManager::qtProperty(const QString &id)const +{ + foreach(QtProperty* prop, d_ptr->m_properties) + { + if (prop->propertyId() == id) + { + return prop; + } + } + return 0; +} + +/*! + Creates a property. + + The base implementation produce QtProperty instances; Reimplement + this function to make this manager produce objects of a QtProperty + subclass. + + \sa addProperty(), initializeProperty() +*/ +QtProperty *QtAbstractPropertyManager::createProperty() +{ + return new QtProperty(this); +} + +/*! + \fn void QtAbstractPropertyManager::initializeProperty(QtProperty *property) = 0 + + This function is called whenever a new valid property pointer has + been created, passing the pointer as parameter. + + The purpose is to let the manager know that the \a property has + been created so that it can provide additional attributes for the + new property, e.g. QtIntPropertyManager adds \l + {QtIntPropertyManager::value()}{value}, \l + {QtIntPropertyManager::minimum()}{minimum} and \l + {QtIntPropertyManager::maximum()}{maximum} attributes. Since each manager + subclass adds type specific attributes, this function is pure + virtual and must be reimplemented when deriving from the + QtAbstractPropertyManager class. + + \sa addProperty(), createProperty() +*/ + +/*! + This function is called just before the specified \a property is destroyed. + + The purpose is to let the property manager know that the \a + property is being destroyed so that it can remove the property's + additional attributes. + + \sa clear(), propertyDestroyed() +*/ +void QtAbstractPropertyManager::uninitializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +//////////////////////////////////// + +/*! + \class QtAbstractEditorFactoryBase + + \brief The QtAbstractEditorFactoryBase provides an interface for + editor factories. + + An editor factory is a class that is able to create an editing + widget of a specified type (e.g. line edits or comboboxes) for a + given QtProperty object, and it is used in conjunction with the + QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser + will use these associations to determine which factories it should + use to create the preferred editing widgets. + + Typically, an editor factory is created by subclassing the + QtAbstractEditorFactory template class which inherits + QtAbstractEditorFactoryBase. But note that several ready-made + implementations are available: + + \list + \o QtCheckBoxFactory + \o QtDateEditFactory + \o QtDateTimeEditFactory + \o QtDoubleSpinBoxFactory + \o QtEnumEditorFactory + \o QtLineEditFactory + \o QtScrollBarFactory + \o QtSliderFactory + \o QtSpinBoxFactory + \o QtTimeEditFactory + \o QtVariantEditorFactory + \endlist + + \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser +*/ + +/*! + \fn virtual QWidget *QtAbstractEditorFactoryBase::createEditor(QtProperty *property, + QWidget *parent) = 0 + + Creates an editing widget (with the given \a parent) for the given + \a property. + + This function is reimplemented in QtAbstractEditorFactory template class + which also provides a pure virtual convenience overload of this + function enabling access to the property's manager. + + \sa QtAbstractEditorFactory::createEditor() +*/ + +/*! + \fn QtAbstractEditorFactoryBase::QtAbstractEditorFactoryBase(QObject *parent = 0) + + Creates an abstract editor factory with the given \a parent. +*/ + +/*! + \fn virtual void QtAbstractEditorFactoryBase::breakConnection(QtAbstractPropertyManager *manager) = 0 + + \internal + + Detaches property manager from factory. + This method is reimplemented in QtAbstractEditorFactory template subclass. + You don't need to reimplement it in your subclasses. Instead implement more convenient + QtAbstractEditorFactory::disconnectPropertyManager() which gives you access to particular manager subclass. +*/ + +/*! + \fn virtual void QtAbstractEditorFactoryBase::managerDestroyed(QObject *manager) = 0 + + \internal + + This method is called when property manager is being destroyed. + Basically it notifies factory not to produce editors for properties owned by \a manager. + You don't need to reimplement it in your subclass. This method is implemented in + QtAbstractEditorFactory template subclass. +*/ + +/*! + \class QtAbstractEditorFactory + + \brief The QtAbstractEditorFactory is the base template class for editor + factories. + + An editor factory is a class that is able to create an editing + widget of a specified type (e.g. line edits or comboboxes) for a + given QtProperty object, and it is used in conjunction with the + QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. + + Note that the QtAbstractEditorFactory functions are using the + PropertyManager template argument class which can be any + QtAbstractPropertyManager subclass. For example: + + \code + QtSpinBoxFactory *factory; + QSet managers = factory->propertyManagers(); + \endcode + + Note that QtSpinBoxFactory by definition creates editing widgets + \e only for properties created by QtIntPropertyManager. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser will + use these associations to determine which factories it should use + to create the preferred editing widgets. + + A QtAbstractEditorFactory object is capable of producing editors for + several property managers at the same time. To create an + association between this factory and a given manager, use the + addPropertyManager() function. Use the removePropertyManager() function to make + this factory stop producing editors for a given property + manager. Use the propertyManagers() function to retrieve the set of + managers currently associated with this factory. + + Several ready-made implementations of the QtAbstractEditorFactory class + are available: + + \list + \o QtCheckBoxFactory + \o QtDateEditFactory + \o QtDateTimeEditFactory + \o QtDoubleSpinBoxFactory + \o QtEnumEditorFactory + \o QtLineEditFactory + \o QtScrollBarFactory + \o QtSliderFactory + \o QtSpinBoxFactory + \o QtTimeEditFactory + \o QtVariantEditorFactory + \endlist + + When deriving from the QtAbstractEditorFactory class, several pure virtual + functions must be implemented: the connectPropertyManager() function is + used by the factory to connect to the given manager's signals, the + createEditor() function is supposed to create an editor for the + given property controlled by the given manager, and finally the + disconnectPropertyManager() function is used by the factory to disconnect + from the specified manager's signals. + + \sa QtAbstractEditorFactoryBase, QtAbstractPropertyManager +*/ + +/*! + \fn QtAbstractEditorFactory::QtAbstractEditorFactory(QObject *parent = 0) + + Creates an editor factory with the given \a parent. + + \sa addPropertyManager() +*/ + +/*! + \fn QWidget *QtAbstractEditorFactory::createEditor(QtProperty *property, QWidget *parent) + + Creates an editing widget (with the given \a parent) for the given + \a property. +*/ + +/*! + \fn void QtAbstractEditorFactory::addPropertyManager(PropertyManager *manager) + + Adds the given \a manager to this factory's set of managers, + making this factory produce editing widgets for properties created + by the given manager. + + The PropertyManager type is a template argument class, and represents the chosen + QtAbstractPropertyManager subclass. + + \sa propertyManagers(), removePropertyManager() +*/ + +/*! + \fn void QtAbstractEditorFactory::removePropertyManager(PropertyManager *manager) + + Removes the given \a manager from this factory's set of + managers. The PropertyManager type is a template argument class, and may be + any QtAbstractPropertyManager subclass. + + \sa propertyManagers(), addPropertyManager() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::connectPropertyManager(PropertyManager *manager) = 0 + + Connects this factory to the given \a manager's signals. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function is used internally by the addPropertyManager() function, and + makes it possible to update an editing widget when the associated + property's data changes. This is typically done in custom slots + responding to the signals emitted by the property's manager, + e.g. QtIntPropertyManager::valueChanged() and + QtIntPropertyManager::rangeChanged(). + + \sa propertyManagers(), disconnectPropertyManager() +*/ + +/*! + \fn virtual QWidget *QtAbstractEditorFactory::createEditor(PropertyManager *manager, QtProperty *property, + QWidget *parent) = 0 + + Creates an editing widget with the given \a parent for the + specified \a property created by the given \a manager. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function must be implemented in derived classes: It is + recommended to store a pointer to the widget and map it to the + given \a property, since the widget must be updated whenever the + associated property's data changes. This is typically done in + custom slots responding to the signals emitted by the property's + manager, e.g. QtIntPropertyManager::valueChanged() and + QtIntPropertyManager::rangeChanged(). + + \sa connectPropertyManager() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::disconnectPropertyManager(PropertyManager *manager) = 0 + + Disconnects this factory from the given \a manager's signals. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function is used internally by the removePropertyManager() function. + + \sa propertyManagers(), connectPropertyManager() +*/ + +/*! + \fn QSet QtAbstractEditorFactory::propertyManagers() const + + Returns the factory's set of associated managers. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + \sa addPropertyManager(), removePropertyManager() +*/ + +/*! + \fn PropertyManager *QtAbstractEditorFactory::propertyManager(QtProperty *property) const + + Returns the property manager for the given \a property, or 0 if + the given \a property doesn't belong to any of this factory's + registered managers. + + The PropertyManager type is a template argument class, and represents the chosen + QtAbstractPropertyManager subclass. + + \sa propertyManagers() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::managerDestroyed(QObject *manager) + + \internal + \reimp +*/ + +//////////////////////////////////// +class QtBrowserItemPrivate +{ +public: + QtBrowserItemPrivate(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) + : m_browser(browser), m_property(property), m_parent(parent), q_ptr(0) {} + + void addChild(QtBrowserItem *index, QtBrowserItem *after); + void removeChild(QtBrowserItem *index); + + QtAbstractPropertyBrowser * const m_browser; + QtProperty *m_property; + QtBrowserItem *m_parent; + + QtBrowserItem *q_ptr; + + QList m_children; + +}; + +void QtBrowserItemPrivate::addChild(QtBrowserItem *index, QtBrowserItem *after) +{ + if (m_children.contains(index)) + return; + int idx = m_children.indexOf(after) + 1; // we insert after returned idx, if it was -1 then we set idx to 0; + m_children.insert(idx, index); +} + +void QtBrowserItemPrivate::removeChild(QtBrowserItem *index) +{ + m_children.removeAll(index); +} + + +/*! + \class QtBrowserItem + + \brief The QtBrowserItem class represents a property in + a property browser instance. + + Browser items are created whenever a QtProperty is inserted to the + property browser. A QtBrowserItem uniquely identifies a + browser's item. Thus, if the same QtProperty is inserted multiple + times, each occurrence gets its own unique QtBrowserItem. The + items are owned by QtAbstractPropertyBrowser and automatically + deleted when they are removed from the browser. + + You can traverse a browser's properties by calling parent() and + children(). The property and the browser associated with an item + are available as property() and browser(). + + \sa QtAbstractPropertyBrowser, QtProperty +*/ + +/*! + Returns the property which is accosiated with this item. Note that + several items can be associated with the same property instance in + the same property browser. + + \sa QtAbstractPropertyBrowser::items() +*/ + +QtProperty *QtBrowserItem::property() const +{ + return d_ptr->m_property; +} + +/*! + Returns the parent item of \e this item. Returns 0 if \e this item + is associated with top-level property in item's property browser. + + \sa children() +*/ + +QtBrowserItem *QtBrowserItem::parent() const +{ + return d_ptr->m_parent; +} + +/*! + Returns the children items of \e this item. The properties + reproduced from children items are always the same as + reproduced from associated property' children, for example: + + \code + QtBrowserItem *item; + QList childrenItems = item->children(); + + QList childrenProperties = item->property()->subProperties(); + \endcode + + The \e childrenItems list represents the same list as \e childrenProperties. +*/ + +QList QtBrowserItem::children() const +{ + return d_ptr->m_children; +} + +/*! + Returns the property browser which owns \e this item. +*/ + +QtAbstractPropertyBrowser *QtBrowserItem::browser() const +{ + return d_ptr->m_browser; +} + +QtBrowserItem::QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) +{ + d_ptr = new QtBrowserItemPrivate(browser, property, parent); + d_ptr->q_ptr = this; +} + +QtBrowserItem::~QtBrowserItem() +{ + delete d_ptr; +} + + +//////////////////////////////////// + +typedef QMap > Map1; +typedef QMap > > Map2; +Q_GLOBAL_STATIC(Map1, m_viewToManagerToFactory) +Q_GLOBAL_STATIC(Map2, m_managerToFactoryToViews) + +class QtAbstractPropertyBrowserPrivate +{ + QtAbstractPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtAbstractPropertyBrowser) +public: + QtAbstractPropertyBrowserPrivate(); + + void insertSubTree(QtProperty *property, + QtProperty *parentProperty); + void removeSubTree(QtProperty *property, + QtProperty *parentProperty); + void createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty); + void removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty); + QtBrowserItem *createBrowserIndex(QtProperty *property, QtBrowserItem *parentIndex, QtBrowserItem *afterIndex); + void removeBrowserIndex(QtBrowserItem *index); + void clearIndex(QtBrowserItem *index); + + void slotPropertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty); + void slotPropertyRemoved(QtProperty *property, QtProperty *parentProperty); + void slotPropertyDestroyed(QtProperty *property); + void slotPropertyDataChanged(QtProperty *property); + + QList m_subItems; + QMap > m_managerToProperties; + QMap > m_propertyToParents; + + QMap m_topLevelPropertyToIndex; + QList m_topLevelIndexes; + QMap > m_propertyToIndexes; + + QtBrowserItem *m_currentItem; +}; + +QtAbstractPropertyBrowserPrivate::QtAbstractPropertyBrowserPrivate() : + m_currentItem(0) +{ +} + +void QtAbstractPropertyBrowserPrivate::insertSubTree(QtProperty *property, + QtProperty *parentProperty) +{ + if (m_propertyToParents.contains(property)) { + // property was already inserted, so its manager is connected + // and all its children are inserted and theirs managers are connected + // we just register new parent (parent has to be new). + m_propertyToParents[property].append(parentProperty); + // don't need to update m_managerToProperties map since + // m_managerToProperties[manager] already contains property. + return; + } + QtAbstractPropertyManager *manager = property->propertyManager(); + if (m_managerToProperties[manager].isEmpty()) { + // connect manager's signals + q_ptr->connect(manager, SIGNAL(propertyInserted(QtProperty *, + QtProperty *, QtProperty *)), + q_ptr, SLOT(slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyRemoved(QtProperty *, + QtProperty *)), + q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyDestroyed(QtProperty *)), + q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyChanged(QtProperty *)), + q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); + } + m_managerToProperties[manager].append(property); + m_propertyToParents[property].append(parentProperty); + + QList subList = property->subProperties(); + QListIterator itSub(subList); + while (itSub.hasNext()) { + QtProperty *subProperty = itSub.next(); + insertSubTree(subProperty, property); + } +} + +void QtAbstractPropertyBrowserPrivate::removeSubTree(QtProperty *property, + QtProperty *parentProperty) +{ + if (!m_propertyToParents.contains(property)) { + // ASSERT + return; + } + + m_propertyToParents[property].removeAll(parentProperty); + if (!m_propertyToParents[property].isEmpty()) + return; + + m_propertyToParents.remove(property); + QtAbstractPropertyManager *manager = property->propertyManager(); + m_managerToProperties[manager].removeAll(property); + if (m_managerToProperties[manager].isEmpty()) { + // disconnect manager's signals + q_ptr->disconnect(manager, SIGNAL(propertyInserted(QtProperty *, + QtProperty *, QtProperty *)), + q_ptr, SLOT(slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyRemoved(QtProperty *, + QtProperty *)), + q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyDestroyed(QtProperty *)), + q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyChanged(QtProperty *)), + q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); + + m_managerToProperties.remove(manager); + } + + QList subList = property->subProperties(); + QListIterator itSub(subList); + while (itSub.hasNext()) { + QtProperty *subProperty = itSub.next(); + removeSubTree(subProperty, property); + } +} + +void QtAbstractPropertyBrowserPrivate::createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty) +{ + QMap parentToAfter; + if (afterProperty) { + QMap >::ConstIterator it = + m_propertyToIndexes.find(afterProperty); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + QtBrowserItem *parentIdx = idx->parent(); + if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) + parentToAfter[idx->parent()] = idx; + } + } else if (parentProperty) { + QMap >::ConstIterator it = + m_propertyToIndexes.find(parentProperty); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + parentToAfter[idx] = 0; + } + } else { + parentToAfter[0] = 0; + } + + const QMap::ConstIterator pcend = parentToAfter.constEnd(); + for (QMap::ConstIterator it = parentToAfter.constBegin(); it != pcend; ++it) + createBrowserIndex(property, it.key(), it.value()); +} + +QtBrowserItem *QtAbstractPropertyBrowserPrivate::createBrowserIndex(QtProperty *property, + QtBrowserItem *parentIndex, QtBrowserItem *afterIndex) +{ + QtBrowserItem *newIndex = new QtBrowserItem(q_ptr, property, parentIndex); + if (parentIndex) { + parentIndex->d_ptr->addChild(newIndex, afterIndex); + } else { + m_topLevelPropertyToIndex[property] = newIndex; + m_topLevelIndexes.insert(m_topLevelIndexes.indexOf(afterIndex) + 1, newIndex); + } + m_propertyToIndexes[property].append(newIndex); + + q_ptr->itemInserted(newIndex, afterIndex); + + QList subItems = property->subProperties(); + QListIterator itChild(subItems); + QtBrowserItem *afterChild = 0; + while (itChild.hasNext()) { + QtProperty *child = itChild.next(); + afterChild = createBrowserIndex(child, newIndex, afterChild); + } + return newIndex; +} + +void QtAbstractPropertyBrowserPrivate::removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty) +{ + QList toRemove; + QMap >::ConstIterator it = + m_propertyToIndexes.find(property); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + QtBrowserItem *parentIdx = idx->parent(); + if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) + toRemove.append(idx); + } + + QListIterator itRemove(toRemove); + while (itRemove.hasNext()) { + QtBrowserItem *index = itRemove.next(); + removeBrowserIndex(index); + } +} + +void QtAbstractPropertyBrowserPrivate::removeBrowserIndex(QtBrowserItem *index) +{ + QList children = index->children(); + for (int i = children.count(); i > 0; i--) { + removeBrowserIndex(children.at(i - 1)); + } + + q_ptr->itemRemoved(index); + + if (index->parent()) { + index->parent()->d_ptr->removeChild(index); + } else { + m_topLevelPropertyToIndex.remove(index->property()); + m_topLevelIndexes.removeAll(index); + } + + QtProperty *property = index->property(); + + m_propertyToIndexes[property].removeAll(index); + if (m_propertyToIndexes[property].isEmpty()) + m_propertyToIndexes.remove(property); + + delete index; +} + +void QtAbstractPropertyBrowserPrivate::clearIndex(QtBrowserItem *index) +{ + QList children = index->children(); + QListIterator itChild(children); + while (itChild.hasNext()) { + clearIndex(itChild.next()); + } + delete index; +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty) +{ + if (!m_propertyToParents.contains(parentProperty)) + return; + createBrowserIndexes(property, parentProperty, afterProperty); + insertSubTree(property, parentProperty); + //q_ptr->propertyInserted(property, parentProperty, afterProperty); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyRemoved(QtProperty *property, + QtProperty *parentProperty) +{ + if (!m_propertyToParents.contains(parentProperty)) + return; + removeSubTree(property, parentProperty); // this line should be probably moved down after propertyRemoved call + //q_ptr->propertyRemoved(property, parentProperty); + removeBrowserIndexes(property, parentProperty); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (!m_subItems.contains(property)) + return; + q_ptr->removeProperty(property); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyDataChanged(QtProperty *property) +{ + if (!m_propertyToParents.contains(property)) + return; + + QMap >::ConstIterator it = + m_propertyToIndexes.find(property); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + q_ptr->itemChanged(idx); + } + //q_ptr->propertyChanged(property); +} + +/*! + \class QtAbstractPropertyBrowser + + \brief QtAbstractPropertyBrowser provides a base class for + implementing property browsers. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + \image qtpropertybrowser.png + + The top level properties can be retrieved using the + properties() function. To traverse each property's + subproperties, use the QtProperty::subProperties() function. In + addition, the set of top level properties can be manipulated using + the addProperty(), insertProperty() and removeProperty() + functions. Note that the QtProperty class provides a corresponding + set of functions making it possible to manipulate the set of + subproperties as well. + + To remove all the properties from the property browser widget, use + the clear() function. This function will clear the editor, but it + will not delete the properties since they can still be used in + other editors. + + The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. A manager + can handle (i.e. create and manage) properties of a given type. In + the property browser the managers are associated with + implementations of the QtAbstractEditorFactory: A factory is a + class able to create an editing widget of a specified type. + + When using a property browser widget, managers must be created for + each of the required property types before the properties + themselves can be created. To ensure that the properties' values + will be displayed using suitable editing widgets, the managers + must be associated with objects of the preferred factory + implementations using the setFactoryForManager() function. The + property browser will use these associations to determine which + factory it should use to create the preferred editing widget. + + Note that a factory can be associated with many managers, but a + manager can only be associated with one single factory within the + context of a single property browser. The associations between + managers and factories can at any time be removed using the + unsetFactoryForManager() function. + + Whenever the property data changes or a property is inserted or + removed, the itemChanged(), itemInserted() or + itemRemoved() functions are called, respectively. These + functions must be reimplemented in derived classes in order to + update the property browser widget. Be aware that some property + instances can appear several times in an abstract tree + structure. For example: + + \table 100% + \row + \o + \code + QtProperty *property1, *property2, *property3; + + property2->addSubProperty(property1); + property3->addSubProperty(property2); + + QtAbstractPropertyBrowser *editor; + + editor->addProperty(property1); + editor->addProperty(property2); + editor->addProperty(property3); + \endcode + \o \image qtpropertybrowser-duplicate.png + \endtable + + The addProperty() function returns a QtBrowserItem that uniquely + identifies the created item. + + To make a property editable in the property browser, the + createEditor() function must be called to provide the + property with a suitable editing widget. + + Note that there are two ready-made property browser + implementations: + + \list + \o QtGroupBoxPropertyBrowser + \o QtTreePropertyBrowser + \endlist + + \sa QtAbstractPropertyManager, QtAbstractEditorFactoryBase +*/ + +/*! + \fn void QtAbstractPropertyBrowser::setFactoryForManager(PropertyManager *manager, + QtAbstractEditorFactory *factory) + + Connects the given \a manager to the given \a factory, ensuring + that properties of the \a manager's type will be displayed with an + editing widget suitable for their value. + + For example: + + \code + QtIntPropertyManager *intManager; + QtDoublePropertyManager *doubleManager; + + QtProperty *myInteger = intManager->addProperty(); + QtProperty *myDouble = doubleManager->addProperty(); + + QtSpinBoxFactory *spinBoxFactory; + QtDoubleSpinBoxFactory *doubleSpinBoxFactory; + + QtAbstractPropertyBrowser *editor; + editor->setFactoryForManager(intManager, spinBoxFactory); + editor->setFactoryForManager(doubleManager, doubleSpinBoxFactory); + + editor->addProperty(myInteger); + editor->addProperty(myDouble); + \endcode + + In this example the \c myInteger property's value is displayed + with a QSpinBox widget, while the \c myDouble property's value is + displayed with a QDoubleSpinBox widget. + + Note that a factory can be associated with many managers, but a + manager can only be associated with one single factory. If the + given \a manager already is associated with another factory, the + old association is broken before the new one established. + + This function ensures that the given \a manager and the given \a + factory are compatible, and it automatically calls the + QtAbstractEditorFactory::addPropertyManager() function if necessary. + + \sa unsetFactoryForManager() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemInserted(QtBrowserItem *insertedItem, + QtBrowserItem *precedingItem) = 0 + + This function is called to update the widget whenever a property + is inserted or added to the property browser, passing pointers to + the \a insertedItem of property and the specified + \a precedingItem as parameters. + + If \a precedingItem is 0, the \a insertedItem was put at + the beginning of its parent item's list of subproperties. If + the parent of \a insertedItem is 0, the \a insertedItem was added as a top + level property of \e this property browser. + + This function must be reimplemented in derived classes. Note that + if the \a insertedItem's property has subproperties, this + method will be called for those properties as soon as the current call is finished. + + \sa insertProperty(), addProperty() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemRemoved(QtBrowserItem *item) = 0 + + This function is called to update the widget whenever a property + is removed from the property browser, passing the pointer to the + \a item of the property as parameters. The passed \a item is + deleted just after this call is finished. + + If the the parent of \a item is 0, the removed \a item was a + top level property in this editor. + + This function must be reimplemented in derived classes. Note that + if the removed \a item's property has subproperties, this + method will be called for those properties just before the current call is started. + + \sa removeProperty() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemChanged(QtBrowserItem *item) = 0 + + This function is called whenever a property's data changes, + passing a pointer to the \a item of property as parameter. + + This function must be reimplemented in derived classes in order to + update the property browser widget whenever a property's name, + tool tip, status tip, "what's this" text, value text or value icon + changes. + + Note that if the property browser contains several occurrences of + the same property, this method will be called once for each + occurrence (with a different item each time). + + \sa QtProperty, items() +*/ + +/*! + Creates an abstract property browser with the given \a parent. +*/ +QtAbstractPropertyBrowser::QtAbstractPropertyBrowser(QWidget *parent) + : QWidget(parent) +{ + d_ptr = new QtAbstractPropertyBrowserPrivate; + d_ptr->q_ptr = this; + +} + +/*! + Destroys the property browser, and destroys all the items that were + created by this property browser. + + Note that the properties that were displayed in the editor are not + deleted since they still can be used in other editors. Neither + does the destructor delete the property managers and editor + factories that were used by this property browser widget unless + this widget was their parent. + + \sa QtAbstractPropertyManager::~QtAbstractPropertyManager() +*/ +QtAbstractPropertyBrowser::~QtAbstractPropertyBrowser() +{ + QList indexes = topLevelItems(); + QListIterator itItem(indexes); + while (itItem.hasNext()) + d_ptr->clearIndex(itItem.next()); + delete d_ptr; +} + +/*! + Returns the property browser's list of top level properties. + + To traverse the subproperties, use the QtProperty::subProperties() + function. + + \sa addProperty(), insertProperty(), removeProperty() +*/ +QList QtAbstractPropertyBrowser::properties() const +{ + return d_ptr->m_subItems; +} + +/*! + Returns the property browser's list of all items associated + with the given \a property. + + There is one item per instance of the property in the browser. + + \sa topLevelItem() +*/ + +QList QtAbstractPropertyBrowser::items(QtProperty *property) const +{ + return d_ptr->m_propertyToIndexes.value(property); +} + +/*! + Returns the top-level items associated with the given \a property. + + Returns 0 if \a property wasn't inserted into this property + browser or isn't a top-level one. + + \sa topLevelItems(), items() +*/ + +QtBrowserItem *QtAbstractPropertyBrowser::topLevelItem(QtProperty *property) const +{ + return d_ptr->m_topLevelPropertyToIndex.value(property); +} + +/*! + Returns the list of top-level items. + + \sa topLevelItem() +*/ + +QList QtAbstractPropertyBrowser::topLevelItems() const +{ + return d_ptr->m_topLevelIndexes; +} + +/*! + Removes all the properties from the editor, but does not delete + them since they can still be used in other editors. + + \sa removeProperty(), QtAbstractPropertyManager::clear() +*/ +void QtAbstractPropertyBrowser::clear() +{ + QList subList = properties(); + QListIterator itSub(subList); + itSub.toBack(); + while (itSub.hasPrevious()) { + QtProperty *property = itSub.previous(); + removeProperty(property); + } +} + +/*! + Appends the given \a property (and its subproperties) to the + property browser's list of top level properties. Returns the item + created by property browser which is associated with the \a property. + In order to get all children items created by the property + browser in this call, the returned item should be traversed. + + If the specified \a property is already added, this function does + nothing and returns 0. + + \sa insertProperty(), QtProperty::addSubProperty(), properties() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::addProperty(QtProperty *property) +{ + QtProperty *afterProperty = 0; + if (d_ptr->m_subItems.count() > 0) + afterProperty = d_ptr->m_subItems.last(); + return insertProperty(property, afterProperty); +} + +/*! + \fn QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, + QtProperty *afterProperty) + + Inserts the given \a property (and its subproperties) after + the specified \a afterProperty in the browser's list of top + level properties. Returns item created by property browser which + is associated with the \a property. In order to get all children items + created by the property browser in this call returned item should be traversed. + + If the specified \a afterProperty is 0, the given \a property is + inserted at the beginning of the list. If \a property is + already inserted, this function does nothing and returns 0. + + \sa addProperty(), QtProperty::insertSubProperty(), properties() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, + QtProperty *afterProperty) +{ + if (!property) + return 0; + + // if item is already inserted in this item then cannot add. + QList pendingList = properties(); + int pos = 0; + int newPos = 0; + QtProperty *properAfterProperty = 0; + while (pos < pendingList.count()) { + QtProperty *prop = pendingList.at(pos); + if (prop == property) + return 0; + if (prop == afterProperty) { + newPos = pos + 1; + properAfterProperty = afterProperty; + } + pos++; + } + d_ptr->createBrowserIndexes(property, 0, afterProperty); + + // traverse inserted subtree and connect to manager's signals + d_ptr->insertSubTree(property, 0); + + d_ptr->m_subItems.insert(newPos, property); + //propertyInserted(property, 0, properAfterProperty); + return topLevelItem(property); +} + +/*! + Removes the specified \a property (and its subproperties) from the + property browser's list of top level properties. All items + that were associated with the given \a property and its children + are deleted. + + Note that the properties are \e not deleted since they can still + be used in other editors. + + \sa clear(), QtProperty::removeSubProperty(), properties() +*/ +void QtAbstractPropertyBrowser::removeProperty(QtProperty *property) +{ + if (!property) + return; + + QList pendingList = properties(); + int pos = 0; + while (pos < pendingList.count()) { + if (pendingList.at(pos) == property) { + d_ptr->m_subItems.removeAt(pos); //perhaps this two lines + d_ptr->removeSubTree(property, 0); //should be moved down after propertyRemoved call. + //propertyRemoved(property, 0); + + d_ptr->removeBrowserIndexes(property, 0); + + // when item is deleted, item will call removeItem for top level items, + // and itemRemoved for nested items. + + return; + } + pos++; + } +} + +/*! + Creates an editing widget (with the given \a parent) for the given + \a property according to the previously established associations + between property managers and editor factories. + + If the property is created by a property manager which was not + associated with any of the existing factories in \e this property + editor, the function returns 0. + + To make a property editable in the property browser, the + createEditor() function must be called to provide the + property with a suitable editing widget. + + Reimplement this function to provide additional decoration for the + editing widgets created by the installed factories. + + \sa setFactoryForManager() +*/ +QWidget *QtAbstractPropertyBrowser::createEditor(QtProperty *property, + QWidget *parent) +{ + QtAbstractEditorFactoryBase *factory = 0; + QtAbstractPropertyManager *manager = property->propertyManager(); + + if (m_viewToManagerToFactory()->contains(this) && + (*m_viewToManagerToFactory())[this].contains(manager)) { + factory = (*m_viewToManagerToFactory())[this][manager]; + } + + if (!factory) + return 0; + return factory->createEditor(property, parent); +} + +bool QtAbstractPropertyBrowser::addFactory(QtAbstractPropertyManager *abstractManager, + QtAbstractEditorFactoryBase *abstractFactory) +{ + bool connectNeeded = false; + if (!m_managerToFactoryToViews()->contains(abstractManager) || + !(*m_managerToFactoryToViews())[abstractManager].contains(abstractFactory)) { + connectNeeded = true; + } else if ((*m_managerToFactoryToViews())[abstractManager][abstractFactory] + .contains(this)) { + return connectNeeded; + } + + if (m_viewToManagerToFactory()->contains(this) && + (*m_viewToManagerToFactory())[this].contains(abstractManager)) { + unsetFactoryForManager(abstractManager); + } + + (*m_managerToFactoryToViews())[abstractManager][abstractFactory].append(this); + (*m_viewToManagerToFactory())[this][abstractManager] = abstractFactory; + + return connectNeeded; +} + +/*! + Removes the association between the given \a manager and the + factory bound to it, automatically calling the + QtAbstractEditorFactory::removePropertyManager() function if necessary. + + \sa setFactoryForManager() +*/ +void QtAbstractPropertyBrowser::unsetFactoryForManager(QtAbstractPropertyManager *manager) +{ + if (!m_viewToManagerToFactory()->contains(this) || + !(*m_viewToManagerToFactory())[this].contains(manager)) { + return; + } + + QtAbstractEditorFactoryBase *abstractFactory = + (*m_viewToManagerToFactory())[this][manager]; + (*m_viewToManagerToFactory())[this].remove(manager); + if ((*m_viewToManagerToFactory())[this].isEmpty()) { + (*m_viewToManagerToFactory()).remove(this); + } + + (*m_managerToFactoryToViews())[manager][abstractFactory].removeAll(this); + if ((*m_managerToFactoryToViews())[manager][abstractFactory].isEmpty()) { + (*m_managerToFactoryToViews())[manager].remove(abstractFactory); + abstractFactory->breakConnection(manager); + if ((*m_managerToFactoryToViews())[manager].isEmpty()) { + (*m_managerToFactoryToViews()).remove(manager); + } + } +} + +/*! + Returns the current item in the property browser. + + \sa setCurrentItem() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::currentItem() const +{ + return d_ptr->m_currentItem; +} + +/*! + Sets the current item in the property browser to \a item. + + \sa currentItem(), currentItemChanged() +*/ +void QtAbstractPropertyBrowser::setCurrentItem(QtBrowserItem *item) +{ + QtBrowserItem *oldItem = d_ptr->m_currentItem; + d_ptr->m_currentItem = item; + if (oldItem != item) + emit currentItemChanged(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtpropertybrowser.cpp" diff --git a/src/qtpropertybrowser.h b/src/qtpropertybrowser.h new file mode 100644 index 0000000..c4c6275 --- /dev/null +++ b/src/qtpropertybrowser.h @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTPROPERTYBROWSER_H +#define QTPROPERTYBROWSER_H + +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +#if defined(Q_WS_WIN) +# if !defined(QT_QTPROPERTYBROWSER_EXPORT) && !defined(QT_QTPROPERTYBROWSER_IMPORT) +# define QT_QTPROPERTYBROWSER_EXPORT +# elif defined(QT_QTPROPERTYBROWSER_IMPORT) +# if defined(QT_QTPROPERTYBROWSER_EXPORT) +# undef QT_QTPROPERTYBROWSER_EXPORT +# endif +# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllimport) +# elif defined(QT_QTPROPERTYBROWSER_EXPORT) +# undef QT_QTPROPERTYBROWSER_EXPORT +# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTPROPERTYBROWSER_EXPORT +#endif + + +class QtAbstractPropertyManager; +class QtPropertyPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtProperty +{ +public: + virtual ~QtProperty(); + + QList subProperties() const; + + QtAbstractPropertyManager *propertyManager() const; + + QString toolTip() const; + QString statusTip() const; + QString whatsThis() const; + QString propertyName() const; + QString propertyId() const; + bool isEnabled() const; + bool isModified() const; + + bool hasValue() const; + QIcon valueIcon() const; + QString valueText() const; + + virtual bool compare(QtProperty* otherProperty)const; + + void setToolTip(const QString &text); + void setStatusTip(const QString &text); + void setWhatsThis(const QString &text); + void setPropertyName(const QString &text); + void setPropertyId(const QString &text); + void setEnabled(bool enable); + void setModified(bool modified); + + bool isSubProperty()const; + void addSubProperty(QtProperty *property); + void insertSubProperty(QtProperty *property, QtProperty *afterProperty); + void removeSubProperty(QtProperty *property); +protected: + explicit QtProperty(QtAbstractPropertyManager *manager); + void propertyChanged(); +private: + friend class QtAbstractPropertyManager; + QtPropertyPrivate *d_ptr; +}; + +class QtAbstractPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyManager : public QObject +{ + Q_OBJECT +public: + + explicit QtAbstractPropertyManager(QObject *parent = 0); + ~QtAbstractPropertyManager(); + + QSet properties() const; + void clear() const; + + QtProperty *addProperty(const QString &name = QString()); + QtProperty *qtProperty(const QString &id)const; +Q_SIGNALS: + + void propertyInserted(QtProperty *property, + QtProperty *parent, QtProperty *after); + void propertyChanged(QtProperty *property); + void propertyRemoved(QtProperty *property, QtProperty *parent); + void propertyDestroyed(QtProperty *property); +protected: + virtual bool hasValue(const QtProperty *property) const; + virtual QIcon valueIcon(const QtProperty *property) const; + virtual QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property) = 0; + virtual void uninitializeProperty(QtProperty *property); + virtual QtProperty *createProperty(); +private: + friend class QtProperty; + QtAbstractPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtAbstractPropertyManager) + Q_DISABLE_COPY(QtAbstractPropertyManager) +}; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractEditorFactoryBase : public QObject +{ + Q_OBJECT +public: + virtual QWidget *createEditor(QtProperty *property, QWidget *parent) = 0; +protected: + explicit QtAbstractEditorFactoryBase(QObject *parent = 0) + : QObject(parent) {} + + virtual void breakConnection(QtAbstractPropertyManager *manager) = 0; +protected Q_SLOTS: + virtual void managerDestroyed(QObject *manager) = 0; + + friend class QtAbstractPropertyBrowser; +}; + +template +class QtAbstractEditorFactory : public QtAbstractEditorFactoryBase +{ +public: + explicit QtAbstractEditorFactory(QObject *parent) : QtAbstractEditorFactoryBase(parent) {} + QWidget *createEditor(QtProperty *property, QWidget *parent) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *manager = it.next(); + if (manager == property->propertyManager()) { + return createEditor(manager, property, parent); + } + } + return 0; + } + void addPropertyManager(PropertyManager *manager) + { + if (m_managers.contains(manager)) + return; + m_managers.insert(manager); + connectPropertyManager(manager); + connect(manager, SIGNAL(destroyed(QObject *)), + this, SLOT(managerDestroyed(QObject *))); + } + void removePropertyManager(PropertyManager *manager) + { + if (!m_managers.contains(manager)) + return; + disconnect(manager, SIGNAL(destroyed(QObject *)), + this, SLOT(managerDestroyed(QObject *))); + disconnectPropertyManager(manager); + m_managers.remove(manager); + } + QSet propertyManagers() const + { + return m_managers; + } + PropertyManager *propertyManager(QtProperty *property) const + { + QtAbstractPropertyManager *manager = property->propertyManager(); + QSetIterator itManager(m_managers); + while (itManager.hasNext()) { + PropertyManager *m = itManager.next(); + if (m == manager) { + return m; + } + } + return 0; + } +protected: + virtual void connectPropertyManager(PropertyManager *manager) = 0; + virtual QWidget *createEditor(PropertyManager *manager, QtProperty *property, + QWidget *parent) = 0; + virtual void disconnectPropertyManager(PropertyManager *manager) = 0; + void managerDestroyed(QObject *manager) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *m = it.next(); + if (m == manager) { + m_managers.remove(m); + return; + } + } + } +private: + void breakConnection(QtAbstractPropertyManager *manager) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *m = it.next(); + if (m == manager) { + removePropertyManager(m); + return; + } + } + } +private: + QSet m_managers; + friend class QtAbstractPropertyEditor; +}; + +class QtAbstractPropertyBrowser; +class QtBrowserItemPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtBrowserItem +{ +public: + QtProperty *property() const; + QtBrowserItem *parent() const; + QList children() const; + QtAbstractPropertyBrowser *browser() const; +private: + explicit QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent); + ~QtBrowserItem(); + QtBrowserItemPrivate *d_ptr; + friend class QtAbstractPropertyBrowserPrivate; +}; + +class QtAbstractPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyBrowser : public QWidget +{ + Q_OBJECT +public: + + explicit QtAbstractPropertyBrowser(QWidget *parent = 0); + ~QtAbstractPropertyBrowser(); + + QList properties() const; + QList items(QtProperty *property) const; + QtBrowserItem *topLevelItem(QtProperty *property) const; + QList topLevelItems() const; + void clear(); + + template + void setFactoryForManager(PropertyManager *manager, + QtAbstractEditorFactory *factory) { + QtAbstractPropertyManager *abstractManager = manager; + QtAbstractEditorFactoryBase *abstractFactory = factory; + + if (addFactory(abstractManager, abstractFactory)) + factory->addPropertyManager(manager); + } + + void unsetFactoryForManager(QtAbstractPropertyManager *manager); + + QtBrowserItem *currentItem() const; + void setCurrentItem(QtBrowserItem *); + +Q_SIGNALS: + void currentItemChanged(QtBrowserItem *); + +public Q_SLOTS: + + QtBrowserItem *addProperty(QtProperty *property); + QtBrowserItem *insertProperty(QtProperty *property, QtProperty *afterProperty); + void removeProperty(QtProperty *property); + +protected: + + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) = 0; + virtual void itemRemoved(QtBrowserItem *item) = 0; + // can be tooltip, statustip, whatsthis, name, icon, text. + virtual void itemChanged(QtBrowserItem *item) = 0; + + virtual QWidget *createEditor(QtProperty *property, QWidget *parent); +private: + + bool addFactory(QtAbstractPropertyManager *abstractManager, + QtAbstractEditorFactoryBase *abstractFactory); + + QtAbstractPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtAbstractPropertyBrowser) + Q_DISABLE_COPY(QtAbstractPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, + QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDataChanged(QtProperty *)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif // QTPROPERTYBROWSER_H diff --git a/src/qtpropertybrowser.pri b/src/qtpropertybrowser.pri new file mode 100644 index 0000000..6a6050c --- /dev/null +++ b/src/qtpropertybrowser.pri @@ -0,0 +1,30 @@ +include(../common.pri) +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +qtpropertybrowser-uselib:!qtpropertybrowser-buildlib { + LIBS += -L$$QTPROPERTYBROWSER_LIBDIR -l$$QTPROPERTYBROWSER_LIBNAME +} else { + SOURCES += $$PWD/qtpropertybrowser.cpp \ + $$PWD/qtpropertymanager.cpp \ + $$PWD/qteditorfactory.cpp \ + $$PWD/qtvariantproperty.cpp \ + $$PWD/qttreepropertybrowser.cpp \ + $$PWD/qtbuttonpropertybrowser.cpp \ + $$PWD/qtgroupboxpropertybrowser.cpp \ + $$PWD/qtpropertybrowserutils.cpp + HEADERS += $$PWD/qtpropertybrowser.h \ + $$PWD/qtpropertymanager.h \ + $$PWD/qteditorfactory.h \ + $$PWD/qtvariantproperty.h \ + $$PWD/qttreepropertybrowser.h \ + $$PWD/qtbuttonpropertybrowser.h \ + $$PWD/qtgroupboxpropertybrowser.h \ + $$PWD/qtpropertybrowserutils_p.h + RESOURCES += $$PWD/qtpropertybrowser.qrc +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTPROPERTYBROWSER_EXPORT + else:qtpropertybrowser-uselib:DEFINES += QT_QTPROPERTYBROWSER_IMPORT +} diff --git a/src/qtpropertybrowser.qrc b/src/qtpropertybrowser.qrc new file mode 100644 index 0000000..4f91ab7 --- /dev/null +++ b/src/qtpropertybrowser.qrc @@ -0,0 +1,23 @@ + + + images/cursor-arrow.png + images/cursor-busy.png + images/cursor-closedhand.png + images/cursor-cross.png + images/cursor-forbidden.png + images/cursor-hand.png + images/cursor-hsplit.png + images/cursor-ibeam.png + images/cursor-openhand.png + images/cursor-sizeall.png + images/cursor-sizeb.png + images/cursor-sizef.png + images/cursor-sizeh.png + images/cursor-sizev.png + images/cursor-uparrow.png + images/cursor-vsplit.png + images/cursor-wait.png + images/cursor-whatsthis.png + + + diff --git a/src/qtpropertybrowserutils.cpp b/src/qtpropertybrowserutils.cpp new file mode 100644 index 0000000..cd19583 --- /dev/null +++ b/src/qtpropertybrowserutils.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +QtCursorDatabase::QtCursorDatabase() +{ + appendCursor(Qt::ArrowCursor, QApplication::translate("QtCursorDatabase", "Arrow", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-arrow.png"))); + appendCursor(Qt::UpArrowCursor, QApplication::translate("QtCursorDatabase", "Up Arrow", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-uparrow.png"))); + appendCursor(Qt::CrossCursor, QApplication::translate("QtCursorDatabase", "Cross", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-cross.png"))); + appendCursor(Qt::WaitCursor, QApplication::translate("QtCursorDatabase", "Wait", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-wait.png"))); + appendCursor(Qt::IBeamCursor, QApplication::translate("QtCursorDatabase", "IBeam", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-ibeam.png"))); + appendCursor(Qt::SizeVerCursor, QApplication::translate("QtCursorDatabase", "Size Vertical", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizev.png"))); + appendCursor(Qt::SizeHorCursor, QApplication::translate("QtCursorDatabase", "Size Horizontal", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeh.png"))); + appendCursor(Qt::SizeFDiagCursor, QApplication::translate("QtCursorDatabase", "Size Backslash", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizef.png"))); + appendCursor(Qt::SizeBDiagCursor, QApplication::translate("QtCursorDatabase", "Size Slash", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeb.png"))); + appendCursor(Qt::SizeAllCursor, QApplication::translate("QtCursorDatabase", "Size All", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeall.png"))); + appendCursor(Qt::BlankCursor, QApplication::translate("QtCursorDatabase", "Blank", 0), QIcon()); + appendCursor(Qt::SplitVCursor, QApplication::translate("QtCursorDatabase", "Split Vertical", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-vsplit.png"))); + appendCursor(Qt::SplitHCursor, QApplication::translate("QtCursorDatabase", "Split Horizontal", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-hsplit.png"))); + appendCursor(Qt::PointingHandCursor, QApplication::translate("QtCursorDatabase", "Pointing Hand", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-hand.png"))); + appendCursor(Qt::ForbiddenCursor, QApplication::translate("QtCursorDatabase", "Forbidden", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-forbidden.png"))); + appendCursor(Qt::OpenHandCursor, QApplication::translate("QtCursorDatabase", "Open Hand", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-openhand.png"))); + appendCursor(Qt::ClosedHandCursor, QApplication::translate("QtCursorDatabase", "Closed Hand", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-closedhand.png"))); + appendCursor(Qt::WhatsThisCursor, QApplication::translate("QtCursorDatabase", "What's This", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-whatsthis.png"))); + appendCursor(Qt::BusyCursor, QApplication::translate("QtCursorDatabase", "Busy", 0), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-busy.png"))); +} + +void QtCursorDatabase::appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon) +{ + if (m_cursorShapeToValue.contains(shape)) + return; + int value = m_cursorNames.count(); + m_cursorNames.append(name); + m_cursorIcons[value] = icon; + m_valueToCursorShape[value] = shape; + m_cursorShapeToValue[shape] = value; +} + +QStringList QtCursorDatabase::cursorShapeNames() const +{ + return m_cursorNames; +} + +QMap QtCursorDatabase::cursorShapeIcons() const +{ + return m_cursorIcons; +} + +QString QtCursorDatabase::cursorToShapeName(const QCursor &cursor) const +{ + int val = cursorToValue(cursor); + if (val >= 0) + return m_cursorNames.at(val); + return QString(); +} + +QIcon QtCursorDatabase::cursorToShapeIcon(const QCursor &cursor) const +{ + int val = cursorToValue(cursor); + return m_cursorIcons.value(val); +} + +int QtCursorDatabase::cursorToValue(const QCursor &cursor) const +{ +#ifndef QT_NO_CURSOR + Qt::CursorShape shape = cursor.shape(); + if (m_cursorShapeToValue.contains(shape)) + return m_cursorShapeToValue[shape]; +#endif + return -1; +} + +#ifndef QT_NO_CURSOR +QCursor QtCursorDatabase::valueToCursor(int value) const +{ + if (m_valueToCursorShape.contains(value)) + return QCursor(m_valueToCursorShape[value]); + return QCursor(); +} +#endif + +QPixmap QtPropertyBrowserUtils::brushValuePixmap(const QBrush &b) +{ + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + + QPainter painter(&img); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(0, 0, img.width(), img.height(), b); + QColor color = b.color(); + if (color.alpha() != 255) { // indicate alpha by an inset + QBrush opaqueBrush = b; + color.setAlpha(255); + opaqueBrush.setColor(color); + painter.fillRect(img.width() / 4, img.height() / 4, + img.width() / 2, img.height() / 2, opaqueBrush); + } + painter.end(); + return QPixmap::fromImage(img); +} + +QIcon QtPropertyBrowserUtils::brushValueIcon(const QBrush &b) +{ + return QIcon(brushValuePixmap(b)); +} + +QString QtPropertyBrowserUtils::colorValueText(const QColor &c) +{ + return QApplication::translate("QtPropertyBrowserUtils", "[%1, %2, %3] (%4)", 0) + .arg(QString::number(c.red())) + .arg(QString::number(c.green())) + .arg(QString::number(c.blue())) + .arg(QString::number(c.alpha())); +} + +QPixmap QtPropertyBrowserUtils::fontValuePixmap(const QFont &font) +{ + QFont f = font; + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter p(&img); + p.setRenderHint(QPainter::TextAntialiasing, true); + p.setRenderHint(QPainter::Antialiasing, true); + f.setPointSize(13); + p.setFont(f); + QTextOption t; + t.setAlignment(Qt::AlignCenter); + p.drawText(QRect(0, 0, 16, 16), QString(QLatin1Char('A')), t); + return QPixmap::fromImage(img); +} + +QIcon QtPropertyBrowserUtils::fontValueIcon(const QFont &f) +{ + return QIcon(fontValuePixmap(f)); +} + +QString QtPropertyBrowserUtils::fontValueText(const QFont &f) +{ + return QApplication::translate("QtPropertyBrowserUtils", "[%1, %2]", 0) + .arg(f.family()) + .arg(f.pointSize()); +} + + +QtBoolEdit::QtBoolEdit(QWidget *parent) : + QWidget(parent), + m_checkBox(new QCheckBox(this)), + m_textVisible(true) +{ + QHBoxLayout *lt = new QHBoxLayout; + if (QApplication::layoutDirection() == Qt::LeftToRight) + lt->setContentsMargins(4, 0, 0, 0); + else + lt->setContentsMargins(0, 0, 4, 0); + lt->addWidget(m_checkBox); + setLayout(lt); + connect(m_checkBox, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); + setFocusProxy(m_checkBox); + m_checkBox->setText(tr("True")); +} + +void QtBoolEdit::setTextVisible(bool textVisible) +{ + if (m_textVisible == textVisible) + return; + + m_textVisible = textVisible; + if (m_textVisible) + m_checkBox->setText(isChecked() ? tr("True") : tr("False")); + else + m_checkBox->setText(QString()); +} + +Qt::CheckState QtBoolEdit::checkState() const +{ + return m_checkBox->checkState(); +} + +void QtBoolEdit::setCheckState(Qt::CheckState state) +{ + m_checkBox->setCheckState(state); +} + +bool QtBoolEdit::isChecked() const +{ + return m_checkBox->isChecked(); +} + +void QtBoolEdit::setChecked(bool c) +{ + m_checkBox->setChecked(c); + if (!m_textVisible) + return; + m_checkBox->setText(isChecked() ? tr("True") : tr("False")); +} + +bool QtBoolEdit::blockCheckBoxSignals(bool block) +{ + return m_checkBox->blockSignals(block); +} + +void QtBoolEdit::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::LeftButton) { + m_checkBox->click(); + event->accept(); + } else { + QWidget::mousePressEvent(event); + } +} + +void QtBoolEdit::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + + + +QtKeySequenceEdit::QtKeySequenceEdit(QWidget *parent) + : QWidget(parent), m_num(0), m_lineEdit(new QLineEdit(this)) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_lineEdit); + layout->setMargin(0); + m_lineEdit->installEventFilter(this); + m_lineEdit->setReadOnly(true); + m_lineEdit->setFocusProxy(this); + setFocusPolicy(m_lineEdit->focusPolicy()); + setAttribute(Qt::WA_InputMethodEnabled); +} + +bool QtKeySequenceEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o == m_lineEdit && e->type() == QEvent::ContextMenu) { + QContextMenuEvent *c = static_cast(e); + QMenu *menu = m_lineEdit->createStandardContextMenu(); + const QList actions = menu->actions(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + action->setShortcut(QKeySequence()); + QString actionString = action->text(); + const int pos = actionString.lastIndexOf(QLatin1Char('\t')); + if (pos > 0) + actionString.remove(pos, actionString.length() - pos); + action->setText(actionString); + } + QAction *actionBefore = 0; + if (actions.count() > 0) + actionBefore = actions[0]; + QAction *clearAction = new QAction(tr("Clear Shortcut"), menu); + menu->insertAction(actionBefore, clearAction); + menu->insertSeparator(actionBefore); + clearAction->setEnabled(!m_keySequence.isEmpty()); + connect(clearAction, SIGNAL(triggered()), this, SLOT(slotClearShortcut())); + menu->exec(c->globalPos()); + delete menu; + e->accept(); + return true; + } + + return QWidget::eventFilter(o, e); +} + +void QtKeySequenceEdit::slotClearShortcut() +{ + if (m_keySequence.isEmpty()) + return; + setKeySequence(QKeySequence()); + emit keySequenceChanged(m_keySequence); +} + +void QtKeySequenceEdit::handleKeyEvent(QKeyEvent *e) +{ + int nextKey = e->key(); + if (nextKey == Qt::Key_Control || nextKey == Qt::Key_Shift || + nextKey == Qt::Key_Meta || nextKey == Qt::Key_Alt || + nextKey == Qt::Key_Super_L || nextKey == Qt::Key_AltGr) + return; + + nextKey |= translateModifiers(e->modifiers(), e->text()); + int k0 = m_keySequence[0]; + int k1 = m_keySequence[1]; + int k2 = m_keySequence[2]; + int k3 = m_keySequence[3]; + switch (m_num) { + case 0: k0 = nextKey; k1 = 0; k2 = 0; k3 = 0; break; + case 1: k1 = nextKey; k2 = 0; k3 = 0; break; + case 2: k2 = nextKey; k3 = 0; break; + case 3: k3 = nextKey; break; + default: break; + } + ++m_num; + if (m_num > 3) + m_num = 0; + m_keySequence = QKeySequence(k0, k1, k2, k3); + m_lineEdit->setText(m_keySequence.toString(QKeySequence::NativeText)); + e->accept(); + emit keySequenceChanged(m_keySequence); +} + +void QtKeySequenceEdit::setKeySequence(const QKeySequence &sequence) +{ + if (sequence == m_keySequence) + return; + m_num = 0; + m_keySequence = sequence; + m_lineEdit->setText(m_keySequence.toString(QKeySequence::NativeText)); +} + +QKeySequence QtKeySequenceEdit::keySequence() const +{ + return m_keySequence; +} + +int QtKeySequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString &text) const +{ + int result = 0; + if ((state & Qt::ShiftModifier) && (text.size() == 0 || !text.at(0).isPrint() || text.at(0).isLetter() || text.at(0).isSpace())) + result |= Qt::SHIFT; + if (state & Qt::ControlModifier) + result |= Qt::CTRL; + if (state & Qt::MetaModifier) + result |= Qt::META; + if (state & Qt::AltModifier) + result |= Qt::ALT; + return result; +} + +void QtKeySequenceEdit::focusInEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + m_lineEdit->selectAll(); + QWidget::focusInEvent(e); +} + +void QtKeySequenceEdit::focusOutEvent(QFocusEvent *e) +{ + m_num = 0; + m_lineEdit->event(e); + QWidget::focusOutEvent(e); +} + +void QtKeySequenceEdit::keyPressEvent(QKeyEvent *e) +{ + handleKeyEvent(e); + e->accept(); +} + +void QtKeySequenceEdit::keyReleaseEvent(QKeyEvent *e) +{ + m_lineEdit->event(e); +} + +void QtKeySequenceEdit::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +bool QtKeySequenceEdit::event(QEvent *e) +{ + if (e->type() == QEvent::Shortcut || + e->type() == QEvent::ShortcutOverride || + e->type() == QEvent::KeyRelease) { + e->accept(); + return true; + } + return QWidget::event(e); +} + + + + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif diff --git a/src/qtpropertybrowserutils_p.h b/src/qtpropertybrowserutils_p.h new file mode 100644 index 0000000..24c6500 --- /dev/null +++ b/src/qtpropertybrowserutils_p.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTPROPERTYBROWSERUTILS_H +#define QTPROPERTYBROWSERUTILS_H + +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QMouseEvent; +class QCheckBox; +class QLineEdit; + +class QtCursorDatabase +{ +public: + QtCursorDatabase(); + + QStringList cursorShapeNames() const; + QMap cursorShapeIcons() const; + QString cursorToShapeName(const QCursor &cursor) const; + QIcon cursorToShapeIcon(const QCursor &cursor) const; + int cursorToValue(const QCursor &cursor) const; +#ifndef QT_NO_CURSOR + QCursor valueToCursor(int value) const; +#endif +private: + void appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon); + QStringList m_cursorNames; + QMap m_cursorIcons; + QMap m_valueToCursorShape; + QMap m_cursorShapeToValue; +}; + +class QtPropertyBrowserUtils +{ +public: + static QPixmap brushValuePixmap(const QBrush &b); + static QIcon brushValueIcon(const QBrush &b); + static QString colorValueText(const QColor &c); + static QPixmap fontValuePixmap(const QFont &f); + static QIcon fontValueIcon(const QFont &f); + static QString fontValueText(const QFont &f); +}; + +class QtBoolEdit : public QWidget { + Q_OBJECT +public: + QtBoolEdit(QWidget *parent = 0); + + bool textVisible() const { return m_textVisible; } + void setTextVisible(bool textVisible); + + Qt::CheckState checkState() const; + void setCheckState(Qt::CheckState state); + + bool isChecked() const; + void setChecked(bool c); + + bool blockCheckBoxSignals(bool block); + +Q_SIGNALS: + void toggled(bool); + +protected: + void mousePressEvent(QMouseEvent * event); + void paintEvent(QPaintEvent *); + +private: + QCheckBox *m_checkBox; + bool m_textVisible; +}; + +class QtKeySequenceEdit : public QWidget +{ + Q_OBJECT +public: + QtKeySequenceEdit(QWidget *parent = 0); + + QKeySequence keySequence() const; + bool eventFilter(QObject *o, QEvent *e); +public Q_SLOTS: + void setKeySequence(const QKeySequence &sequence); +Q_SIGNALS: + void keySequenceChanged(const QKeySequence &sequence); +protected: + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *); + bool event(QEvent *e); +private slots: + void slotClearShortcut(); +private: + void handleKeyEvent(QKeyEvent *e); + int translateModifiers(Qt::KeyboardModifiers state, const QString &text) const; + + int m_num; + QKeySequence m_keySequence; + QLineEdit *m_lineEdit; +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qtpropertymanager.cpp b/src/qtpropertymanager.cpp new file mode 100644 index 0000000..ef627e9 --- /dev/null +++ b/src/qtpropertymanager.cpp @@ -0,0 +1,6425 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qtpropertymanager.h" +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +template +static void setSimpleMinimumData(PrivateData *data, const Value &minVal) +{ + data->minVal = minVal; + if (data->maxVal < data->minVal) + data->maxVal = data->minVal; + + if (data->val < data->minVal) + data->val = data->minVal; +} + +template +static void setSimpleMaximumData(PrivateData *data, const Value &maxVal) +{ + data->maxVal = maxVal; + if (data->minVal > data->maxVal) + data->minVal = data->maxVal; + + if (data->val > data->maxVal) + data->val = data->maxVal; +} + +template +static void setSizeMinimumData(PrivateData *data, const Value &newMinVal) +{ + data->minVal = newMinVal; + if (data->maxVal.width() < data->minVal.width()) + data->maxVal.setWidth(data->minVal.width()); + if (data->maxVal.height() < data->minVal.height()) + data->maxVal.setHeight(data->minVal.height()); + + if (data->val.width() < data->minVal.width()) + data->val.setWidth(data->minVal.width()); + if (data->val.height() < data->minVal.height()) + data->val.setHeight(data->minVal.height()); +} + +template +static void setSizeMaximumData(PrivateData *data, const Value &newMaxVal) +{ + data->maxVal = newMaxVal; + if (data->minVal.width() > data->maxVal.width()) + data->minVal.setWidth(data->maxVal.width()); + if (data->minVal.height() > data->maxVal.height()) + data->minVal.setHeight(data->maxVal.height()); + + if (data->val.width() > data->maxVal.width()) + data->val.setWidth(data->maxVal.width()); + if (data->val.height() > data->maxVal.height()) + data->val.setHeight(data->maxVal.height()); +} + +template +static SizeValue qBoundSize(const SizeValue &minVal, const SizeValue &val, const SizeValue &maxVal) +{ + SizeValue croppedVal = val; + if (minVal.width() > val.width()) + croppedVal.setWidth(minVal.width()); + else if (maxVal.width() < val.width()) + croppedVal.setWidth(maxVal.width()); + + if (minVal.height() > val.height()) + croppedVal.setHeight(minVal.height()); + else if (maxVal.height() < val.height()) + croppedVal.setHeight(maxVal.height()); + + return croppedVal; +} + +// Match the exact signature of qBound for VS 6. +QSize qBound(QSize minVal, QSize val, QSize maxVal) +{ + return qBoundSize(minVal, val, maxVal); +} + +QSizeF qBound(QSizeF minVal, QSizeF val, QSizeF maxVal) +{ + return qBoundSize(minVal, val, maxVal); +} + +namespace { + +namespace { +template +void orderBorders(Value &minVal, Value &maxVal) +{ + if (minVal > maxVal) + qSwap(minVal, maxVal); +} + +template +static void orderSizeBorders(Value &minVal, Value &maxVal) +{ + Value fromSize = minVal; + Value toSize = maxVal; + if (fromSize.width() > toSize.width()) { + fromSize.setWidth(maxVal.width()); + toSize.setWidth(minVal.width()); + } + if (fromSize.height() > toSize.height()) { + fromSize.setHeight(maxVal.height()); + toSize.setHeight(minVal.height()); + } + minVal = fromSize; + maxVal = toSize; +} + +void orderBorders(QSize &minVal, QSize &maxVal) +{ + orderSizeBorders(minVal, maxVal); +} + +void orderBorders(QSizeF &minVal, QSizeF &maxVal) +{ + orderSizeBorders(minVal, maxVal); +} + +} +} +//////// + +template +static Value getData(const QMap &propertyMap, + Value PrivateData::*data, + const QtProperty *property, const Value &defaultValue = Value()) +{ + typedef QMap PropertyToData; + typedef typename PropertyToData::const_iterator PropertyToDataConstIterator; + const PropertyToDataConstIterator it = propertyMap.constFind(property); + if (it == propertyMap.constEnd()) + return defaultValue; + return it.value().*data; +} + +template +static Value getValue(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::val, property, defaultValue); +} + +template +static Value getMinimum(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::minVal, property, defaultValue); +} + +template +static Value getMaximum(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::maxVal, property, defaultValue); +} + +template +static void setSimpleValue(QMap &propertyMap, + PropertyManager *manager, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + QtProperty *property, const Value &val) +{ + typedef QMap PropertyToData; + typedef typename PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = propertyMap.find(property); + if (it == propertyMap.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, val); +} + +template +static void setValueInRange(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + QtProperty *property, const Value &val, + void (PropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, ValueChangeParameter)) +{ + typedef typename PropertyManagerPrivate::Data PrivateData; + typedef QMap PropertyToData; + typedef typename PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + PrivateData &data = it.value(); + + if (data.val == val) + return; + + const Value oldVal = data.val; + + data.val = qBound(data.minVal, val, data.maxVal); + + if (data.val == oldVal) + return; + + if (setSubPropertyValue) + (managerPrivate->*setSubPropertyValue)(property, data.val); + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setBorderValues(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &minVal, const Value &maxVal, + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) +{ + typedef typename PropertyManagerPrivate::Data PrivateData; + typedef QMap PropertyToData; + typedef typename PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + Value fromVal = minVal; + Value toVal = maxVal; + orderBorders(fromVal, toVal); + + PrivateData &data = it.value(); + + if (data.minVal == fromVal && data.maxVal == toVal) + return; + + const Value oldVal = data.val; + + data.setMinimumValue(fromVal); + data.setMaximumValue(toVal); + + emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); + + if (setSubPropertyRange) + (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); + + if (data.val == oldVal) + return; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setBorderValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, + Value (PrivateData::*getRangeVal)() const, + void (PrivateData::*setRangeVal)(ValueChangeParameter), const Value &borderVal, + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) +{ + typedef QMap PropertyToData; + typedef typename PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + PrivateData &data = it.value(); + + if ((data.*getRangeVal)() == borderVal) + return; + + const Value oldVal = data.val; + + (data.*setRangeVal)(borderVal); + + emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); + + if (setSubPropertyRange) + (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); + + if (data.val == oldVal) + return; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setMinimumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &minVal) +{ + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; + setBorderValue(manager, managerPrivate, + propertyChangedSignal, valueChangedSignal, rangeChangedSignal, + property, &PropertyManagerPrivate::Data::minimumValue, &PropertyManagerPrivate::Data::setMinimumValue, minVal, setSubPropertyRange); +} + +template +static void setMaximumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &maxVal) +{ + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; + setBorderValue(manager, managerPrivate, + propertyChangedSignal, valueChangedSignal, rangeChangedSignal, + property, &PropertyManagerPrivate::Data::maximumValue, &PropertyManagerPrivate::Data::setMaximumValue, maxVal, setSubPropertyRange); +} + +class QtMetaEnumWrapper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSizePolicy::Policy policy READ policy) +public: + QSizePolicy::Policy policy() const { return QSizePolicy::Ignored; } +private: + QtMetaEnumWrapper(QObject *parent) : QObject(parent) {} +}; + +class QtMetaEnumProvider +{ +public: + QtMetaEnumProvider(); + + QStringList policyEnumNames() const { return m_policyEnumNames; } + QStringList languageEnumNames() const { return m_languageEnumNames; } + QStringList countryEnumNames(QLocale::Language language) const { return m_countryEnumNames.value(language); } + + QSizePolicy::Policy indexToSizePolicy(int index) const; + int sizePolicyToIndex(QSizePolicy::Policy policy) const; + + void indexToLocale(int languageIndex, int countryIndex, QLocale::Language *language, QLocale::Country *country) const; + void localeToIndex(QLocale::Language language, QLocale::Country country, int *languageIndex, int *countryIndex) const; + +private: + void initLocale(); + + QStringList m_policyEnumNames; + QStringList m_languageEnumNames; + QMap m_countryEnumNames; + QMap m_indexToLanguage; + QMap m_languageToIndex; + QMap > m_indexToCountry; + QMap > m_countryToIndex; + QMetaEnum m_policyEnum; +}; + +#if QT_VERSION < 0x040300 + +static QList countriesForLanguage(QLocale::Language language) +{ + QList countries; + QLocale::Country country = QLocale::AnyCountry; + while (country <= QLocale::LastCountry) { + QLocale locale(language, country); + if (locale.language() == language && !countries.contains(locale.country())) + countries << locale.country(); + country = (QLocale::Country)((uint)country + 1); // ++country + } + return countries; +} + +#endif + +static QList sortCountries(const QList &countries) +{ + QMultiMap nameToCountry; + QListIterator itCountry(countries); + while (itCountry.hasNext()) { + QLocale::Country country = itCountry.next(); + nameToCountry.insert(QLocale::countryToString(country), country); + } + return nameToCountry.values(); +} + +void QtMetaEnumProvider::initLocale() +{ + QMultiMap nameToLanguage; + QLocale::Language language = QLocale::C; + while (language <= QLocale::LastLanguage) { + QLocale locale(language); + if (locale.language() == language) + nameToLanguage.insert(QLocale::languageToString(language), language); + language = (QLocale::Language)((uint)language + 1); // ++language + } + + const QLocale system = QLocale::system(); + if (!nameToLanguage.contains(QLocale::languageToString(system.language()))) + nameToLanguage.insert(QLocale::languageToString(system.language()), system.language()); + + QList languages = nameToLanguage.values(); + QListIterator itLang(languages); + while (itLang.hasNext()) { + QLocale::Language language = itLang.next(); + QList countries; +#if QT_VERSION < 0x040300 + countries = countriesForLanguage(language); +#else + countries = QLocale::countriesForLanguage(language); +#endif + if (countries.isEmpty() && language == system.language()) + countries << system.country(); + + if (!countries.isEmpty() && !m_languageToIndex.contains(language)) { + countries = sortCountries(countries); + int langIdx = m_languageEnumNames.count(); + m_indexToLanguage[langIdx] = language; + m_languageToIndex[language] = langIdx; + QStringList countryNames; + QListIterator it(countries); + int countryIdx = 0; + while (it.hasNext()) { + QLocale::Country country = it.next(); + countryNames << QLocale::countryToString(country); + m_indexToCountry[langIdx][countryIdx] = country; + m_countryToIndex[language][country] = countryIdx; + ++countryIdx; + } + m_languageEnumNames << QLocale::languageToString(language); + m_countryEnumNames[language] = countryNames; + } + } +} + +QtMetaEnumProvider::QtMetaEnumProvider() +{ + QMetaProperty p; + + p = QtMetaEnumWrapper::staticMetaObject.property( + QtMetaEnumWrapper::staticMetaObject.propertyOffset() + 0); + m_policyEnum = p.enumerator(); + const int keyCount = m_policyEnum.keyCount(); + for (int i = 0; i < keyCount; i++) + m_policyEnumNames << QLatin1String(m_policyEnum.key(i)); + + initLocale(); +} + +QSizePolicy::Policy QtMetaEnumProvider::indexToSizePolicy(int index) const +{ + return static_cast(m_policyEnum.value(index)); +} + +int QtMetaEnumProvider::sizePolicyToIndex(QSizePolicy::Policy policy) const +{ + const int keyCount = m_policyEnum.keyCount(); + for (int i = 0; i < keyCount; i++) + if (indexToSizePolicy(i) == policy) + return i; + return -1; +} + +void QtMetaEnumProvider::indexToLocale(int languageIndex, int countryIndex, QLocale::Language *language, QLocale::Country *country) const +{ + QLocale::Language l = QLocale::C; + QLocale::Country c = QLocale::AnyCountry; + if (m_indexToLanguage.contains(languageIndex)) { + l = m_indexToLanguage[languageIndex]; + if (m_indexToCountry.contains(languageIndex) && m_indexToCountry[languageIndex].contains(countryIndex)) + c = m_indexToCountry[languageIndex][countryIndex]; + } + if (language) + *language = l; + if (country) + *country = c; +} + +void QtMetaEnumProvider::localeToIndex(QLocale::Language language, QLocale::Country country, int *languageIndex, int *countryIndex) const +{ + int l = -1; + int c = -1; + if (m_languageToIndex.contains(language)) { + l = m_languageToIndex[language]; + if (m_countryToIndex.contains(language) && m_countryToIndex[language].contains(country)) + c = m_countryToIndex[language][country]; + } + + if (languageIndex) + *languageIndex = l; + if (countryIndex) + *countryIndex = c; +} + +Q_GLOBAL_STATIC(QtMetaEnumProvider, metaEnumProvider) + +// QtGroupPropertyManager + +/*! + \class QtGroupPropertyManager + + \brief The QtGroupPropertyManager provides and manages group properties. + + This class is intended to provide a grouping element without any value. + + \sa QtAbstractPropertyManager +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtGroupPropertyManager::QtGroupPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtGroupPropertyManager::~QtGroupPropertyManager() +{ + +} + +/*! + \reimp +*/ +bool QtGroupPropertyManager::hasValue(const QtProperty *property) const +{ + Q_UNUSED(property) + return false; +} + +/*! + \reimp +*/ +void QtGroupPropertyManager::initializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +/*! + \reimp +*/ +void QtGroupPropertyManager::uninitializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +// QtIntPropertyManager + +class QtIntPropertyManagerPrivate +{ + QtIntPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtIntPropertyManager) +public: + + struct Data + { + Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1) {} + int val; + int minVal; + int maxVal; + int singleStep; + int minimumValue() const { return minVal; } + int maximumValue() const { return maxVal; } + void setMinimumValue(int newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(int newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtIntPropertyManager + + \brief The QtIntPropertyManager provides and manages int properties. + + An int property has a current value, and a range specifying the + valid values. The range is defined by a minimum and a maximum + value. + + The property's value and range can be retrieved using the value(), + minimum() and maximum() functions, and can be set using the + setValue(), setMinimum() and setMaximum() slots. Alternatively, + the range can be defined in one go using the setRange() slot. + + In addition, QtIntPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes, + and the rangeChanged() signal which is emitted whenever such a + property changes its range of valid values. + + \sa QtAbstractPropertyManager, QtSpinBoxFactory, QtSliderFactory, QtScrollBarFactory +*/ + +/*! + \fn void QtIntPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtIntPropertyManager::rangeChanged(QtProperty *property, int minimum, int maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid values, passing a pointer to the + \a property and the new \a minimum and \a maximum values. + + \sa setRange() +*/ + +/*! + \fn void QtIntPropertyManager::singleStepChanged(QtProperty *property, int step) + + This signal is emitted whenever a property created by this manager + changes its single step property, passing a pointer to the + \a property and the new \a step value + + \sa setSingleStep() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtIntPropertyManager::QtIntPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtIntPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtIntPropertyManager::~QtIntPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa setValue() +*/ +int QtIntPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's minimum value. + + \sa setMinimum(), maximum(), setRange() +*/ +int QtIntPropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's maximum value. + + \sa setMaximum(), minimum(), setRange() +*/ +int QtIntPropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's step value. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa setSingleStep() +*/ +int QtIntPropertyManager::singleStep(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtIntPropertyManagerPrivate::Data::singleStep, property, 0); +} + +/*! + \reimp +*/ +QString QtIntPropertyManager::valueText(const QtProperty *property) const +{ + const QtIntPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return QString::number(it.value().val); +} + +/*! + \fn void QtIntPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's range, the \a value is adjusted to the nearest valid + value within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtIntPropertyManager::setValue(QtProperty *property, int val) +{ + void (QtIntPropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, int) = 0; + setValueInRange(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtIntPropertyManager::setMinimum(QtProperty *property, int minVal) +{ + setMinimumValue(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting maximum value, the minimum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtIntPropertyManager::setMaximum(QtProperty *property, int maxVal) +{ + setMaximumValue(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtIntPropertyManager::setRange(QtProperty *property, int minimum, int maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtIntPropertyManager::setRange(QtProperty *property, int minVal, int maxVal) +{ + void (QtIntPropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, int, int, int) = 0; + setBorderValues(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + Sets the step value for the given \a property to \a step. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa singleStep() +*/ +void QtIntPropertyManager::setSingleStep(QtProperty *property, int step) +{ + const QtIntPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtIntPropertyManagerPrivate::Data data = it.value(); + + if (step < 0) + step = 0; + + if (data.singleStep == step) + return; + + data.singleStep = step; + + it.value() = data; + + emit singleStepChanged(property, data.singleStep); +} + +/*! + \reimp +*/ +void QtIntPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtIntPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtIntPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDoublePropertyManager + +class QtDoublePropertyManagerPrivate +{ + QtDoublePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDoublePropertyManager) +public: + + struct Data + { + Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1), decimals(2) {} + double val; + double minVal; + double maxVal; + double singleStep; + int decimals; + double minimumValue() const { return minVal; } + double maximumValue() const { return maxVal; } + void setMinimumValue(double newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(double newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtDoublePropertyManager + + \brief The QtDoublePropertyManager provides and manages double properties. + + A double property has a current value, and a range specifying the + valid values. The range is defined by a minimum and a maximum + value. + + The property's value and range can be retrieved using the value(), + minimum() and maximum() functions, and can be set using the + setValue(), setMinimum() and setMaximum() slots. + Alternatively, the range can be defined in one go using the + setRange() slot. + + In addition, QtDoublePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid values. + + \sa QtAbstractPropertyManager, QtDoubleSpinBoxFactory +*/ + +/*! + \fn void QtDoublePropertyManager::valueChanged(QtProperty *property, double value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtDoublePropertyManager::rangeChanged(QtProperty *property, double minimum, double maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid values, passing a pointer to the + \a property and the new \a minimum and \a maximum values + + \sa setRange() +*/ + +/*! + \fn void QtDoublePropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + \fn void QtDoublePropertyManager::singleStepChanged(QtProperty *property, double step) + + This signal is emitted whenever a property created by this manager + changes its single step property, passing a pointer to the + \a property and the new \a step value + + \sa setSingleStep() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDoublePropertyManager::QtDoublePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDoublePropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDoublePropertyManager::~QtDoublePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa setValue() +*/ +double QtDoublePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's minimum value. + + \sa maximum(), setRange() +*/ +double QtDoublePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's maximum value. + + \sa minimum(), setRange() +*/ +double QtDoublePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's step value. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa setSingleStep() +*/ +double QtDoublePropertyManager::singleStep(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::singleStep, property, 0); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtDoublePropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + \reimp +*/ +QString QtDoublePropertyManager::valueText(const QtProperty *property) const +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return QString::number(it.value().val, 'f', it.value().decimals); +} + +/*! + \fn void QtDoublePropertyManager::setValue(QtProperty *property, double value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given + \a property's range, the \a value is adjusted to the nearest valid value + within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtDoublePropertyManager::setValue(QtProperty *property, double val) +{ + void (QtDoublePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, double) = 0; + setValueInRange(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the step value for the given \a property to \a step. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa singleStep() +*/ +void QtDoublePropertyManager::setSingleStep(QtProperty *property, double step) +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtDoublePropertyManagerPrivate::Data data = it.value(); + + if (step < 0) + step = 0; + + if (data.singleStep == step) + return; + + data.singleStep = step; + + it.value() = data; + + emit singleStepChanged(property, data.singleStep); +} + +/*! + \fn void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtDoublePropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtDoublePropertyManager::setMinimum(QtProperty *property, double minVal) +{ + setMinimumValue(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting the maximum value, the minimum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtDoublePropertyManager::setMaximum(QtProperty *property, double maxVal) +{ + setMaximumValue(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtDoublePropertyManager::setRange(QtProperty *property, double minimum, double maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtDoublePropertyManager::setRange(QtProperty *property, double minVal, double maxVal) +{ + void (QtDoublePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, double, double, double) = 0; + setBorderValues(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + \reimp +*/ +void QtDoublePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtDoublePropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtDoublePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtStringPropertyManager + +class QtStringPropertyManagerPrivate +{ + QtStringPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtStringPropertyManager) +public: + + struct Data + { + Data() : regExp(QString(QLatin1Char('*')), Qt::CaseSensitive, QRegExp::Wildcard) + { + } + QString val; + QRegExp regExp; + }; + + typedef QMap PropertyValueMap; + QMap m_values; +}; + +/*! + \class QtStringPropertyManager + + \brief The QtStringPropertyManager provides and manages QString properties. + + A string property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The current value can be checked against a regular expression. To + set the regular expression use the setRegExp() slot, use the + regExp() function to retrieve the currently set expression. + + In addition, QtStringPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the regExpChanged() signal which is emitted whenever + such a property changes its currently set regular expression. + + \sa QtAbstractPropertyManager, QtLineEditFactory +*/ + +/*! + \fn void QtStringPropertyManager::valueChanged(QtProperty *property, const QString &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtStringPropertyManager::regExpChanged(QtProperty *property, const QRegExp ®Exp) + + This signal is emitted whenever a property created by this manager + changes its currenlty set regular expression, passing a pointer to + the \a property and the new \a regExp as parameters. + + \sa setRegExp() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtStringPropertyManager::QtStringPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtStringPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtStringPropertyManager::~QtStringPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns an empty string. + + \sa setValue() +*/ +QString QtStringPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's currently set regular expression. + + If the given \a property is not managed by this manager, this + function returns an empty expression. + + \sa setRegExp() +*/ +QRegExp QtStringPropertyManager::regExp(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtStringPropertyManagerPrivate::Data::regExp, property, QRegExp()); +} + +/*! + \reimp +*/ +QString QtStringPropertyManager::valueText(const QtProperty *property) const +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().val; +} + +/*! + \fn void QtStringPropertyManager::setValue(QtProperty *property, const QString &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value doesn't match the given \a property's + regular expression, this function does nothing. + + \sa value(), setRegExp(), valueChanged() +*/ +void QtStringPropertyManager::setValue(QtProperty *property, const QString &val) +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtStringPropertyManagerPrivate::Data data = it.value(); + + if (data.val == val) + return; + + if (data.regExp.isValid() && !data.regExp.exactMatch(val)) + return; + + data.val = val; + + it.value() = data; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the regular expression of the given \a property to \a regExp. + + \sa regExp(), setValue(), regExpChanged() +*/ +void QtStringPropertyManager::setRegExp(QtProperty *property, const QRegExp ®Exp) +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtStringPropertyManagerPrivate::Data data = it.value() ; + + if (data.regExp == regExp) + return; + + data.regExp = regExp; + + it.value() = data; + + emit regExpChanged(property, data.regExp); +} + +/*! + \reimp +*/ +void QtStringPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtStringPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtStringPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtBoolPropertyManager + +class QtBoolPropertyManagerPrivate +{ + QtBoolPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtBoolPropertyManager) +public: + + QMap m_values; +}; + +/*! + \class QtBoolPropertyManager + + \brief The QtBoolPropertyManager class provides and manages boolean properties. + + The property's value can be retrieved using the value() function, + and set using the setValue() slot. + + In addition, QtBoolPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtCheckBoxFactory +*/ + +/*! + \fn void QtBoolPropertyManager::valueChanged(QtProperty *property, bool value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtBoolPropertyManager::QtBoolPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtBoolPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtBoolPropertyManager::~QtBoolPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns false. + + \sa setValue() +*/ +bool QtBoolPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, false); +} + +/*! + \reimp +*/ +QString QtBoolPropertyManager::valueText(const QtProperty *property) const +{ + const QMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + static const QString trueText = tr("True"); + static const QString falseText = tr("False"); + return it.value() ? trueText : falseText; +} + +// Return an icon containing a check box indicator +static QIcon drawCheckBox(bool value) +{ + QStyleOptionButton opt; + opt.state |= value ? QStyle::State_On : QStyle::State_Off; + opt.state |= QStyle::State_Enabled; + const QStyle *style = QApplication::style(); + // Figure out size of an indicator and make sure it is not scaled down in a list view item + // by making the pixmap as big as a list view icon and centering the indicator in it. + // (if it is smaller, it can't be helped) + const int indicatorWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &opt); + const int indicatorHeight = style->pixelMetric(QStyle::PM_IndicatorHeight, &opt); + const int listViewIconSize = indicatorWidth; + const int pixmapWidth = indicatorWidth; + const int pixmapHeight = qMax(indicatorHeight, listViewIconSize); + + opt.rect = QRect(0, 0, indicatorWidth, indicatorHeight); + QPixmap pixmap = QPixmap(pixmapWidth, pixmapHeight); + pixmap.fill(Qt::transparent); + { + // Center? + const int xoff = (pixmapWidth > indicatorWidth) ? (pixmapWidth - indicatorWidth) / 2 : 0; + const int yoff = (pixmapHeight > indicatorHeight) ? (pixmapHeight - indicatorHeight) / 2 : 0; + QPainter painter(&pixmap); + painter.translate(xoff, yoff); + QCheckBox cb; + style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &painter, &cb); + } + return QIcon(pixmap); +} + +/*! + \reimp +*/ +QIcon QtBoolPropertyManager::valueIcon(const QtProperty *property) const +{ + const QMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + static const QIcon checkedIcon = drawCheckBox(true); + static const QIcon uncheckedIcon = drawCheckBox(false); + return it.value() ? checkedIcon : uncheckedIcon; +} + +/*! + \fn void QtBoolPropertyManager::setValue(QtProperty *property, bool value) + + Sets the value of the given \a property to \a value. + + \sa value() +*/ +void QtBoolPropertyManager::setValue(QtProperty *property, bool val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtBoolPropertyManager::propertyChanged, + &QtBoolPropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtBoolPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = false; +} + +/*! + \reimp +*/ +void QtBoolPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDatePropertyManager + +class QtDatePropertyManagerPrivate +{ + QtDatePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDatePropertyManager) +public: + + struct Data + { + Data() : val(QDate::currentDate()), minVal(QDate(1752, 9, 14)), + maxVal(QDate(7999, 12, 31)) {} + QDate val; + QDate minVal; + QDate maxVal; + QDate minimumValue() const { return minVal; } + QDate maximumValue() const { return maxVal; } + void setMinimumValue(const QDate &newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(const QDate &newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + QString m_format; + + typedef QMap PropertyValueMap; + QMap m_values; +}; + +/*! + \class QtDatePropertyManager + + \brief The QtDatePropertyManager provides and manages QDate properties. + + A date property has a current value, and a range specifying the + valid dates. The range is defined by a minimum and a maximum + value. + + The property's values can be retrieved using the minimum(), + maximum() and value() functions, and can be set using the + setMinimum(), setMaximum() and setValue() slots. Alternatively, + the range can be defined in one go using the setRange() slot. + + In addition, QtDatePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid dates. + + \sa QtAbstractPropertyManager, QtDateEditFactory, QtDateTimePropertyManager +*/ + +/*! + \fn void QtDatePropertyManager::valueChanged(QtProperty *property, const QDate &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtDatePropertyManager::rangeChanged(QtProperty *property, const QDate &minimum, const QDate &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid dates, passing a pointer to the \a + property and the new \a minimum and \a maximum dates. + + \sa setRange() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDatePropertyManager::QtDatePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDatePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDatePropertyManager::~QtDatePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns an invalid date. + + \sa setValue() +*/ +QDate QtDatePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's minimum date. + + \sa maximum(), setRange() +*/ +QDate QtDatePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum date. + + \sa minimum(), setRange() +*/ +QDate QtDatePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtDatePropertyManager::valueText(const QtProperty *property) const +{ + const QtDatePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().val.toString(d_ptr->m_format); +} + +/*! + \fn void QtDatePropertyManager::setValue(QtProperty *property, const QDate &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not a valid date according to the + given \a property's range, the value is adjusted to the nearest + valid value within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtDatePropertyManager::setValue(QtProperty *property, const QDate &val) +{ + void (QtDatePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, const QDate &) = 0; + setValueInRange(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa minimum(), setRange() +*/ +void QtDatePropertyManager::setMinimum(QtProperty *property, const QDate &minVal) +{ + setMinimumValue(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting the maximum value, the minimum and current + values are adjusted if necessary (ensuring that the range remains + valid and that the current value is within in the range). + + \sa maximum(), setRange() +*/ +void QtDatePropertyManager::setMaximum(QtProperty *property, const QDate &maxVal) +{ + setMaximumValue(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minimum, const QDate &maximum) + + Sets the range of valid dates. + + This is a convenience function defining the range of valid dates + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new date range, the current value is adjusted if + necessary (ensuring that the value remains in date range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal) +{ + void (QtDatePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, const QDate &, + const QDate &, const QDate &) = 0; + setBorderValues(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + \reimp +*/ +void QtDatePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtDatePropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtDatePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtTimePropertyManager + +class QtTimePropertyManagerPrivate +{ + QtTimePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtTimePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtTimePropertyManager + + \brief The QtTimePropertyManager provides and manages QTime properties. + + A time property's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtTimePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtTimeEditFactory +*/ + +/*! + \fn void QtTimePropertyManager::valueChanged(QtProperty *property, const QTime &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtTimePropertyManager::QtTimePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtTimePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.timeFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtTimePropertyManager::~QtTimePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns an invalid time object. + + \sa setValue() +*/ +QTime QtTimePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QTime()); +} + +/*! + \reimp +*/ +QString QtTimePropertyManager::valueText(const QtProperty *property) const +{ + const QtTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(d_ptr->m_format); +} + +/*! + \fn void QtTimePropertyManager::setValue(QtProperty *property, const QTime &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtTimePropertyManager::setValue(QtProperty *property, const QTime &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtTimePropertyManager::propertyChanged, + &QtTimePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtTimePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QTime::currentTime(); +} + +/*! + \reimp +*/ +void QtTimePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDateTimePropertyManager + +class QtDateTimePropertyManagerPrivate +{ + QtDateTimePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDateTimePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtDateTimePropertyManager + + \brief The QtDateTimePropertyManager provides and manages QDateTime properties. + + A date and time property has a current value which can be + retrieved using the value() function, and set using the setValue() + slot. In addition, QtDateTimePropertyManager provides the + valueChanged() signal which is emitted whenever a property created + by this manager changes. + + \sa QtAbstractPropertyManager, QtDateTimeEditFactory, QtDatePropertyManager +*/ + +/*! + \fn void QtDateTimePropertyManager::valueChanged(QtProperty *property, const QDateTime &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDateTimePropertyManager::QtDateTimePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDateTimePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); + d_ptr->m_format += QLatin1Char(' '); + d_ptr->m_format += loc.timeFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDateTimePropertyManager::~QtDateTimePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid QDateTime object. + + \sa setValue() +*/ +QDateTime QtDateTimePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QDateTime()); +} + +/*! + \reimp +*/ +QString QtDateTimePropertyManager::valueText(const QtProperty *property) const +{ + const QtDateTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(d_ptr->m_format); +} + +/*! + \fn void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtDateTimePropertyManager::propertyChanged, + &QtDateTimePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtDateTimePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QDateTime::currentDateTime(); +} + +/*! + \reimp +*/ +void QtDateTimePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtKeySequencePropertyManager + +class QtKeySequencePropertyManagerPrivate +{ + QtKeySequencePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtKeySequencePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtKeySequencePropertyManager + + \brief The QtKeySequencePropertyManager provides and manages QKeySequence properties. + + A key sequence's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtKeySequencePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtKeySequencePropertyManager::valueChanged(QtProperty *property, const QKeySequence &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtKeySequencePropertyManager::QtKeySequencePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtKeySequencePropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtKeySequencePropertyManager::~QtKeySequencePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an empty QKeySequence object. + + \sa setValue() +*/ +QKeySequence QtKeySequencePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QKeySequence()); +} + +/*! + \reimp +*/ +QString QtKeySequencePropertyManager::valueText(const QtProperty *property) const +{ + const QtKeySequencePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(QKeySequence::NativeText); +} + +/*! + \fn void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtKeySequencePropertyManager::propertyChanged, + &QtKeySequencePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtKeySequencePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QKeySequence(); +} + +/*! + \reimp +*/ +void QtKeySequencePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtCharPropertyManager + +class QtCharPropertyManagerPrivate +{ + QtCharPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtCharPropertyManager) +public: + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtCharPropertyManager + + \brief The QtCharPropertyManager provides and manages QChar properties. + + A char's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtCharPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtCharPropertyManager::valueChanged(QtProperty *property, const QChar &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtCharPropertyManager::QtCharPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtCharPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtCharPropertyManager::~QtCharPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an null QChar object. + + \sa setValue() +*/ +QChar QtCharPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QChar()); +} + +/*! + \reimp +*/ +QString QtCharPropertyManager::valueText(const QtProperty *property) const +{ + const QtCharPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QChar c = it.value(); + return c.isNull() ? QString() : QString(c); +} + +/*! + \fn void QtCharPropertyManager::setValue(QtProperty *property, const QChar &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtCharPropertyManager::setValue(QtProperty *property, const QChar &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtCharPropertyManager::propertyChanged, + &QtCharPropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtCharPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QChar(); +} + +/*! + \reimp +*/ +void QtCharPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtLocalePropertyManager + +class QtLocalePropertyManagerPrivate +{ + QtLocalePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtLocalePropertyManager) +public: + + QtLocalePropertyManagerPrivate(); + + void slotEnumChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToLanguage; + QMap m_propertyToCountry; + + QMap m_languageToProperty; + QMap m_countryToProperty; +}; + +QtLocalePropertyManagerPrivate::QtLocalePropertyManagerPrivate() +{ +} + +void QtLocalePropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_languageToProperty.value(property, 0)) { + const QLocale loc = m_values[prop]; + QLocale::Language newLanguage = loc.language(); + QLocale::Country newCountry = loc.country(); + metaEnumProvider()->indexToLocale(value, 0, &newLanguage, 0); + QLocale newLoc(newLanguage, newCountry); + q_ptr->setValue(prop, newLoc); + } else if (QtProperty *prop = m_countryToProperty.value(property, 0)) { + const QLocale loc = m_values[prop]; + QLocale::Language newLanguage = loc.language(); + QLocale::Country newCountry = loc.country(); + metaEnumProvider()->indexToLocale(m_enumPropertyManager->value(m_propertyToLanguage.value(prop)), value, &newLanguage, &newCountry); + QLocale newLoc(newLanguage, newCountry); + q_ptr->setValue(prop, newLoc); + } +} + +void QtLocalePropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *subProp = m_languageToProperty.value(property, 0)) { + m_propertyToLanguage[subProp] = 0; + m_languageToProperty.remove(property); + } else if (QtProperty *subProp = m_countryToProperty.value(property, 0)) { + m_propertyToCountry[subProp] = 0; + m_countryToProperty.remove(property); + } +} + +/*! + \class QtLocalePropertyManager + + \brief The QtLocalePropertyManager provides and manages QLocale properties. + + A locale property has nested \e language and \e country + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by QtEnumPropertyManager object. + These submanager can be retrieved using the subEnumPropertyManager() + function. In order to provide editing widgets for the subproperties + in a property browser widget, this manager must be associated with editor factory. + + In addition, QtLocalePropertyManager provides the valueChanged() + signal which is emitted whenever a property created by this + manager changes. + + \sa QtAbstractPropertyManager, QtEnumPropertyManager +*/ + +/*! + \fn void QtLocalePropertyManager::valueChanged(QtProperty *property, const QLocale &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtLocalePropertyManager::QtLocalePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtLocalePropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtLocalePropertyManager::~QtLocalePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e language + and \e country subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtLocalePropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns the default locale. + + \sa setValue() +*/ +QLocale QtLocalePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QLocale()); +} + +/*! + \reimp +*/ +QString QtLocalePropertyManager::valueText(const QtProperty *property) const +{ + const QtLocalePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + QLocale loc = it.value(); + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(loc.language(), loc.country(), &langIdx, &countryIdx); + QString str = tr("%1, %2") + .arg(metaEnumProvider()->languageEnumNames().at(langIdx)) + .arg(metaEnumProvider()->countryEnumNames(loc.language()).at(countryIdx)); + return str; +} + +/*! + \fn void QtLocalePropertyManager::setValue(QtProperty *property, const QLocale &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtLocalePropertyManager::setValue(QtProperty *property, const QLocale &val) +{ + const QtLocalePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + const QLocale loc = it.value(); + if (loc == val) + return; + + it.value() = val; + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(val.language(), val.country(), &langIdx, &countryIdx); + if (loc.language() != val.language()) { + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToLanguage.value(property), langIdx); + d_ptr->m_enumPropertyManager->setEnumNames(d_ptr->m_propertyToCountry.value(property), + metaEnumProvider()->countryEnumNames(val.language())); + } + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToCountry.value(property), countryIdx); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtLocalePropertyManager::initializeProperty(QtProperty *property) +{ + QLocale val; + d_ptr->m_values[property] = val; + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(val.language(), val.country(), &langIdx, &countryIdx); + + QtProperty *languageProp = d_ptr->m_enumPropertyManager->addProperty(); + languageProp->setPropertyName(tr("Language")); + d_ptr->m_enumPropertyManager->setEnumNames(languageProp, metaEnumProvider()->languageEnumNames()); + d_ptr->m_enumPropertyManager->setValue(languageProp, langIdx); + d_ptr->m_propertyToLanguage[property] = languageProp; + d_ptr->m_languageToProperty[languageProp] = property; + property->addSubProperty(languageProp); + + QtProperty *countryProp = d_ptr->m_enumPropertyManager->addProperty(); + countryProp->setPropertyName(tr("Country")); + d_ptr->m_enumPropertyManager->setEnumNames(countryProp, metaEnumProvider()->countryEnumNames(val.language())); + d_ptr->m_enumPropertyManager->setValue(countryProp, countryIdx); + d_ptr->m_propertyToCountry[property] = countryProp; + d_ptr->m_countryToProperty[countryProp] = property; + property->addSubProperty(countryProp); +} + +/*! + \reimp +*/ +void QtLocalePropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *languageProp = d_ptr->m_propertyToLanguage[property]; + if (languageProp) { + d_ptr->m_languageToProperty.remove(languageProp); + delete languageProp; + } + d_ptr->m_propertyToLanguage.remove(property); + + QtProperty *countryProp = d_ptr->m_propertyToCountry[property]; + if (countryProp) { + d_ptr->m_countryToProperty.remove(countryProp); + delete countryProp; + } + d_ptr->m_propertyToCountry.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtPointPropertyManager + +class QtPointPropertyManagerPrivate +{ + QtPointPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtPointPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + + QMap m_xToProperty; + QMap m_yToProperty; +}; + +void QtPointPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *xprop = m_xToProperty.value(property, 0)) { + QPoint p = m_values[xprop]; + p.setX(value); + q_ptr->setValue(xprop, p); + } else if (QtProperty *yprop = m_yToProperty.value(property, 0)) { + QPoint p = m_values[yprop]; + p.setY(value); + q_ptr->setValue(yprop, p); + } +} + +void QtPointPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } +} + +/*! \class QtPointPropertyManager + + \brief The QtPointPropertyManager provides and manages QPoint properties. + + A point property has nested \e x and \e y subproperties. The + top-level property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtPointPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtPointFPropertyManager +*/ + +/*! + \fn void QtPointPropertyManager::valueChanged(QtProperty *property, const QPoint &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtPointPropertyManager::QtPointPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtPointPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtPointPropertyManager::~QtPointPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x and \e y + subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtPointPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a point with coordinates (0, 0). + + \sa setValue() +*/ +QPoint QtPointPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QPoint()); +} + +/*! + \reimp +*/ +QString QtPointPropertyManager::valueText(const QtProperty *property) const +{ + const QtPointPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QPoint v = it.value(); + return QString(tr("(%1, %2)").arg(QString::number(v.x())) + .arg(QString::number(v.y()))); +} + +/*! + \fn void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &val) +{ + const QtPointPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtPointPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QPoint(0, 0); + + QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_intPropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_intPropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); +} + +/*! + \reimp +*/ +void QtPointPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtPointFPropertyManager + +class QtPointFPropertyManagerPrivate +{ + QtPointFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtPointFPropertyManager) +public: + + struct Data + { + Data() : decimals(2) {} + QPointF val; + int decimals; + }; + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + + QMap m_xToProperty; + QMap m_yToProperty; +}; + +void QtPointFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QPointF p = m_values[prop].val; + p.setX(value); + q_ptr->setValue(prop, p); + } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { + QPointF p = m_values[prop].val; + p.setY(value); + q_ptr->setValue(prop, p); + } +} + +void QtPointFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } +} + +/*! \class QtPointFPropertyManager + + \brief The QtPointFPropertyManager provides and manages QPointF properties. + + A point property has nested \e x and \e y subproperties. The + top-level property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtPointFPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtPointPropertyManager +*/ + +/*! + \fn void QtPointFPropertyManager::valueChanged(QtProperty *property, const QPointF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtPointFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtPointFPropertyManager::QtPointFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtPointFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtPointFPropertyManager::~QtPointFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x and \e y + subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtPointFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a point with coordinates (0, 0). + + \sa setValue() +*/ +QPointF QtPointFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtPointFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtPointFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + \reimp +*/ +QString QtPointFPropertyManager::valueText(const QtProperty *property) const +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QPointF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("(%1, %2)").arg(QString::number(v.x(), 'f', dec)) + .arg(QString::number(v.y(), 'f', dec))); +} + +/*! + \fn void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &val) +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value().val == val) + return; + + it.value().val = val; + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \fn void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtPointFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + \reimp +*/ +void QtPointFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtPointFPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); +} + +/*! + \reimp +*/ +void QtPointFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizePropertyManager + +class QtSizePropertyManagerPrivate +{ + QtSizePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizePropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + void setValue(QtProperty *property, const QSize &val); + void setRange(QtProperty *property, + const QSize &minVal, const QSize &maxVal, const QSize &val); + + struct Data + { + Data() : val(QSize(0, 0)), minVal(QSize(0, 0)), maxVal(QSize(INT_MAX, INT_MAX)) {} + QSize val; + QSize minVal; + QSize maxVal; + QSize minimumValue() const { return minVal; } + QSize maximumValue() const { return maxVal; } + void setMinimumValue(const QSize &newMinVal) { setSizeMinimumData(this, newMinVal); } + void setMaximumValue(const QSize &newMaxVal) { setSizeMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtSizePropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_wToProperty.value(property, 0)) { + QSize s = m_values[prop].val; + s.setWidth(value); + q_ptr->setValue(prop, s); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + QSize s = m_values[prop].val; + s.setHeight(value); + q_ptr->setValue(prop, s); + } +} + +void QtSizePropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtSizePropertyManagerPrivate::setValue(QtProperty *property, const QSize &val) +{ + m_intPropertyManager->setValue(m_propertyToW.value(property), val.width()); + m_intPropertyManager->setValue(m_propertyToH.value(property), val.height()); +} + +void QtSizePropertyManagerPrivate::setRange(QtProperty *property, + const QSize &minVal, const QSize &maxVal, const QSize &val) +{ + QtProperty *wProperty = m_propertyToW.value(property); + QtProperty *hProperty = m_propertyToH.value(property); + m_intPropertyManager->setRange(wProperty, minVal.width(), maxVal.width()); + m_intPropertyManager->setValue(wProperty, val.width()); + m_intPropertyManager->setRange(hProperty, minVal.height(), maxVal.height()); + m_intPropertyManager->setValue(hProperty, val.height()); +} + +/*! + \class QtSizePropertyManager + + \brief The QtSizePropertyManager provides and manages QSize properties. + + A size property has nested \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A size property also has a range of valid values defined by a + minimum size and a maximum size. These sizes can be retrieved + using the minimum() and the maximum() functions, and set using the + setMinimum() and setMaximum() slots. Alternatively, the range can + be defined in one go using the setRange() slot. + + In addition, QtSizePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid sizes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtSizeFPropertyManager +*/ + +/*! + \fn void QtSizePropertyManager::valueChanged(QtProperty *property, const QSize &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtSizePropertyManager::rangeChanged(QtProperty *property, const QSize &minimum, const QSize &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid sizes, passing a pointer to the \a + property and the new \a minimum and \a maximum sizes. + + \sa setRange() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizePropertyManager::QtSizePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizePropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizePropertyManager::~QtSizePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e width and \e height + subproperties. + + In order to provide editing widgets for the \e width and \e height + properties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtSizePropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid size + + \sa setValue() +*/ +QSize QtSizePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's minimum size value. + + \sa setMinimum(), maximum(), setRange() +*/ +QSize QtSizePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum size value. + + \sa setMaximum(), minimum(), setRange() +*/ +QSize QtSizePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtSizePropertyManager::valueText(const QtProperty *property) const +{ + const QtSizePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QSize v = it.value().val; + return QString(tr("%1 x %2").arg(QString::number(v.width())) + .arg(QString::number(v.height()))); +} + +/*! + \fn void QtSizePropertyManager::setValue(QtProperty *property, const QSize &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's size range, the \a value is adjusted to the nearest + valid value within the size range. + + \sa value(), setRange(), valueChanged() +*/ +void QtSizePropertyManager::setValue(QtProperty *property, const QSize &val) +{ + setValueInRange(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + property, val, &QtSizePropertyManagerPrivate::setValue); +} + +/*! + Sets the minimum size value for the given \a property to \a minVal. + + When setting the minimum size value, the maximum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtSizePropertyManager::setMinimum(QtProperty *property, const QSize &minVal) +{ + setBorderValue(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, + &QtSizePropertyManagerPrivate::Data::minimumValue, + &QtSizePropertyManagerPrivate::Data::setMinimumValue, + minVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + Sets the maximum size value for the given \a property to \a maxVal. + + When setting the maximum size value, the minimum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtSizePropertyManager::setMaximum(QtProperty *property, const QSize &maxVal) +{ + setBorderValue(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, + &QtSizePropertyManagerPrivate::Data::maximumValue, + &QtSizePropertyManagerPrivate::Data::setMaximumValue, + maxVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + \fn void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minimum, const QSize &maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within the range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal) +{ + setBorderValues(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, minVal, maxVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + \reimp +*/ +void QtSizePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtSizePropertyManagerPrivate::Data(); + + QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_intPropertyManager->setValue(wProp, 0); + d_ptr->m_intPropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_intPropertyManager->setValue(hProp, 0); + d_ptr->m_intPropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtSizePropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizeFPropertyManager + +class QtSizeFPropertyManagerPrivate +{ + QtSizeFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizeFPropertyManager) +public: + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + void setValue(QtProperty *property, const QSizeF &val); + void setRange(QtProperty *property, + const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val); + + struct Data + { + Data() : val(QSizeF(0, 0)), minVal(QSizeF(0, 0)), maxVal(QSizeF(INT_MAX, INT_MAX)), decimals(2) {} + QSizeF val; + QSizeF minVal; + QSizeF maxVal; + int decimals; + QSizeF minimumValue() const { return minVal; } + QSizeF maximumValue() const { return maxVal; } + void setMinimumValue(const QSizeF &newMinVal) { setSizeMinimumData(this, newMinVal); } + void setMaximumValue(const QSizeF &newMaxVal) { setSizeMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtSizeFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_wToProperty.value(property, 0)) { + QSizeF s = m_values[prop].val; + s.setWidth(value); + q_ptr->setValue(prop, s); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + QSizeF s = m_values[prop].val; + s.setHeight(value); + q_ptr->setValue(prop, s); + } +} + +void QtSizeFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtSizeFPropertyManagerPrivate::setValue(QtProperty *property, const QSizeF &val) +{ + m_doublePropertyManager->setValue(m_propertyToW.value(property), val.width()); + m_doublePropertyManager->setValue(m_propertyToH.value(property), val.height()); +} + +void QtSizeFPropertyManagerPrivate::setRange(QtProperty *property, + const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val) +{ + m_doublePropertyManager->setRange(m_propertyToW[property], minVal.width(), maxVal.width()); + m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); + m_doublePropertyManager->setRange(m_propertyToH[property], minVal.height(), maxVal.height()); + m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtSizeFPropertyManager + + \brief The QtSizeFPropertyManager provides and manages QSizeF properties. + + A size property has nested \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A size property also has a range of valid values defined by a + minimum size and a maximum size. These sizes can be retrieved + using the minimum() and the maximum() functions, and set using the + setMinimum() and setMaximum() slots. Alternatively, the range can + be defined in one go using the setRange() slot. + + In addition, QtSizeFPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid sizes. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtSizePropertyManager +*/ + +/*! + \fn void QtSizeFPropertyManager::valueChanged(QtProperty *property, const QSizeF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtSizeFPropertyManager::rangeChanged(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid sizes, passing a pointer to the \a + property and the new \a minimum and \a maximum sizes. + + \sa setRange() +*/ + +/*! + \fn void QtSizeFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizeFPropertyManager::QtSizeFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizeFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizeFPropertyManager::~QtSizeFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e width and \e height + subproperties. + + In order to provide editing widgets for the \e width and \e height + properties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtSizeFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid size + + \sa setValue() +*/ +QSizeF QtSizeFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtSizeFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtSizeFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + Returns the given \a property's minimum size value. + + \sa setMinimum(), maximum(), setRange() +*/ +QSizeF QtSizeFPropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum size value. + + \sa setMaximum(), minimum(), setRange() +*/ +QSizeF QtSizeFPropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtSizeFPropertyManager::valueText(const QtProperty *property) const +{ + const QtSizeFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QSizeF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("%1 x %2").arg(QString::number(v.width(), 'f', dec)) + .arg(QString::number(v.height(), 'f', dec))); +} + +/*! + \fn void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's size range, the \a value is adjusted to the nearest + valid value within the size range. + + \sa value(), setRange(), valueChanged() +*/ +void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &val) +{ + setValueInRange(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + property, val, &QtSizeFPropertyManagerPrivate::setValue); +} + +/*! + \fn void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtSizeFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtSizeFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + Sets the minimum size value for the given \a property to \a minVal. + + When setting the minimum size value, the maximum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtSizeFPropertyManager::setMinimum(QtProperty *property, const QSizeF &minVal) +{ + setBorderValue(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, + &QtSizeFPropertyManagerPrivate::Data::minimumValue, + &QtSizeFPropertyManagerPrivate::Data::setMinimumValue, + minVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + Sets the maximum size value for the given \a property to \a maxVal. + + When setting the maximum size value, the minimum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtSizeFPropertyManager::setMaximum(QtProperty *property, const QSizeF &maxVal) +{ + setBorderValue(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, + &QtSizeFPropertyManagerPrivate::Data::maximumValue, + &QtSizeFPropertyManagerPrivate::Data::setMaximumValue, + maxVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + \fn void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within the range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal) +{ + setBorderValues(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, minVal, maxVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + \reimp +*/ +void QtSizeFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtSizeFPropertyManagerPrivate::Data(); + + QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(wProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(hProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtSizeFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtRectPropertyManager + +class QtRectPropertyManagerPrivate +{ + QtRectPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtRectPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + void setConstraint(QtProperty *property, const QRect &constraint, const QRect &val); + + struct Data + { + Data() : val(0, 0, 0, 0) {} + QRect val; + QRect constraint; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_xToProperty; + QMap m_yToProperty; + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtRectPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QRect r = m_values[prop].val; + r.moveLeft(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_yToProperty.value(property)) { + QRect r = m_values[prop].val; + r.moveTop(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRect r = data.val; + r.setWidth(value); + if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { + r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); + } + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRect r = data.val; + r.setHeight(value); + if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { + r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); + } + q_ptr->setValue(prop, r); + } +} + +void QtRectPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtRectPropertyManagerPrivate::setConstraint(QtProperty *property, + const QRect &constraint, const QRect &val) +{ + const bool isNull = constraint.isNull(); + const int left = isNull ? INT_MIN : constraint.left(); + const int right = isNull ? INT_MAX : constraint.left() + constraint.width(); + const int top = isNull ? INT_MIN : constraint.top(); + const int bottom = isNull ? INT_MAX : constraint.top() + constraint.height(); + const int width = isNull ? INT_MAX : constraint.width(); + const int height = isNull ? INT_MAX : constraint.height(); + + m_intPropertyManager->setRange(m_propertyToX[property], left, right); + m_intPropertyManager->setRange(m_propertyToY[property], top, bottom); + m_intPropertyManager->setRange(m_propertyToW[property], 0, width); + m_intPropertyManager->setRange(m_propertyToH[property], 0, height); + + m_intPropertyManager->setValue(m_propertyToX[property], val.x()); + m_intPropertyManager->setValue(m_propertyToY[property], val.y()); + m_intPropertyManager->setValue(m_propertyToW[property], val.width()); + m_intPropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtRectPropertyManager + + \brief The QtRectPropertyManager provides and manages QRect properties. + + A rectangle property has nested \e x, \e y, \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A rectangle property also has a constraint rectangle which can be + retrieved using the constraint() function, and set using the + setConstraint() slot. + + In addition, QtRectPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the constraintChanged() signal which is emitted + whenever such a property changes its constraint rectangle. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtRectFPropertyManager +*/ + +/*! + \fn void QtRectPropertyManager::valueChanged(QtProperty *property, const QRect &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtRectPropertyManager::constraintChanged(QtProperty *property, const QRect &constraint) + + This signal is emitted whenever property changes its constraint + rectangle, passing a pointer to the \a property and the new \a + constraint rectangle as parameters. + + \sa setConstraint() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtRectPropertyManager::QtRectPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtRectPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtRectPropertyManager::~QtRectPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x, \e y, \e width + and \e height subproperties. + + In order to provide editing widgets for the mentioned + subproperties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtRectPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid rectangle. + + \sa setValue(), constraint() +*/ +QRect QtRectPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's constraining rectangle. If returned value is null QRect it means there is no constraint applied. + + \sa value(), setConstraint() +*/ +QRect QtRectPropertyManager::constraint(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectPropertyManagerPrivate::Data::constraint, property, QRect()); +} + +/*! + \reimp +*/ +QString QtRectPropertyManager::valueText(const QtProperty *property) const +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QRect v = it.value().val; + return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x())) + .arg(QString::number(v.y())) + .arg(QString::number(v.width())) + .arg(QString::number(v.height()))); +} + +/*! + \fn void QtRectPropertyManager::setValue(QtProperty *property, const QRect &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + If the specified \a value is not inside the given \a property's + constraining rectangle, the value is adjusted accordingly to fit + within the constraint. + + \sa value(), setConstraint(), valueChanged() +*/ +void QtRectPropertyManager::setValue(QtProperty *property, const QRect &val) +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectPropertyManagerPrivate::Data data = it.value(); + + QRect newRect = val.normalized(); + if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { + const QRect r1 = data.constraint; + const QRect r2 = newRect; + newRect.setLeft(qMax(r1.left(), r2.left())); + newRect.setRight(qMin(r1.right(), r2.right())); + newRect.setTop(qMax(r1.top(), r2.top())); + newRect.setBottom(qMin(r1.bottom(), r2.bottom())); + if (newRect.width() < 0 || newRect.height() < 0) + return; + } + + if (data.val == newRect) + return; + + data.val = newRect; + + it.value() = data; + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's constraining rectangle to \a + constraint. + + When setting the constraint, the current value is adjusted if + necessary (ensuring that the current rectangle value is inside the + constraint). In order to reset the constraint pass a null QRect value. + + \sa setValue(), constraint(), constraintChanged() +*/ +void QtRectPropertyManager::setConstraint(QtProperty *property, const QRect &constraint) +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectPropertyManagerPrivate::Data data = it.value(); + + QRect newConstraint = constraint.normalized(); + if (data.constraint == newConstraint) + return; + + const QRect oldVal = data.val; + + data.constraint = newConstraint; + + if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { + QRect r1 = data.constraint; + QRect r2 = data.val; + + if (r2.width() > r1.width()) + r2.setWidth(r1.width()); + if (r2.height() > r1.height()) + r2.setHeight(r1.height()); + if (r2.left() < r1.left()) + r2.moveLeft(r1.left()); + else if (r2.right() > r1.right()) + r2.moveRight(r1.right()); + if (r2.top() < r1.top()) + r2.moveTop(r1.top()); + else if (r2.bottom() > r1.bottom()) + r2.moveBottom(r1.bottom()); + + data.val = r2; + } + + it.value() = data; + + emit constraintChanged(property, data.constraint); + + d_ptr->setConstraint(property, data.constraint, data.val); + + if (data.val == oldVal) + return; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \reimp +*/ +void QtRectPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtRectPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_intPropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_intPropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); + + QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_intPropertyManager->setValue(wProp, 0); + d_ptr->m_intPropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_intPropertyManager->setValue(hProp, 0); + d_ptr->m_intPropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtRectPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtRectFPropertyManager + +class QtRectFPropertyManagerPrivate +{ + QtRectFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtRectFPropertyManager) +public: + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + void setConstraint(QtProperty *property, const QRectF &constraint, const QRectF &val); + + struct Data + { + Data() : val(0, 0, 0, 0), decimals(2) {} + QRectF val; + QRectF constraint; + int decimals; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_xToProperty; + QMap m_yToProperty; + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtRectFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QRectF r = m_values[prop].val; + r.moveLeft(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { + QRectF r = m_values[prop].val; + r.moveTop(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRectF r = data.val; + r.setWidth(value); + if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { + r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); + } + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRectF r = data.val; + r.setHeight(value); + if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { + r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); + } + q_ptr->setValue(prop, r); + } +} + +void QtRectFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtRectFPropertyManagerPrivate::setConstraint(QtProperty *property, + const QRectF &constraint, const QRectF &val) +{ + const bool isNull = constraint.isNull(); + const float left = isNull ? FLT_MIN : constraint.left(); + const float right = isNull ? FLT_MAX : constraint.left() + constraint.width(); + const float top = isNull ? FLT_MIN : constraint.top(); + const float bottom = isNull ? FLT_MAX : constraint.top() + constraint.height(); + const float width = isNull ? FLT_MAX : constraint.width(); + const float height = isNull ? FLT_MAX : constraint.height(); + + m_doublePropertyManager->setRange(m_propertyToX[property], left, right); + m_doublePropertyManager->setRange(m_propertyToY[property], top, bottom); + m_doublePropertyManager->setRange(m_propertyToW[property], 0, width); + m_doublePropertyManager->setRange(m_propertyToH[property], 0, height); + + m_doublePropertyManager->setValue(m_propertyToX[property], val.x()); + m_doublePropertyManager->setValue(m_propertyToY[property], val.y()); + m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); + m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtRectFPropertyManager + + \brief The QtRectFPropertyManager provides and manages QRectF properties. + + A rectangle property has nested \e x, \e y, \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A rectangle property also has a constraint rectangle which can be + retrieved using the constraint() function, and set using the + setConstraint() slot. + + In addition, QtRectFPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the constraintChanged() signal which is emitted + whenever such a property changes its constraint rectangle. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtRectPropertyManager +*/ + +/*! + \fn void QtRectFPropertyManager::valueChanged(QtProperty *property, const QRectF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtRectFPropertyManager::constraintChanged(QtProperty *property, const QRectF &constraint) + + This signal is emitted whenever property changes its constraint + rectangle, passing a pointer to the \a property and the new \a + constraint rectangle as parameters. + + \sa setConstraint() +*/ + +/*! + \fn void QtRectFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtRectFPropertyManager::QtRectFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtRectFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtRectFPropertyManager::~QtRectFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x, \e y, \e width + and \e height subproperties. + + In order to provide editing widgets for the mentioned + subproperties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtRectFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid rectangle. + + \sa setValue(), constraint() +*/ +QRectF QtRectFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtRectFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + Returns the given \a property's constraining rectangle. If returned value is null QRectF it means there is no constraint applied. + + \sa value(), setConstraint() +*/ +QRectF QtRectFPropertyManager::constraint(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::constraint, property, QRect()); +} + +/*! + \reimp +*/ +QString QtRectFPropertyManager::valueText(const QtProperty *property) const +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QRectF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x(), 'f', dec)) + .arg(QString::number(v.y(), 'f', dec)) + .arg(QString::number(v.width(), 'f', dec)) + .arg(QString::number(v.height(), 'f', dec))); +} + +/*! + \fn void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + If the specified \a value is not inside the given \a property's + constraining rectangle, the value is adjusted accordingly to fit + within the constraint. + + \sa value(), setConstraint(), valueChanged() +*/ +void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &val) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + QRectF newRect = val.normalized(); + if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { + const QRectF r1 = data.constraint; + const QRectF r2 = newRect; + newRect.setLeft(qMax(r1.left(), r2.left())); + newRect.setRight(qMin(r1.right(), r2.right())); + newRect.setTop(qMax(r1.top(), r2.top())); + newRect.setBottom(qMin(r1.bottom(), r2.bottom())); + if (newRect.width() < 0 || newRect.height() < 0) + return; + } + + if (data.val == newRect) + return; + + data.val = newRect; + + it.value() = data; + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's constraining rectangle to \a + constraint. + + When setting the constraint, the current value is adjusted if + necessary (ensuring that the current rectangle value is inside the + constraint). In order to reset the constraint pass a null QRectF value. + + \sa setValue(), constraint(), constraintChanged() +*/ +void QtRectFPropertyManager::setConstraint(QtProperty *property, const QRectF &constraint) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + QRectF newConstraint = constraint.normalized(); + if (data.constraint == newConstraint) + return; + + const QRectF oldVal = data.val; + + data.constraint = newConstraint; + + if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { + QRectF r1 = data.constraint; + QRectF r2 = data.val; + + if (r2.width() > r1.width()) + r2.setWidth(r1.width()); + if (r2.height() > r1.height()) + r2.setHeight(r1.height()); + if (r2.left() < r1.left()) + r2.moveLeft(r1.left()); + else if (r2.right() > r1.right()) + r2.moveRight(r1.right()); + if (r2.top() < r1.top()) + r2.moveTop(r1.top()); + else if (r2.bottom() > r1.bottom()) + r2.moveBottom(r1.bottom()); + + data.val = r2; + } + + it.value() = data; + + emit constraintChanged(property, data.constraint); + + d_ptr->setConstraint(property, data.constraint, data.val); + + if (data.val == oldVal) + return; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \fn void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + \reimp +*/ +void QtRectFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtRectFPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); + + QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(wProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(hProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtRectFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtEnumPropertyManager + +class QtEnumPropertyManagerPrivate +{ + QtEnumPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtEnumPropertyManager) +public: + + struct Data + { + Data() : val(-1) {} + int val; + QStringList enumNames; + QMap enumIcons; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtEnumPropertyManager + + \brief The QtEnumPropertyManager provides and manages enum properties. + + Each enum property has an associated list of enum names which can + be retrieved using the enumNames() function, and set using the + corresponding setEnumNames() function. An enum property's value is + represented by an index in this list, and can be retrieved and set + using the value() and setValue() slots respectively. + + Each enum value can also have an associated icon. The mapping from + values to icons can be set using the setEnumIcons() function and + queried with the enumIcons() function. + + In addition, QtEnumPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. The enumNamesChanged() or enumIconsChanged() signal is emitted + whenever the list of enum names or icons is altered. + + \sa QtAbstractPropertyManager, QtEnumEditorFactory +*/ + +/*! + \fn void QtEnumPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtEnumPropertyManager::enumNamesChanged(QtProperty *property, const QStringList &names) + + This signal is emitted whenever a property created by this manager + changes its enum names, passing a pointer to the \a property and + the new \a names as parameters. + + \sa setEnumNames() +*/ + +/*! + \fn void QtEnumPropertyManager::enumIconsChanged(QtProperty *property, const QMap &icons) + + This signal is emitted whenever a property created by this manager + changes its enum icons, passing a pointer to the \a property and + the new mapping of values to \a icons as parameters. + + \sa setEnumIcons() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtEnumPropertyManager::QtEnumPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtEnumPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtEnumPropertyManager::~QtEnumPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value which is an index in the + list returned by enumNames() + + If the given property is not managed by this manager, this + function returns -1. + + \sa enumNames(), setValue() +*/ +int QtEnumPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, -1); +} + +/*! + Returns the given \a property's list of enum names. + + \sa value(), setEnumNames() +*/ +QStringList QtEnumPropertyManager::enumNames(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumNames, property, QStringList()); +} + +/*! + Returns the given \a property's map of enum values to their icons. + + \sa value(), setEnumIcons() +*/ +QMap QtEnumPropertyManager::enumIcons(const QtProperty *property) const +{ + return getData >(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumIcons, property, QMap()); +} + +/*! + \reimp +*/ +QString QtEnumPropertyManager::valueText(const QtProperty *property) const +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QtEnumPropertyManagerPrivate::Data &data = it.value(); + + const int v = data.val; + if (v >= 0 && v < data.enumNames.count()) + return data.enumNames.at(v); + return QString(); +} + +/*! + \reimp +*/ +QIcon QtEnumPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + const QtEnumPropertyManagerPrivate::Data &data = it.value(); + + const int v = data.val; + return data.enumIcons.value(v); +} + +/*! + \fn void QtEnumPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. + + The specified \a value must be less than the size of the given \a + property's enumNames() list, and larger than (or equal to) 0. + + \sa value(), valueChanged() +*/ +void QtEnumPropertyManager::setValue(QtProperty *property, int val) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtEnumPropertyManagerPrivate::Data data = it.value(); + + if (val >= data.enumNames.count()) + return; + + if (val < 0 && data.enumNames.count() > 0) + return; + + if (val < 0) + val = -1; + + if (data.val == val) + return; + + data.val = val; + + it.value() = data; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's list of enum names to \a + enumNames. The \a property's current value is reset to 0 + indicating the first item of the list. + + If the specified \a enumNames list is empty, the \a property's + current value is set to -1. + + \sa enumNames(), enumNamesChanged() +*/ +void QtEnumPropertyManager::setEnumNames(QtProperty *property, const QStringList &enumNames) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtEnumPropertyManagerPrivate::Data data = it.value(); + + if (data.enumNames == enumNames) + return; + + data.enumNames = enumNames; + + data.val = -1; + + if (enumNames.count() > 0) + data.val = 0; + + it.value() = data; + + emit enumNamesChanged(property, data.enumNames); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's map of enum values to their icons to \a + enumIcons. + + Each enum value can have associated icon. This association is represented with passed \a enumIcons map. + + \sa enumNames(), enumNamesChanged() +*/ +void QtEnumPropertyManager::setEnumIcons(QtProperty *property, const QMap &enumIcons) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + it.value().enumIcons = enumIcons; + + emit enumIconsChanged(property, it.value().enumIcons); + + emit propertyChanged(property); +} + +/*! + \reimp +*/ +void QtEnumPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtEnumPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtEnumPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtFlagPropertyManager + +class QtFlagPropertyManagerPrivate +{ + QtFlagPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtFlagPropertyManager) +public: + + void slotBoolChanged(QtProperty *property, bool value); + void slotPropertyDestroyed(QtProperty *property); + + struct Data + { + Data() : val(-1) {} + int val; + QStringList flagNames; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtBoolPropertyManager *m_boolPropertyManager; + + QMap > m_propertyToFlags; + + QMap m_flagToProperty; +}; + +void QtFlagPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) +{ + QtProperty *prop = m_flagToProperty.value(property, 0); + if (prop == 0) + return; + + QListIterator itProp(m_propertyToFlags[prop]); + int level = 0; + while (itProp.hasNext()) { + QtProperty *p = itProp.next(); + if (p == property) { + int v = m_values[prop].val; + if (value) { + v |= (1 << level); + } else { + v &= ~(1 << level); + } + q_ptr->setValue(prop, v); + return; + } + level++; + } +} + +void QtFlagPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + QtProperty *flagProperty = m_flagToProperty.value(property, 0); + if (flagProperty == 0) + return; + + m_propertyToFlags[flagProperty].replace(m_propertyToFlags[flagProperty].indexOf(property), 0); + m_flagToProperty.remove(property); +} + +/*! + \class QtFlagPropertyManager + + \brief The QtFlagPropertyManager provides and manages flag properties. + + Each flag property has an associated list of flag names which can + be retrieved using the flagNames() function, and set using the + corresponding setFlagNames() function. + + The flag manager provides properties with nested boolean + subproperties representing each flag, i.e. a flag property's value + is the binary combination of the subproperties' values. A + property's value can be retrieved and set using the value() and + setValue() slots respectively. The combination of flags is represented + by single int value - that's why it's possible to store up to + 32 independent flags in one flag property. + + The subproperties are created by a QtBoolPropertyManager object. This + manager can be retrieved using the subBoolPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtFlagPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the flagNamesChanged() signal which is emitted + whenever the list of flag names is altered. + + \sa QtAbstractPropertyManager, QtBoolPropertyManager +*/ + +/*! + \fn void QtFlagPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtFlagPropertyManager::flagNamesChanged(QtProperty *property, const QStringList &names) + + This signal is emitted whenever a property created by this manager + changes its flag names, passing a pointer to the \a property and the + new \a names as parameters. + + \sa setFlagNames() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtFlagPropertyManager::QtFlagPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtFlagPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); + connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotBoolChanged(QtProperty *, bool))); + connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtFlagPropertyManager::~QtFlagPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that produces the nested boolean subproperties + representing each flag. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtBoolPropertyManager *QtFlagPropertyManager::subBoolPropertyManager() const +{ + return d_ptr->m_boolPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa flagNames(), setValue() +*/ +int QtFlagPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's list of flag names. + + \sa value(), setFlagNames() +*/ +QStringList QtFlagPropertyManager::flagNames(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtFlagPropertyManagerPrivate::Data::flagNames, property, QStringList()); +} + +/*! + \reimp +*/ +QString QtFlagPropertyManager::valueText(const QtProperty *property) const +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QtFlagPropertyManagerPrivate::Data &data = it.value(); + + QString str; + int level = 0; + const QChar bar = QLatin1Char('|'); + const QStringList::const_iterator fncend = data.flagNames.constEnd(); + for (QStringList::const_iterator it = data.flagNames.constBegin(); it != fncend; ++it) { + if (data.val & (1 << level)) { + if (!str.isEmpty()) + str += bar; + str += *it; + } + + level++; + } + return str; +} + +/*! + \fn void QtFlagPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + The specified \a value must be less than the binary combination of + the property's flagNames() list size (i.e. less than 2\sup n, + where \c n is the size of the list) and larger than (or equal to) + 0. + + \sa value(), valueChanged() +*/ +void QtFlagPropertyManager::setValue(QtProperty *property, int val) +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtFlagPropertyManagerPrivate::Data data = it.value(); + + if (data.val == val) + return; + + if (val > (1 << data.flagNames.count()) - 1) + return; + + if (val < 0) + return; + + data.val = val; + + it.value() = data; + + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + int level = 0; + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) + d_ptr->m_boolPropertyManager->setValue(prop, val & (1 << level)); + level++; + } + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's list of flag names to \a flagNames. The + property's current value is reset to 0 indicating the first item + of the list. + + \sa flagNames(), flagNamesChanged() +*/ +void QtFlagPropertyManager::setFlagNames(QtProperty *property, const QStringList &flagNames) +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtFlagPropertyManagerPrivate::Data data = it.value(); + + if (data.flagNames == flagNames) + return; + + data.flagNames = flagNames; + data.val = 0; + + it.value() = data; + + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) { + delete prop; + d_ptr->m_flagToProperty.remove(prop); + } + } + d_ptr->m_propertyToFlags[property].clear(); + + QStringListIterator itFlag(flagNames); + while (itFlag.hasNext()) { + const QString flagName = itFlag.next(); + QtProperty *prop = d_ptr->m_boolPropertyManager->addProperty(); + prop->setPropertyName(flagName); + property->addSubProperty(prop); + d_ptr->m_propertyToFlags[property].append(prop); + d_ptr->m_flagToProperty[prop] = property; + } + + emit flagNamesChanged(property, data.flagNames); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \reimp +*/ +void QtFlagPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtFlagPropertyManagerPrivate::Data(); + + d_ptr->m_propertyToFlags[property] = QList(); +} + +/*! + \reimp +*/ +void QtFlagPropertyManager::uninitializeProperty(QtProperty *property) +{ + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) { + delete prop; + d_ptr->m_flagToProperty.remove(prop); + } + } + d_ptr->m_propertyToFlags.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizePolicyPropertyManager + +class QtSizePolicyPropertyManagerPrivate +{ + QtSizePolicyPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizePolicyPropertyManager) +public: + + QtSizePolicyPropertyManagerPrivate(); + + void slotIntChanged(QtProperty *property, int value); + void slotEnumChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToHPolicy; + QMap m_propertyToVPolicy; + QMap m_propertyToHStretch; + QMap m_propertyToVStretch; + + QMap m_hPolicyToProperty; + QMap m_vPolicyToProperty; + QMap m_hStretchToProperty; + QMap m_vStretchToProperty; +}; + +QtSizePolicyPropertyManagerPrivate::QtSizePolicyPropertyManagerPrivate() +{ +} + +void QtSizePolicyPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_hStretchToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setHorizontalStretch(value); + q_ptr->setValue(prop, sp); + } else if (QtProperty *prop = m_vStretchToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setVerticalStretch(value); + q_ptr->setValue(prop, sp); + } +} + +void QtSizePolicyPropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_hPolicyToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setHorizontalPolicy(metaEnumProvider()->indexToSizePolicy(value)); + q_ptr->setValue(prop, sp); + } else if (QtProperty *prop = m_vPolicyToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setVerticalPolicy(metaEnumProvider()->indexToSizePolicy(value)); + q_ptr->setValue(prop, sp); + } +} + +void QtSizePolicyPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_hStretchToProperty.value(property, 0)) { + m_propertyToHStretch[pointProp] = 0; + m_hStretchToProperty.remove(property); + } else if (QtProperty *pointProp = m_vStretchToProperty.value(property, 0)) { + m_propertyToVStretch[pointProp] = 0; + m_vStretchToProperty.remove(property); + } else if (QtProperty *pointProp = m_hPolicyToProperty.value(property, 0)) { + m_propertyToHPolicy[pointProp] = 0; + m_hPolicyToProperty.remove(property); + } else if (QtProperty *pointProp = m_vPolicyToProperty.value(property, 0)) { + m_propertyToVPolicy[pointProp] = 0; + m_vPolicyToProperty.remove(property); + } +} + +/*! + \class QtSizePolicyPropertyManager + + \brief The QtSizePolicyPropertyManager provides and manages QSizePolicy properties. + + A size policy property has nested \e horizontalPolicy, \e + verticalPolicy, \e horizontalStretch and \e verticalStretch + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by QtIntPropertyManager and QtEnumPropertyManager + objects. These managers can be retrieved using the subIntPropertyManager() + and subEnumPropertyManager() functions respectively. In order to provide + editing widgets for the subproperties in a property browser widget, + these managers must be associated with editor factories. + + In addition, QtSizePolicyPropertyManager provides the valueChanged() + signal which is emitted whenever a property created by this + manager changes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtEnumPropertyManager +*/ + +/*! + \fn void QtSizePolicyPropertyManager::valueChanged(QtProperty *property, const QSizePolicy &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizePolicyPropertyManager::QtSizePolicyPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizePolicyPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizePolicyPropertyManager::~QtSizePolicyPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e horizontalStretch + and \e verticalStretch subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtSizePolicyPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the manager that creates the nested \e horizontalPolicy + and \e verticalPolicy subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtSizePolicyPropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns the default size policy. + + \sa setValue() +*/ +QSizePolicy QtSizePolicyPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QSizePolicy()); +} + +/*! + \reimp +*/ +QString QtSizePolicyPropertyManager::valueText(const QtProperty *property) const +{ + const QtSizePolicyPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QSizePolicy sp = it.value(); + const QtMetaEnumProvider *mep = metaEnumProvider(); + const int hIndex = mep->sizePolicyToIndex(sp.horizontalPolicy()); + const int vIndex = mep->sizePolicyToIndex(sp.verticalPolicy()); + //! Unknown size policy on reading invalid uic3 files + const QString hPolicy = hIndex != -1 ? mep->policyEnumNames().at(hIndex) : tr(""); + const QString vPolicy = vIndex != -1 ? mep->policyEnumNames().at(vIndex) : tr(""); + const QString str = tr("[%1, %2, %3, %4]").arg(hPolicy, vPolicy).arg(sp.horizontalStretch()).arg(sp.verticalStretch()); + return str; +} + +/*! + \fn void QtSizePolicyPropertyManager::setValue(QtProperty *property, const QSizePolicy &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtSizePolicyPropertyManager::setValue(QtProperty *property, const QSizePolicy &val) +{ + const QtSizePolicyPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToHPolicy[property], + metaEnumProvider()->sizePolicyToIndex(val.horizontalPolicy())); + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToVPolicy[property], + metaEnumProvider()->sizePolicyToIndex(val.verticalPolicy())); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToHStretch[property], + val.horizontalStretch()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToVStretch[property], + val.verticalStretch()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtSizePolicyPropertyManager::initializeProperty(QtProperty *property) +{ + QSizePolicy val; + d_ptr->m_values[property] = val; + + QtProperty *hPolicyProp = d_ptr->m_enumPropertyManager->addProperty(); + hPolicyProp->setPropertyName(tr("Horizontal Policy")); + d_ptr->m_enumPropertyManager->setEnumNames(hPolicyProp, metaEnumProvider()->policyEnumNames()); + d_ptr->m_enumPropertyManager->setValue(hPolicyProp, + metaEnumProvider()->sizePolicyToIndex(val.horizontalPolicy())); + d_ptr->m_propertyToHPolicy[property] = hPolicyProp; + d_ptr->m_hPolicyToProperty[hPolicyProp] = property; + property->addSubProperty(hPolicyProp); + + QtProperty *vPolicyProp = d_ptr->m_enumPropertyManager->addProperty(); + vPolicyProp->setPropertyName(tr("Vertical Policy")); + d_ptr->m_enumPropertyManager->setEnumNames(vPolicyProp, metaEnumProvider()->policyEnumNames()); + d_ptr->m_enumPropertyManager->setValue(vPolicyProp, + metaEnumProvider()->sizePolicyToIndex(val.verticalPolicy())); + d_ptr->m_propertyToVPolicy[property] = vPolicyProp; + d_ptr->m_vPolicyToProperty[vPolicyProp] = property; + property->addSubProperty(vPolicyProp); + + QtProperty *hStretchProp = d_ptr->m_intPropertyManager->addProperty(); + hStretchProp->setPropertyName(tr("Horizontal Stretch")); + d_ptr->m_intPropertyManager->setValue(hStretchProp, val.horizontalStretch()); + d_ptr->m_intPropertyManager->setRange(hStretchProp, 0, 0xff); + d_ptr->m_propertyToHStretch[property] = hStretchProp; + d_ptr->m_hStretchToProperty[hStretchProp] = property; + property->addSubProperty(hStretchProp); + + QtProperty *vStretchProp = d_ptr->m_intPropertyManager->addProperty(); + vStretchProp->setPropertyName(tr("Vertical Stretch")); + d_ptr->m_intPropertyManager->setValue(vStretchProp, val.verticalStretch()); + d_ptr->m_intPropertyManager->setRange(vStretchProp, 0, 0xff); + d_ptr->m_propertyToVStretch[property] = vStretchProp; + d_ptr->m_vStretchToProperty[vStretchProp] = property; + property->addSubProperty(vStretchProp); + +} + +/*! + \reimp +*/ +void QtSizePolicyPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *hPolicyProp = d_ptr->m_propertyToHPolicy[property]; + if (hPolicyProp) { + d_ptr->m_hPolicyToProperty.remove(hPolicyProp); + delete hPolicyProp; + } + d_ptr->m_propertyToHPolicy.remove(property); + + QtProperty *vPolicyProp = d_ptr->m_propertyToVPolicy[property]; + if (vPolicyProp) { + d_ptr->m_vPolicyToProperty.remove(vPolicyProp); + delete vPolicyProp; + } + d_ptr->m_propertyToVPolicy.remove(property); + + QtProperty *hStretchProp = d_ptr->m_propertyToHStretch[property]; + if (hStretchProp) { + d_ptr->m_hStretchToProperty.remove(hStretchProp); + delete hStretchProp; + } + d_ptr->m_propertyToHStretch.remove(property); + + QtProperty *vStretchProp = d_ptr->m_propertyToVStretch[property]; + if (vStretchProp) { + d_ptr->m_vStretchToProperty.remove(vStretchProp); + delete vStretchProp; + } + d_ptr->m_propertyToVStretch.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtFontPropertyManager: +// QtFontPropertyManagerPrivate has a mechanism for reacting +// to QApplication::fontDatabaseChanged() [4.5], which is emitted +// when someone loads an application font. The signals are compressed +// using a timer with interval 0, which then causes the family +// enumeration manager to re-set its strings and index values +// for each property. + +Q_GLOBAL_STATIC(QFontDatabase, fontDatabase) + +class QtFontPropertyManagerPrivate +{ + QtFontPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtFontPropertyManager) +public: + + QtFontPropertyManagerPrivate(); + + void slotIntChanged(QtProperty *property, int value); + void slotEnumChanged(QtProperty *property, int value); + void slotBoolChanged(QtProperty *property, bool value); + void slotPropertyDestroyed(QtProperty *property); + void slotFontDatabaseChanged(); + void slotFontDatabaseDelayedChange(); + + QStringList m_familyNames; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + QtEnumPropertyManager *m_enumPropertyManager; + QtBoolPropertyManager *m_boolPropertyManager; + + QMap m_propertyToFamily; + QMap m_propertyToPointSize; + QMap m_propertyToBold; + QMap m_propertyToItalic; + QMap m_propertyToUnderline; + QMap m_propertyToStrikeOut; + QMap m_propertyToKerning; + + QMap m_familyToProperty; + QMap m_pointSizeToProperty; + QMap m_boldToProperty; + QMap m_italicToProperty; + QMap m_underlineToProperty; + QMap m_strikeOutToProperty; + QMap m_kerningToProperty; + + bool m_settingValue; + QTimer *m_fontDatabaseChangeTimer; +}; + +QtFontPropertyManagerPrivate::QtFontPropertyManagerPrivate() : + m_settingValue(false), + m_fontDatabaseChangeTimer(0) +{ +} + +void QtFontPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_pointSizeToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setPointSize(value); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_familyToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setFamily(m_familyNames.at(value)); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_boldToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setBold(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_italicToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setItalic(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_underlineToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setUnderline(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_strikeOutToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setStrikeOut(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_kerningToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setKerning(value); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_pointSizeToProperty.value(property, 0)) { + m_propertyToPointSize[pointProp] = 0; + m_pointSizeToProperty.remove(property); + } else if (QtProperty *pointProp = m_familyToProperty.value(property, 0)) { + m_propertyToFamily[pointProp] = 0; + m_familyToProperty.remove(property); + } else if (QtProperty *pointProp = m_boldToProperty.value(property, 0)) { + m_propertyToBold[pointProp] = 0; + m_boldToProperty.remove(property); + } else if (QtProperty *pointProp = m_italicToProperty.value(property, 0)) { + m_propertyToItalic[pointProp] = 0; + m_italicToProperty.remove(property); + } else if (QtProperty *pointProp = m_underlineToProperty.value(property, 0)) { + m_propertyToUnderline[pointProp] = 0; + m_underlineToProperty.remove(property); + } else if (QtProperty *pointProp = m_strikeOutToProperty.value(property, 0)) { + m_propertyToStrikeOut[pointProp] = 0; + m_strikeOutToProperty.remove(property); + } else if (QtProperty *pointProp = m_kerningToProperty.value(property, 0)) { + m_propertyToKerning[pointProp] = 0; + m_kerningToProperty.remove(property); + } +} + +void QtFontPropertyManagerPrivate::slotFontDatabaseChanged() +{ + if (!m_fontDatabaseChangeTimer) { + m_fontDatabaseChangeTimer = new QTimer(q_ptr); + m_fontDatabaseChangeTimer->setInterval(0); + m_fontDatabaseChangeTimer->setSingleShot(true); + QObject::connect(m_fontDatabaseChangeTimer, SIGNAL(timeout()), q_ptr, SLOT(slotFontDatabaseDelayedChange())); + } + if (!m_fontDatabaseChangeTimer->isActive()) + m_fontDatabaseChangeTimer->start(); +} + +void QtFontPropertyManagerPrivate::slotFontDatabaseDelayedChange() +{ + typedef QMap PropertyPropertyMap; + // rescan available font names + const QStringList oldFamilies = m_familyNames; + m_familyNames = fontDatabase()->families(); + + // Adapt all existing properties + if (!m_propertyToFamily.empty()) { + PropertyPropertyMap::const_iterator cend = m_propertyToFamily.constEnd(); + for (PropertyPropertyMap::const_iterator it = m_propertyToFamily.constBegin(); it != cend; ++it) { + QtProperty *familyProp = it.value(); + const int oldIdx = m_enumPropertyManager->value(familyProp); + int newIdx = m_familyNames.indexOf(oldFamilies.at(oldIdx)); + if (newIdx < 0) + newIdx = 0; + m_enumPropertyManager->setEnumNames(familyProp, m_familyNames); + m_enumPropertyManager->setValue(familyProp, newIdx); + } + } +} + +/*! + \class QtFontPropertyManager + + \brief The QtFontPropertyManager provides and manages QFont properties. + + A font property has nested \e family, \e pointSize, \e bold, \e + italic, \e underline, \e strikeOut and \e kerning subproperties. The top-level + property's value can be retrieved using the value() function, and + set using the setValue() slot. + + The subproperties are created by QtIntPropertyManager, QtEnumPropertyManager and + QtBoolPropertyManager objects. These managers can be retrieved using the + corresponding subIntPropertyManager(), subEnumPropertyManager() and + subBoolPropertyManager() functions. In order to provide editing widgets + for the subproperties in a property browser widget, these managers + must be associated with editor factories. + + In addition, QtFontPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtEnumPropertyManager, QtIntPropertyManager, QtBoolPropertyManager +*/ + +/*! + \fn void QtFontPropertyManager::valueChanged(QtProperty *property, const QFont &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtFontPropertyManager::QtFontPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtFontPropertyManagerPrivate; + d_ptr->q_ptr = this; +#if QT_VERSION >= 0x040500 + QObject::connect(qApp, SIGNAL(fontDatabaseChanged()), this, SLOT(slotFontDatabaseChanged())); +#endif + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); + connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotBoolChanged(QtProperty *, bool))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtFontPropertyManager::~QtFontPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the \e pointSize subproperty. + + In order to provide editing widgets for the \e pointSize property + in a property browser widget, this manager must be associated + with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtFontPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the manager that create the \e family subproperty. + + In order to provide editing widgets for the \e family property + in a property browser widget, this manager must be associated + with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtFontPropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the manager that creates the \e bold, \e italic, \e underline, + \e strikeOut and \e kerning subproperties. + + In order to provide editing widgets for the mentioned properties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtBoolPropertyManager *QtFontPropertyManager::subBoolPropertyManager() const +{ + return d_ptr->m_boolPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns a font object that uses the application's default + font. + + \sa setValue() +*/ +QFont QtFontPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QFont()); +} + +/*! + \reimp +*/ +QString QtFontPropertyManager::valueText(const QtProperty *property) const +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return QtPropertyBrowserUtils::fontValueText(it.value()); +} + +/*! + \reimp +*/ +QIcon QtFontPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + return QtPropertyBrowserUtils::fontValueIcon(it.value()); +} + +/*! + \fn void QtFontPropertyManager::setValue(QtProperty *property, const QFont &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtFontPropertyManager::setValue(QtProperty *property, const QFont &val) +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + const QFont oldVal = it.value(); + if (oldVal == val && oldVal.resolve() == val.resolve()) + return; + + it.value() = val; + + int idx = d_ptr->m_familyNames.indexOf(val.family()); + if (idx == -1) + idx = 0; + bool settingValue = d_ptr->m_settingValue; + d_ptr->m_settingValue = true; + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToFamily[property], idx); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToPointSize[property], val.pointSize()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToBold[property], val.bold()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToItalic[property], val.italic()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToUnderline[property], val.underline()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToStrikeOut[property], val.strikeOut()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToKerning[property], val.kerning()); + d_ptr->m_settingValue = settingValue; + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtFontPropertyManager::initializeProperty(QtProperty *property) +{ + QFont val; + d_ptr->m_values[property] = val; + + QtProperty *familyProp = d_ptr->m_enumPropertyManager->addProperty(); + familyProp->setPropertyName(tr("Family")); + if (d_ptr->m_familyNames.empty()) + d_ptr->m_familyNames = fontDatabase()->families(); + d_ptr->m_enumPropertyManager->setEnumNames(familyProp, d_ptr->m_familyNames); + int idx = d_ptr->m_familyNames.indexOf(val.family()); + if (idx == -1) + idx = 0; + d_ptr->m_enumPropertyManager->setValue(familyProp, idx); + d_ptr->m_propertyToFamily[property] = familyProp; + d_ptr->m_familyToProperty[familyProp] = property; + property->addSubProperty(familyProp); + + QtProperty *pointSizeProp = d_ptr->m_intPropertyManager->addProperty(); + pointSizeProp->setPropertyName(tr("Point Size")); + d_ptr->m_intPropertyManager->setValue(pointSizeProp, val.pointSize()); + d_ptr->m_intPropertyManager->setMinimum(pointSizeProp, 1); + d_ptr->m_propertyToPointSize[property] = pointSizeProp; + d_ptr->m_pointSizeToProperty[pointSizeProp] = property; + property->addSubProperty(pointSizeProp); + + QtProperty *boldProp = d_ptr->m_boolPropertyManager->addProperty(); + boldProp->setPropertyName(tr("Bold")); + d_ptr->m_boolPropertyManager->setValue(boldProp, val.bold()); + d_ptr->m_propertyToBold[property] = boldProp; + d_ptr->m_boldToProperty[boldProp] = property; + property->addSubProperty(boldProp); + + QtProperty *italicProp = d_ptr->m_boolPropertyManager->addProperty(); + italicProp->setPropertyName(tr("Italic")); + d_ptr->m_boolPropertyManager->setValue(italicProp, val.italic()); + d_ptr->m_propertyToItalic[property] = italicProp; + d_ptr->m_italicToProperty[italicProp] = property; + property->addSubProperty(italicProp); + + QtProperty *underlineProp = d_ptr->m_boolPropertyManager->addProperty(); + underlineProp->setPropertyName(tr("Underline")); + d_ptr->m_boolPropertyManager->setValue(underlineProp, val.underline()); + d_ptr->m_propertyToUnderline[property] = underlineProp; + d_ptr->m_underlineToProperty[underlineProp] = property; + property->addSubProperty(underlineProp); + + QtProperty *strikeOutProp = d_ptr->m_boolPropertyManager->addProperty(); + strikeOutProp->setPropertyName(tr("Strikeout")); + d_ptr->m_boolPropertyManager->setValue(strikeOutProp, val.strikeOut()); + d_ptr->m_propertyToStrikeOut[property] = strikeOutProp; + d_ptr->m_strikeOutToProperty[strikeOutProp] = property; + property->addSubProperty(strikeOutProp); + + QtProperty *kerningProp = d_ptr->m_boolPropertyManager->addProperty(); + kerningProp->setPropertyName(tr("Kerning")); + d_ptr->m_boolPropertyManager->setValue(kerningProp, val.kerning()); + d_ptr->m_propertyToKerning[property] = kerningProp; + d_ptr->m_kerningToProperty[kerningProp] = property; + property->addSubProperty(kerningProp); +} + +/*! + \reimp +*/ +void QtFontPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *familyProp = d_ptr->m_propertyToFamily[property]; + if (familyProp) { + d_ptr->m_familyToProperty.remove(familyProp); + delete familyProp; + } + d_ptr->m_propertyToFamily.remove(property); + + QtProperty *pointSizeProp = d_ptr->m_propertyToPointSize[property]; + if (pointSizeProp) { + d_ptr->m_pointSizeToProperty.remove(pointSizeProp); + delete pointSizeProp; + } + d_ptr->m_propertyToPointSize.remove(property); + + QtProperty *boldProp = d_ptr->m_propertyToBold[property]; + if (boldProp) { + d_ptr->m_boldToProperty.remove(boldProp); + delete boldProp; + } + d_ptr->m_propertyToBold.remove(property); + + QtProperty *italicProp = d_ptr->m_propertyToItalic[property]; + if (italicProp) { + d_ptr->m_italicToProperty.remove(italicProp); + delete italicProp; + } + d_ptr->m_propertyToItalic.remove(property); + + QtProperty *underlineProp = d_ptr->m_propertyToUnderline[property]; + if (underlineProp) { + d_ptr->m_underlineToProperty.remove(underlineProp); + delete underlineProp; + } + d_ptr->m_propertyToUnderline.remove(property); + + QtProperty *strikeOutProp = d_ptr->m_propertyToStrikeOut[property]; + if (strikeOutProp) { + d_ptr->m_strikeOutToProperty.remove(strikeOutProp); + delete strikeOutProp; + } + d_ptr->m_propertyToStrikeOut.remove(property); + + QtProperty *kerningProp = d_ptr->m_propertyToKerning[property]; + if (kerningProp) { + d_ptr->m_kerningToProperty.remove(kerningProp); + delete kerningProp; + } + d_ptr->m_propertyToKerning.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtColorPropertyManager + +class QtColorPropertyManagerPrivate +{ + QtColorPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtColorPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToR; + QMap m_propertyToG; + QMap m_propertyToB; + QMap m_propertyToA; + + QMap m_rToProperty; + QMap m_gToProperty; + QMap m_bToProperty; + QMap m_aToProperty; +}; + +void QtColorPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_rToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setRed(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_gToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setGreen(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_bToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setBlue(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_aToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setAlpha(value); + q_ptr->setValue(prop, c); + } +} + +void QtColorPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_rToProperty.value(property, 0)) { + m_propertyToR[pointProp] = 0; + m_rToProperty.remove(property); + } else if (QtProperty *pointProp = m_gToProperty.value(property, 0)) { + m_propertyToG[pointProp] = 0; + m_gToProperty.remove(property); + } else if (QtProperty *pointProp = m_bToProperty.value(property, 0)) { + m_propertyToB[pointProp] = 0; + m_bToProperty.remove(property); + } else if (QtProperty *pointProp = m_aToProperty.value(property, 0)) { + m_propertyToA[pointProp] = 0; + m_aToProperty.remove(property); + } +} + +/*! + \class QtColorPropertyManager + + \brief The QtColorPropertyManager provides and manages QColor properties. + + A color property has nested \e red, \e green and \e blue + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtColorPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser, QtIntPropertyManager +*/ + +/*! + \fn void QtColorPropertyManager::valueChanged(QtProperty *property, const QColor &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtColorPropertyManager::QtColorPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtColorPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtColorPropertyManager::~QtColorPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that produces the nested \e red, \e green and + \e blue subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtColorPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns an invalid color. + + \sa setValue() +*/ +QColor QtColorPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QColor()); +} + +/*! + \reimp +*/ + +QString QtColorPropertyManager::valueText(const QtProperty *property) const +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return QtPropertyBrowserUtils::colorValueText(it.value()); +} + +/*! + \reimp +*/ + +QIcon QtColorPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + return QtPropertyBrowserUtils::brushValueIcon(QBrush(it.value())); +} + +/*! + \fn void QtColorPropertyManager::setValue(QtProperty *property, const QColor &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtColorPropertyManager::setValue(QtProperty *property, const QColor &val) +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToR[property], val.red()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToG[property], val.green()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToB[property], val.blue()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToA[property], val.alpha()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtColorPropertyManager::initializeProperty(QtProperty *property) +{ + QColor val; + d_ptr->m_values[property] = val; + + QtProperty *rProp = d_ptr->m_intPropertyManager->addProperty(); + rProp->setPropertyName(tr("Red")); + d_ptr->m_intPropertyManager->setValue(rProp, val.red()); + d_ptr->m_intPropertyManager->setRange(rProp, 0, 0xFF); + d_ptr->m_propertyToR[property] = rProp; + d_ptr->m_rToProperty[rProp] = property; + property->addSubProperty(rProp); + + QtProperty *gProp = d_ptr->m_intPropertyManager->addProperty(); + gProp->setPropertyName(tr("Green")); + d_ptr->m_intPropertyManager->setValue(gProp, val.green()); + d_ptr->m_intPropertyManager->setRange(gProp, 0, 0xFF); + d_ptr->m_propertyToG[property] = gProp; + d_ptr->m_gToProperty[gProp] = property; + property->addSubProperty(gProp); + + QtProperty *bProp = d_ptr->m_intPropertyManager->addProperty(); + bProp->setPropertyName(tr("Blue")); + d_ptr->m_intPropertyManager->setValue(bProp, val.blue()); + d_ptr->m_intPropertyManager->setRange(bProp, 0, 0xFF); + d_ptr->m_propertyToB[property] = bProp; + d_ptr->m_bToProperty[bProp] = property; + property->addSubProperty(bProp); + + QtProperty *aProp = d_ptr->m_intPropertyManager->addProperty(); + aProp->setPropertyName(tr("Alpha")); + d_ptr->m_intPropertyManager->setValue(aProp, val.alpha()); + d_ptr->m_intPropertyManager->setRange(aProp, 0, 0xFF); + d_ptr->m_propertyToA[property] = aProp; + d_ptr->m_aToProperty[aProp] = property; + property->addSubProperty(aProp); +} + +/*! + \reimp +*/ +void QtColorPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *rProp = d_ptr->m_propertyToR[property]; + if (rProp) { + d_ptr->m_rToProperty.remove(rProp); + delete rProp; + } + d_ptr->m_propertyToR.remove(property); + + QtProperty *gProp = d_ptr->m_propertyToG[property]; + if (gProp) { + d_ptr->m_gToProperty.remove(gProp); + delete gProp; + } + d_ptr->m_propertyToG.remove(property); + + QtProperty *bProp = d_ptr->m_propertyToB[property]; + if (bProp) { + d_ptr->m_bToProperty.remove(bProp); + delete bProp; + } + d_ptr->m_propertyToB.remove(property); + + QtProperty *aProp = d_ptr->m_propertyToA[property]; + if (aProp) { + d_ptr->m_aToProperty.remove(aProp); + delete aProp; + } + d_ptr->m_propertyToA.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtCursorPropertyManager + +Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) + +class QtCursorPropertyManagerPrivate +{ + QtCursorPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtCursorPropertyManager) +public: + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtCursorPropertyManager + + \brief The QtCursorPropertyManager provides and manages QCursor properties. + + A cursor property has a current value which can be + retrieved using the value() function, and set using the setValue() + slot. In addition, QtCursorPropertyManager provides the + valueChanged() signal which is emitted whenever a property created + by this manager changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtCursorPropertyManager::valueChanged(QtProperty *property, const QCursor &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtCursorPropertyManager::QtCursorPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtCursorPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtCursorPropertyManager::~QtCursorPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a default QCursor object. + + \sa setValue() +*/ +#ifndef QT_NO_CURSOR +QCursor QtCursorPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QCursor()); +} +#endif + +/*! + \reimp +*/ +QString QtCursorPropertyManager::valueText(const QtProperty *property) const +{ + const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return cursorDatabase()->cursorToShapeName(it.value()); +} + +/*! + \reimp +*/ +QIcon QtCursorPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + return cursorDatabase()->cursorToShapeIcon(it.value()); +} + +/*! + \fn void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) +{ +#ifndef QT_NO_CURSOR + const QtCursorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value().shape() == value.shape() && value.shape() != Qt::BitmapCursor) + return; + + it.value() = value; + + emit propertyChanged(property); + emit valueChanged(property, value); +#endif +} + +/*! + \reimp +*/ +void QtCursorPropertyManager::initializeProperty(QtProperty *property) +{ +#ifndef QT_NO_CURSOR + d_ptr->m_values[property] = QCursor(); +#endif +} + +/*! + \reimp +*/ +void QtCursorPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtpropertymanager.cpp" +#include "qtpropertymanager.moc" diff --git a/src/qtpropertymanager.h b/src/qtpropertymanager.h new file mode 100644 index 0000000..f5d157b --- /dev/null +++ b/src/qtpropertymanager.h @@ -0,0 +1,749 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTPROPERTYMANAGER_H +#define QTPROPERTYMANAGER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QDate; +class QTime; +class QDateTime; +class QLocale; + +class QT_QTPROPERTYBROWSER_EXPORT QtGroupPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtGroupPropertyManager(QObject *parent = 0); + ~QtGroupPropertyManager(); + +protected: + virtual bool hasValue(const QtProperty *property) const; + + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +}; + +class QtIntPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtIntPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtIntPropertyManager(QObject *parent = 0); + ~QtIntPropertyManager(); + + int value(const QtProperty *property) const; + int minimum(const QtProperty *property) const; + int maximum(const QtProperty *property) const; + int singleStep(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setMinimum(QtProperty *property, int minVal); + void setMaximum(QtProperty *property, int maxVal); + void setRange(QtProperty *property, int minVal, int maxVal); + void setSingleStep(QtProperty *property, int step); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void rangeChanged(QtProperty *property, int minVal, int maxVal); + void singleStepChanged(QtProperty *property, int step); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtIntPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtIntPropertyManager) + Q_DISABLE_COPY(QtIntPropertyManager) +}; + +class QtBoolPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtBoolPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtBoolPropertyManager(QObject *parent = 0); + ~QtBoolPropertyManager(); + + bool value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, bool val); +Q_SIGNALS: + void valueChanged(QtProperty *property, bool val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtBoolPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtBoolPropertyManager) + Q_DISABLE_COPY(QtBoolPropertyManager) +}; + +class QtDoublePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDoublePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDoublePropertyManager(QObject *parent = 0); + ~QtDoublePropertyManager(); + + double value(const QtProperty *property) const; + double minimum(const QtProperty *property) const; + double maximum(const QtProperty *property) const; + double singleStep(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, double val); + void setMinimum(QtProperty *property, double minVal); + void setMaximum(QtProperty *property, double maxVal); + void setRange(QtProperty *property, double minVal, double maxVal); + void setSingleStep(QtProperty *property, double step); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, double val); + void rangeChanged(QtProperty *property, double minVal, double maxVal); + void singleStepChanged(QtProperty *property, double step); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDoublePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDoublePropertyManager) + Q_DISABLE_COPY(QtDoublePropertyManager) +}; + +class QtStringPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtStringPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtStringPropertyManager(QObject *parent = 0); + ~QtStringPropertyManager(); + + QString value(const QtProperty *property) const; + QRegExp regExp(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QString &val); + void setRegExp(QtProperty *property, const QRegExp ®Exp); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QString &val); + void regExpChanged(QtProperty *property, const QRegExp ®Exp); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtStringPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtStringPropertyManager) + Q_DISABLE_COPY(QtStringPropertyManager) +}; + +class QtDatePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDatePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDatePropertyManager(QObject *parent = 0); + ~QtDatePropertyManager(); + + QDate value(const QtProperty *property) const; + QDate minimum(const QtProperty *property) const; + QDate maximum(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QDate &val); + void setMinimum(QtProperty *property, const QDate &minVal); + void setMaximum(QtProperty *property, const QDate &maxVal); + void setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QDate &val); + void rangeChanged(QtProperty *property, const QDate &minVal, const QDate &maxVal); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDatePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDatePropertyManager) + Q_DISABLE_COPY(QtDatePropertyManager) +}; + +class QtTimePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTimePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtTimePropertyManager(QObject *parent = 0); + ~QtTimePropertyManager(); + + QTime value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QTime &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QTime &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtTimePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTimePropertyManager) + Q_DISABLE_COPY(QtTimePropertyManager) +}; + +class QtDateTimePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateTimePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDateTimePropertyManager(QObject *parent = 0); + ~QtDateTimePropertyManager(); + + QDateTime value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QDateTime &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QDateTime &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDateTimePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateTimePropertyManager) + Q_DISABLE_COPY(QtDateTimePropertyManager) +}; + +class QtKeySequencePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtKeySequencePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtKeySequencePropertyManager(QObject *parent = 0); + ~QtKeySequencePropertyManager(); + + QKeySequence value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QKeySequence &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QKeySequence &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtKeySequencePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtKeySequencePropertyManager) + Q_DISABLE_COPY(QtKeySequencePropertyManager) +}; + +class QtCharPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCharPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtCharPropertyManager(QObject *parent = 0); + ~QtCharPropertyManager(); + + QChar value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QChar &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QChar &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtCharPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCharPropertyManager) + Q_DISABLE_COPY(QtCharPropertyManager) +}; + +class QtEnumPropertyManager; +class QtLocalePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtLocalePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtLocalePropertyManager(QObject *parent = 0); + ~QtLocalePropertyManager(); + + QtEnumPropertyManager *subEnumPropertyManager() const; + + QLocale value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QLocale &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QLocale &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtLocalePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtLocalePropertyManager) + Q_DISABLE_COPY(QtLocalePropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtPointPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtPointPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtPointPropertyManager(QObject *parent = 0); + ~QtPointPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QPoint value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QPoint &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QPoint &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtPointPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtPointPropertyManager) + Q_DISABLE_COPY(QtPointPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtPointFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtPointFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtPointFPropertyManager(QObject *parent = 0); + ~QtPointFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QPointF value(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QPointF &val); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QPointF &val); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtPointFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtPointFPropertyManager) + Q_DISABLE_COPY(QtPointFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizePropertyManager(QObject *parent = 0); + ~QtSizePropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QSize value(const QtProperty *property) const; + QSize minimum(const QtProperty *property) const; + QSize maximum(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSize &val); + void setMinimum(QtProperty *property, const QSize &minVal); + void setMaximum(QtProperty *property, const QSize &maxVal); + void setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSize &val); + void rangeChanged(QtProperty *property, const QSize &minVal, const QSize &maxVal); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizePropertyManager) + Q_DISABLE_COPY(QtSizePropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizeFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizeFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizeFPropertyManager(QObject *parent = 0); + ~QtSizeFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QSizeF value(const QtProperty *property) const; + QSizeF minimum(const QtProperty *property) const; + QSizeF maximum(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSizeF &val); + void setMinimum(QtProperty *property, const QSizeF &minVal); + void setMaximum(QtProperty *property, const QSizeF &maxVal); + void setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSizeF &val); + void rangeChanged(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizeFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizeFPropertyManager) + Q_DISABLE_COPY(QtSizeFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtRectPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtRectPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtRectPropertyManager(QObject *parent = 0); + ~QtRectPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QRect value(const QtProperty *property) const; + QRect constraint(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QRect &val); + void setConstraint(QtProperty *property, const QRect &constraint); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QRect &val); + void constraintChanged(QtProperty *property, const QRect &constraint); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtRectPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtRectPropertyManager) + Q_DISABLE_COPY(QtRectPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtRectFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtRectFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtRectFPropertyManager(QObject *parent = 0); + ~QtRectFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QRectF value(const QtProperty *property) const; + QRectF constraint(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QRectF &val); + void setConstraint(QtProperty *property, const QRectF &constraint); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QRectF &val); + void constraintChanged(QtProperty *property, const QRectF &constraint); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtRectFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtRectFPropertyManager) + Q_DISABLE_COPY(QtRectFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtEnumPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtEnumPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtEnumPropertyManager(QObject *parent = 0); + ~QtEnumPropertyManager(); + + int value(const QtProperty *property) const; + QStringList enumNames(const QtProperty *property) const; + QMap enumIcons(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setEnumNames(QtProperty *property, const QStringList &names); + void setEnumIcons(QtProperty *property, const QMap &icons); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void enumNamesChanged(QtProperty *property, const QStringList &names); + void enumIconsChanged(QtProperty *property, const QMap &icons); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtEnumPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtEnumPropertyManager) + Q_DISABLE_COPY(QtEnumPropertyManager) +}; + +class QtFlagPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFlagPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtFlagPropertyManager(QObject *parent = 0); + ~QtFlagPropertyManager(); + + QtBoolPropertyManager *subBoolPropertyManager() const; + + int value(const QtProperty *property) const; + QStringList flagNames(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setFlagNames(QtProperty *property, const QStringList &names); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void flagNamesChanged(QtProperty *property, const QStringList &names); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtFlagPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFlagPropertyManager) + Q_DISABLE_COPY(QtFlagPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizePolicyPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizePolicyPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizePolicyPropertyManager(QObject *parent = 0); + ~QtSizePolicyPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + QtEnumPropertyManager *subEnumPropertyManager() const; + + QSizePolicy value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSizePolicy &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSizePolicy &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizePolicyPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizePolicyPropertyManager) + Q_DISABLE_COPY(QtSizePolicyPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtFontPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFontPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtFontPropertyManager(QObject *parent = 0); + ~QtFontPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + QtEnumPropertyManager *subEnumPropertyManager() const; + QtBoolPropertyManager *subBoolPropertyManager() const; + + QFont value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QFont &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QFont &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtFontPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFontPropertyManager) + Q_DISABLE_COPY(QtFontPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseChanged()) + Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseDelayedChange()) +}; + +class QtColorPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtColorPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtColorPropertyManager(QObject *parent = 0); + ~QtColorPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QColor value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QColor &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QColor &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtColorPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtColorPropertyManager) + Q_DISABLE_COPY(QtColorPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtCursorPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCursorPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtCursorPropertyManager(QObject *parent = 0); + ~QtCursorPropertyManager(); + +#ifndef QT_NO_CURSOR + QCursor value(const QtProperty *property) const; +#endif + +public Q_SLOTS: + void setValue(QtProperty *property, const QCursor &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QCursor &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtCursorPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCursorPropertyManager) + Q_DISABLE_COPY(QtCursorPropertyManager) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qttreepropertybrowser.cpp b/src/qttreepropertybrowser.cpp new file mode 100644 index 0000000..9e506af --- /dev/null +++ b/src/qttreepropertybrowser.cpp @@ -0,0 +1,1076 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qttreepropertybrowser.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtPropertyEditorView; + +class QtTreePropertyBrowserPrivate +{ + QtTreePropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtTreePropertyBrowser) + +public: + QtTreePropertyBrowserPrivate(); + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + QtProperty *indexToProperty(const QModelIndex &index) const; + QTreeWidgetItem *indexToItem(const QModelIndex &index) const; + QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const; + bool lastColumn(int column) const; + void disableItem(QTreeWidgetItem *item) const; + void enableItem(QTreeWidgetItem *item) const; + bool hasValue(QTreeWidgetItem *item) const; + + void slotCollapsed(const QModelIndex &index); + void slotExpanded(const QModelIndex &index); + + QColor calculatedBackgroundColor(QtBrowserItem *item) const; + + QtPropertyEditorView *treeWidget() const { return m_treeWidget; } + bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; } + + QtBrowserItem *currentItem() const; + void setCurrentItem(QtBrowserItem *browserItem, bool block); + void editItem(QtBrowserItem *browserItem); + + void slotCurrentBrowserItemChanged(QtBrowserItem *item); + void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *); + + QTreeWidgetItem *editedItem() const; + +private: + void updateItem(QTreeWidgetItem *item); + + QMap m_indexToItem; + QMap m_itemToIndex; + + QMap m_indexToBackgroundColor; + + QtPropertyEditorView *m_treeWidget; + + bool m_headerVisible; + QtTreePropertyBrowser::ResizeMode m_resizeMode; + class QtPropertyEditorDelegate *m_delegate; + bool m_markPropertiesWithoutValue; + bool m_browserChangedBlocked; + QIcon m_expandIcon; +}; + +// ------------ QtPropertyEditorView +class QtPropertyEditorView : public QTreeWidget +{ + Q_OBJECT +public: + QtPropertyEditorView(QWidget *parent = 0); + + void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) + { m_editorPrivate = editorPrivate; } + + QTreeWidgetItem *indexToItem(const QModelIndex &index) const + { return itemFromIndex(index); } + +protected: + void keyPressEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent *event); + void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + QtTreePropertyBrowserPrivate *m_editorPrivate; +}; + +QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) : + QTreeWidget(parent), + m_editorPrivate(0) +{ + connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int))); +} + +void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV3 opt = option; + bool hasValue = true; + if (m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property) + hasValue = property->hasValue(); + } + if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { + const QColor c = option.palette.color(QPalette::Dark); + painter->fillRect(option.rect, c); + opt.palette.setColor(QPalette::AlternateBase, c); + } else { + const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); + if (c.isValid()) { + painter->fillRect(option.rect, c); + opt.palette.setColor(QPalette::AlternateBase, c.lighter(112)); + } + } + QTreeWidget::drawRow(painter, opt, index); + QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); + painter->save(); + painter->setPen(QPen(color)); + painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + painter->restore(); +} + +void QtPropertyEditorView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Space: // Trigger Edit + if (!m_editorPrivate->editedItem()) + if (const QTreeWidgetItem *item = currentItem()) + if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { + event->accept(); + // If the current position is at column 0, move to 1. + QModelIndex index = currentIndex(); + if (index.column() == 0) { + index = index.sibling(index.row(), 1); + setCurrentIndex(index); + } + edit(index); + return; + } + break; + default: + break; + } + QTreeWidget::keyPressEvent(event); +} + +void QtPropertyEditorView::mousePressEvent(QMouseEvent *event) +{ + QTreeWidget::mousePressEvent(event); + QTreeWidgetItem *item = itemAt(event->pos()); + + if (item) { + if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton) + && (header()->logicalIndexAt(event->pos().x()) == 1) + && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { + editItem(item, 1); + } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) { + if (event->pos().x() + header()->offset() < 20) + item->setExpanded(!item->isExpanded()); + } + } +} + +// ------------ QtPropertyEditorDelegate +class QtPropertyEditorDelegate : public QItemDelegate +{ + Q_OBJECT +public: + QtPropertyEditorDelegate(QObject *parent = 0) + : QItemDelegate(parent), m_editorPrivate(0), m_editedItem(0), m_editedWidget(0), m_disablePainting(false) + {} + + void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) + { m_editorPrivate = editorPrivate; } + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setModelData(QWidget *, QAbstractItemModel *, + const QModelIndex &) const {} + + void setEditorData(QWidget *, const QModelIndex &) const {} + + bool eventFilter(QObject *object, QEvent *event); + void closeEditor(QtProperty *property); + + QTreeWidgetItem *editedItem() const { return m_editedItem; } + +protected: + + void drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, + const QRect &rect, const QPixmap &pixmap) const; + void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, + const QRect &rect, const QString &text) const; + +private slots: + void slotEditorDestroyed(QObject *object); + +private: + int indentation(const QModelIndex &index) const; + + typedef QMap EditorToPropertyMap; + mutable EditorToPropertyMap m_editorToProperty; + + typedef QMap PropertyToEditorMap; + mutable PropertyToEditorMap m_propertyToEditor; + QtTreePropertyBrowserPrivate *m_editorPrivate; + mutable QTreeWidgetItem *m_editedItem; + mutable QWidget *m_editedWidget; + mutable bool m_disablePainting; +}; + +int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const +{ + if (!m_editorPrivate) + return 0; + + QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); + int indent = 0; + while (item->parent()) { + item = item->parent(); + ++indent; + } + if (m_editorPrivate->treeWidget()->rootIsDecorated()) + ++indent; + return indent * m_editorPrivate->treeWidget()->indentation(); +} + +void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object) +{ + if (QWidget *w = qobject_cast(object)) { + const EditorToPropertyMap::iterator it = m_editorToProperty.find(w); + if (it != m_editorToProperty.end()) { + m_propertyToEditor.remove(it.value()); + m_editorToProperty.erase(it); + } + if (m_editedWidget == w) { + m_editedWidget = 0; + m_editedItem = 0; + } + } +} + +void QtPropertyEditorDelegate::closeEditor(QtProperty *property) +{ + if (QWidget *w = m_propertyToEditor.value(property, 0)) + w->deleteLater(); +} + +QWidget *QtPropertyEditorDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &, const QModelIndex &index) const +{ + if (index.column() == 1 && m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); + if (property && item && (item->flags() & Qt::ItemIsEnabled)) { + QWidget *editor = m_editorPrivate->createEditor(property, parent); + if (editor) { + editor->setAutoFillBackground(true); + editor->installEventFilter(const_cast(this)); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + m_propertyToEditor[property] = editor; + m_editorToProperty[editor] = property; + m_editedItem = item; + m_editedWidget = editor; + } + return editor; + } + } + return 0; +} + +void QtPropertyEditorDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + editor->setGeometry(option.rect.adjusted(0, 0, 0, -1)); +} + +void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + bool hasValue = true; + if (m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property) + hasValue = property->hasValue(); + } + QStyleOptionViewItemV3 opt = option; + if ((m_editorPrivate && index.column() == 0) || !hasValue) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property && property->isModified()) { + opt.font.setBold(true); + opt.fontMetrics = QFontMetrics(opt.font); + } + } + QColor c; + if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { + c = opt.palette.color(QPalette::Dark); + opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText)); + } else { + c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); + if (c.isValid() && (opt.features & QStyleOptionViewItemV2::Alternate)) + c = c.lighter(112); + } + if (c.isValid()) + painter->fillRect(option.rect, c); + opt.state &= ~QStyle::State_HasFocus; + if (index.column() == 1) { + QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); + if (m_editedItem && m_editedItem == item) + m_disablePainting = true; + } + QItemDelegate::paint(painter, opt, index); + m_disablePainting = false; + + opt.palette.setCurrentColorGroup(QPalette::Active); + QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); + painter->save(); + painter->setPen(QPen(color)); + if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) { + int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left(); + painter->drawLine(right, option.rect.y(), right, option.rect.bottom()); + } + painter->restore(); +} + +void QtPropertyEditorDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, + const QRect &rect, const QPixmap &pixmap) const +{ + if (m_disablePainting) + return; + + QItemDelegate::drawDecoration(painter, option, rect, pixmap); +} + +void QtPropertyEditorDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, + const QRect &rect, const QString &text) const +{ + if (m_disablePainting) + return; + + QItemDelegate::drawDisplay(painter, option, rect, text); +} + +QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + return QItemDelegate::sizeHint(option, index) + QSize(3, 4); +} + +bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FocusOut) { + QFocusEvent *fe = static_cast(event); + if (fe->reason() == Qt::ActiveWindowFocusReason) + return false; + } + return QItemDelegate::eventFilter(object, event); +} + +// -------- QtTreePropertyBrowserPrivate implementation +QtTreePropertyBrowserPrivate::QtTreePropertyBrowserPrivate() : + m_treeWidget(0), + m_headerVisible(true), + m_resizeMode(QtTreePropertyBrowser::Stretch), + m_delegate(0), + m_markPropertiesWithoutValue(false), + m_browserChangedBlocked(false) +{ +} + +// Draw an icon indicating opened/closing branches +static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style) +{ + QPixmap pix(14, 14); + pix.fill(Qt::transparent); + QStyleOption branchOption; + QRect r(QPoint(0, 0), pix.size()); + branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp + branchOption.palette = palette; + branchOption.state = QStyle::State_Children; + + QPainter p; + // Draw closed state + p.begin(&pix); + style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); + p.end(); + QIcon rc = pix; + rc.addPixmap(pix, QIcon::Selected, QIcon::Off); + // Draw opened state + branchOption.state |= QStyle::State_Open; + pix.fill(Qt::transparent); + p.begin(&pix); + style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); + p.end(); + + rc.addPixmap(pix, QIcon::Normal, QIcon::On); + rc.addPixmap(pix, QIcon::Selected, QIcon::On); + return rc; +} + +void QtTreePropertyBrowserPrivate::init(QWidget *parent) +{ + QHBoxLayout *layout = new QHBoxLayout(parent); + layout->setMargin(0); + m_treeWidget = new QtPropertyEditorView(parent); + m_treeWidget->setEditorPrivate(this); + m_treeWidget->setIconSize(QSize(18, 18)); + layout->addWidget(m_treeWidget); + + m_treeWidget->setColumnCount(2); + QStringList labels; + labels.append(QApplication::translate("QtTreePropertyBrowser", "Property", 0)); + labels.append(QApplication::translate("QtTreePropertyBrowser", "Value", 0)); + m_treeWidget->setHeaderLabels(labels); + m_treeWidget->setAlternatingRowColors(true); + m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed); + m_delegate = new QtPropertyEditorDelegate(parent); + m_delegate->setEditorPrivate(this); + m_treeWidget->setItemDelegate(m_delegate); + m_treeWidget->header()->setSectionsMovable(false); + m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); + + m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style()); + + QObject::connect(m_treeWidget, SIGNAL(collapsed(const QModelIndex &)), q_ptr, SLOT(slotCollapsed(const QModelIndex &))); + QObject::connect(m_treeWidget, SIGNAL(expanded(const QModelIndex &)), q_ptr, SLOT(slotExpanded(const QModelIndex &))); + QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); +} + +QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const +{ + if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem()) + return m_itemToIndex.value(treeItem); + return 0; +} + +void QtTreePropertyBrowserPrivate::setCurrentItem(QtBrowserItem *browserItem, bool block) +{ + const bool blocked = block ? m_treeWidget->blockSignals(true) : false; + if (browserItem == 0) + m_treeWidget->setCurrentItem(0); + else + m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem)); + if (block) + m_treeWidget->blockSignals(blocked); +} + +QtProperty *QtTreePropertyBrowserPrivate::indexToProperty(const QModelIndex &index) const +{ + QTreeWidgetItem *item = m_treeWidget->indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (idx) + return idx->property(); + return 0; +} + +QtBrowserItem *QtTreePropertyBrowserPrivate::indexToBrowserItem(const QModelIndex &index) const +{ + QTreeWidgetItem *item = m_treeWidget->indexToItem(index); + return m_itemToIndex.value(item); +} + +QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const +{ + return m_treeWidget->indexToItem(index); +} + +bool QtTreePropertyBrowserPrivate::lastColumn(int column) const +{ + return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1; +} + +void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const +{ + Qt::ItemFlags flags = item->flags(); + if (flags & Qt::ItemIsEnabled) { + flags &= ~Qt::ItemIsEnabled; + item->setFlags(flags); + m_delegate->closeEditor(m_itemToIndex[item]->property()); + const int childCount = item->childCount(); + for (int i = 0; i < childCount; i++) { + QTreeWidgetItem *child = item->child(i); + disableItem(child); + } + } +} + +void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const +{ + Qt::ItemFlags flags = item->flags(); + flags |= Qt::ItemIsEnabled; + item->setFlags(flags); + const int childCount = item->childCount(); + for (int i = 0; i < childCount; i++) { + QTreeWidgetItem *child = item->child(i); + QtProperty *property = m_itemToIndex[child]->property(); + if (property->isEnabled()) { + enableItem(child); + } + } +} + +bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const +{ + QtBrowserItem *browserItem = m_itemToIndex.value(item); + if (browserItem) + return browserItem->property()->hasValue(); + return false; +} + +void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex); + QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent()); + + QTreeWidgetItem *newItem = 0; + if (parentItem) { + newItem = new QTreeWidgetItem(parentItem, afterItem); + } else { + newItem = new QTreeWidgetItem(m_treeWidget, afterItem); + } + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); + m_treeWidget->setItemExpanded(newItem, true); + + updateItem(newItem); +} + +void QtTreePropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + QTreeWidgetItem *item = m_indexToItem.value(index); + + if (m_treeWidget->currentItem() == item) { + m_treeWidget->setCurrentItem(0); + } + + delete item; + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + m_indexToBackgroundColor.remove(index); +} + +void QtTreePropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + QTreeWidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + QIcon expandIcon; + if (property->hasValue()) { + QString toolTip = property->toolTip(); + if (toolTip.isEmpty()) + toolTip = property->valueText(); + item->setToolTip(1, toolTip); + item->setIcon(1, property->valueIcon()); + item->setText(1, property->valueText()); + } else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated()) { + expandIcon = m_expandIcon; + } + item->setIcon(0, expandIcon); + item->setFirstColumnSpanned(!property->hasValue()); + item->setToolTip(0, property->propertyName()); + item->setStatusTip(0, property->statusTip()); + item->setWhatsThis(0, property->whatsThis()); + item->setText(0, property->propertyName()); + bool wasEnabled = item->flags() & Qt::ItemIsEnabled; + bool isEnabled = wasEnabled; + if (property->isEnabled()) { + QTreeWidgetItem *parent = item->parent(); + if (!parent || (parent->flags() & Qt::ItemIsEnabled)) + isEnabled = true; + else + isEnabled = false; + } else { + isEnabled = false; + } + if (wasEnabled != isEnabled) { + if (isEnabled) + enableItem(item); + else + disableItem(item); + } + m_treeWidget->viewport()->update(); +} + +QColor QtTreePropertyBrowserPrivate::calculatedBackgroundColor(QtBrowserItem *item) const +{ + QtBrowserItem *i = item; + const QMap::const_iterator itEnd = m_indexToBackgroundColor.constEnd(); + while (i) { + QMap::const_iterator it = m_indexToBackgroundColor.constFind(i); + if (it != itEnd) + return it.value(); + i = i->parent(); + } + return QColor(); +} + +void QtTreePropertyBrowserPrivate::slotCollapsed(const QModelIndex &index) +{ + QTreeWidgetItem *item = indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (item) + emit q_ptr->collapsed(idx); +} + +void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index) +{ + QTreeWidgetItem *item = indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (item) + emit q_ptr->expanded(idx); +} + +void QtTreePropertyBrowserPrivate::slotCurrentBrowserItemChanged(QtBrowserItem *item) +{ + if (!m_browserChangedBlocked && item != currentItem()) + setCurrentItem(item, true); +} + +void QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *) +{ + QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0; + m_browserChangedBlocked = true; + q_ptr->setCurrentItem(browserItem); + m_browserChangedBlocked = false; +} + +QTreeWidgetItem *QtTreePropertyBrowserPrivate::editedItem() const +{ + return m_delegate->editedItem(); +} + +void QtTreePropertyBrowserPrivate::editItem(QtBrowserItem *browserItem) +{ + if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, 0)) { + m_treeWidget->setCurrentItem (treeItem, 1); + m_treeWidget->editItem(treeItem, 1); + } +} + +/*! + \class QtTreePropertyBrowser + + \brief The QtTreePropertyBrowser class provides QTreeWidget based + property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtTreePropertyBrowser provides a tree based view for all nested + properties, i.e. properties that have subproperties can be in an + expanded (subproperties are visible) or collapsed (subproperties + are hidden) state. For example: + + \image qttreepropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtTreePropertyBrowser class. + The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item) + + This signal is emitted when the \a item is collapsed. + + \sa expanded(), setExpanded() +*/ + +/*! + \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item) + + This signal is emitted when the \a item is expanded. + + \sa collapsed(), setExpanded() +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtTreePropertyBrowser::QtTreePropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtTreePropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); + connect(this, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentBrowserItemChanged(QtBrowserItem*))); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtTreePropertyBrowser::~QtTreePropertyBrowser() +{ + delete d_ptr; +} + +/*! + \property QtTreePropertyBrowser::indentation + \brief indentation of the items in the tree view. +*/ +int QtTreePropertyBrowser::indentation() const +{ + return d_ptr->m_treeWidget->indentation(); +} + +void QtTreePropertyBrowser::setIndentation(int i) +{ + d_ptr->m_treeWidget->setIndentation(i); +} + +/*! + \property QtTreePropertyBrowser::rootIsDecorated + \brief whether to show controls for expanding and collapsing root items. +*/ +bool QtTreePropertyBrowser::rootIsDecorated() const +{ + return d_ptr->m_treeWidget->rootIsDecorated(); +} + +void QtTreePropertyBrowser::setRootIsDecorated(bool show) +{ + d_ptr->m_treeWidget->setRootIsDecorated(show); + QMapIterator it(d_ptr->m_itemToIndex); + while (it.hasNext()) { + QtProperty *property = it.next().value()->property(); + if (!property->hasValue()) + d_ptr->updateItem(it.key()); + } +} + +/*! + \property QtTreePropertyBrowser::alternatingRowColors + \brief whether to draw the background using alternating colors. + By default this property is set to true. +*/ +bool QtTreePropertyBrowser::alternatingRowColors() const +{ + return d_ptr->m_treeWidget->alternatingRowColors(); +} + +void QtTreePropertyBrowser::setAlternatingRowColors(bool enable) +{ + d_ptr->m_treeWidget->setAlternatingRowColors(enable); + QMapIterator it(d_ptr->m_itemToIndex); +} + +/*! + \property QtTreePropertyBrowser::headerVisible + \brief whether to show the header. +*/ +bool QtTreePropertyBrowser::isHeaderVisible() const +{ + return d_ptr->m_headerVisible; +} + +void QtTreePropertyBrowser::setHeaderVisible(bool visible) +{ + if (d_ptr->m_headerVisible == visible) + return; + + d_ptr->m_headerVisible = visible; + d_ptr->m_treeWidget->header()->setVisible(visible); +} + +/*! + \enum QtTreePropertyBrowser::ResizeMode + + The resize mode specifies the behavior of the header sections. + + \value Interactive The user can resize the sections. + The sections can also be resized programmatically using setSplitterPosition(). + + \value Fixed The user cannot resize the section. + The section can only be resized programmatically using setSplitterPosition(). + + \value Stretch QHeaderView will automatically resize the section to fill the available space. + The size cannot be changed by the user or programmatically. + + \value ResizeToContents QHeaderView will automatically resize the section to its optimal + size based on the contents of the entire column. + The size cannot be changed by the user or programmatically. + + \sa setResizeMode() +*/ + +/*! + \property QtTreePropertyBrowser::resizeMode + \brief the resize mode of setions in the header. +*/ + +QtTreePropertyBrowser::ResizeMode QtTreePropertyBrowser::resizeMode() const +{ + return d_ptr->m_resizeMode; +} + +void QtTreePropertyBrowser::setResizeMode(QtTreePropertyBrowser::ResizeMode mode) +{ + if (d_ptr->m_resizeMode == mode) + return; + + d_ptr->m_resizeMode = mode; + QHeaderView::ResizeMode m = QHeaderView::Stretch; + switch (mode) { + case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive; break; + case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed; break; + case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents; break; + case QtTreePropertyBrowser::Stretch: + default: m = QHeaderView::Stretch; break; + } + d_ptr->m_treeWidget->header()->setSectionResizeMode(m); +} + +/*! + \property QtTreePropertyBrowser::splitterPosition + \brief the position of the splitter between the colunms. +*/ + +int QtTreePropertyBrowser::splitterPosition() const +{ + return d_ptr->m_treeWidget->header()->sectionSize(0); +} + +void QtTreePropertyBrowser::setSplitterPosition(int position) +{ + d_ptr->m_treeWidget->header()->resizeSection(0, position); +} + +/*! + Sets the \a item to either collapse or expanded, depending on the value of \a expanded. + + \sa isExpanded(), expanded(), collapsed() +*/ + +void QtTreePropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) +{ + QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); + if (treeItem) + treeItem->setExpanded(expanded); +} + +/*! + Returns true if the \a item is expanded; otherwise returns false. + + \sa setExpanded() +*/ + +bool QtTreePropertyBrowser::isExpanded(QtBrowserItem *item) const +{ + QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); + if (treeItem) + return treeItem->isExpanded(); + return false; +} + +/*! + Returns true if the \a item is visible; otherwise returns false. + + \sa setItemVisible() + \since 4.5 +*/ + +bool QtTreePropertyBrowser::isItemVisible(QtBrowserItem *item) const +{ + if (const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) + return !treeItem->isHidden(); + return false; +} + +/*! + Sets the \a item to be visible, depending on the value of \a visible. + + \sa isItemVisible() + \since 4.5 +*/ + +void QtTreePropertyBrowser::setItemVisible(QtBrowserItem *item, bool visible) +{ + if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) + treeItem->setHidden(!visible); +} + +/*! + Sets the \a item's background color to \a color. Note that while item's background + is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color) + + \sa backgroundColor(), calculatedBackgroundColor() +*/ + +void QtTreePropertyBrowser::setBackgroundColor(QtBrowserItem *item, const QColor &color) +{ + if (!d_ptr->m_indexToItem.contains(item)) + return; + if (color.isValid()) + d_ptr->m_indexToBackgroundColor[item] = color; + else + d_ptr->m_indexToBackgroundColor.remove(item); + d_ptr->m_treeWidget->viewport()->update(); +} + +/*! + Returns the \a item's color. If there is no color set for item it returns invalid color. + + \sa calculatedBackgroundColor(), setBackgroundColor() +*/ + +QColor QtTreePropertyBrowser::backgroundColor(QtBrowserItem *item) const +{ + return d_ptr->m_indexToBackgroundColor.value(item); +} + +/*! + Returns the \a item's color. If there is no color set for item it returns parent \a item's + color (if there is no color set for parent it returns grandparent's color and so on). In case + the color is not set for \a item and it's top level item it returns invalid color. + + \sa backgroundColor(), setBackgroundColor() +*/ + +QColor QtTreePropertyBrowser::calculatedBackgroundColor(QtBrowserItem *item) const +{ + return d_ptr->calculatedBackgroundColor(item); +} + +/*! + \property QtTreePropertyBrowser::propertiesWithoutValueMarked + \brief whether to enable or disable marking properties without value. + + When marking is enabled the item's background is rendered in dark color and item's + foreground is rendered with light color. + + \sa propertiesWithoutValueMarked() +*/ +void QtTreePropertyBrowser::setPropertiesWithoutValueMarked(bool mark) +{ + if (d_ptr->m_markPropertiesWithoutValue == mark) + return; + + d_ptr->m_markPropertiesWithoutValue = mark; + QMapIterator it(d_ptr->m_itemToIndex); + while (it.hasNext()) { + QtProperty *property = it.next().value()->property(); + if (!property->hasValue()) + d_ptr->updateItem(it.key()); + } + d_ptr->m_treeWidget->viewport()->update(); +} + +bool QtTreePropertyBrowser::propertiesWithoutValueMarked() const +{ + return d_ptr->m_markPropertiesWithoutValue; +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +/*! + Sets the current item to \a item and opens the relevant editor for it. +*/ +void QtTreePropertyBrowser::editItem(QtBrowserItem *item) +{ + d_ptr->editItem(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qttreepropertybrowser.cpp" +#include "qttreepropertybrowser.moc" diff --git a/src/qttreepropertybrowser.h b/src/qttreepropertybrowser.h new file mode 100644 index 0000000..c5f7fa8 --- /dev/null +++ b/src/qttreepropertybrowser.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTTREEPROPERTYBROWSER_H +#define QTTREEPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QTreeWidgetItem; +class QtTreePropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTreePropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT + Q_ENUMS(ResizeMode) + Q_PROPERTY(int indentation READ indentation WRITE setIndentation) + Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated) + Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors) + Q_PROPERTY(bool headerVisible READ isHeaderVisible WRITE setHeaderVisible) + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(int splitterPosition READ splitterPosition WRITE setSplitterPosition) + Q_PROPERTY(bool propertiesWithoutValueMarked READ propertiesWithoutValueMarked WRITE setPropertiesWithoutValueMarked) +public: + + enum ResizeMode + { + Interactive, + Stretch, + Fixed, + ResizeToContents + }; + + QtTreePropertyBrowser(QWidget *parent = 0); + ~QtTreePropertyBrowser(); + + int indentation() const; + void setIndentation(int i); + + bool rootIsDecorated() const; + void setRootIsDecorated(bool show); + + bool alternatingRowColors() const; + void setAlternatingRowColors(bool enable); + + bool isHeaderVisible() const; + void setHeaderVisible(bool visible); + + ResizeMode resizeMode() const; + void setResizeMode(ResizeMode mode); + + int splitterPosition() const; + void setSplitterPosition(int position); + + void setExpanded(QtBrowserItem *item, bool expanded); + bool isExpanded(QtBrowserItem *item) const; + + bool isItemVisible(QtBrowserItem *item) const; + void setItemVisible(QtBrowserItem *item, bool visible); + + void setBackgroundColor(QtBrowserItem *item, const QColor &color); + QColor backgroundColor(QtBrowserItem *item) const; + QColor calculatedBackgroundColor(QtBrowserItem *item) const; + + void setPropertiesWithoutValueMarked(bool mark); + bool propertiesWithoutValueMarked() const; + + void editItem(QtBrowserItem *item); + +Q_SIGNALS: + + void collapsed(QtBrowserItem *item); + void expanded(QtBrowserItem *item); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtTreePropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTreePropertyBrowser) + Q_DISABLE_COPY(QtTreePropertyBrowser) + + Q_PRIVATE_SLOT(d_func(), void slotCollapsed(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotExpanded(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentBrowserItemChanged(QtBrowserItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/src/qtvariantproperty.cpp b/src/qtvariantproperty.cpp new file mode 100644 index 0000000..c41730c --- /dev/null +++ b/src/qtvariantproperty.cpp @@ -0,0 +1,2358 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#include "qtvariantproperty.h" +#include "qtpropertymanager.h" +#include "qteditorfactory.h" +#include +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtEnumPropertyType +{ +}; + + +class QtFlagPropertyType +{ +}; + + +class QtGroupPropertyType +{ +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +Q_DECLARE_METATYPE(QtEnumPropertyType) +Q_DECLARE_METATYPE(QtFlagPropertyType) +Q_DECLARE_METATYPE(QtGroupPropertyType) + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +/*! + Returns the type id for an enum property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Int for the enum property + type). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::enumTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a flag property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Int for the flag property + type). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::flagTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a group property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Invalid for the group + property type, since it doesn't provide any value). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::groupTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a icon map attribute. + + Note that the property's attribute type can be retrieved using the + attributeType() function. + + \sa attributeType(), QtEnumPropertyManager::enumIcons() +*/ +int QtVariantPropertyManager::iconMapTypeId() +{ + return qMetaTypeId(); +} + +typedef QMap PropertyMap; +Q_GLOBAL_STATIC(PropertyMap, propertyToWrappedProperty) + +static QtProperty *wrappedProperty(QtProperty *property) +{ + return propertyToWrappedProperty()->value(property, 0); +} + +class QtVariantPropertyPrivate +{ + QtVariantProperty *q_ptr; +public: + QtVariantPropertyPrivate(QtVariantPropertyManager *m) : manager(m) {} + + QtVariantPropertyManager *manager; +}; + +/*! + \class QtVariantProperty + + \brief The QtVariantProperty class is a convenience class handling + QVariant based properties. + + QtVariantProperty provides additional API: A property's type, + value type, attribute values and current value can easily be + retrieved using the propertyType(), valueType(), attributeValue() + and value() functions respectively. In addition, the attribute + values and the current value can be set using the corresponding + setValue() and setAttribute() functions. + + For example, instead of writing: + + \code + QtVariantPropertyManager *variantPropertyManager; + QtProperty *property; + + variantPropertyManager->setValue(property, 10); + \endcode + + you can write: + + \code + QtVariantPropertyManager *variantPropertyManager; + QtVariantProperty *property; + + property->setValue(10); + \endcode + + QtVariantProperty instances can only be created by the + QtVariantPropertyManager class. + + \sa QtProperty, QtVariantPropertyManager, QtVariantEditorFactory +*/ + +/*! + Creates a variant property using the given \a manager. + + Do not use this constructor to create variant property instances; + use the QtVariantPropertyManager::addProperty() function + instead. This constructor is used internally by the + QtVariantPropertyManager::createProperty() function. + + \sa QtVariantPropertyManager +*/ +QtVariantProperty::QtVariantProperty(QtVariantPropertyManager *manager) + : QtProperty(manager), d_ptr(new QtVariantPropertyPrivate(manager)) +{ + +} + +/*! + Destroys this property. + + \sa QtProperty::~QtProperty() +*/ +QtVariantProperty::~QtVariantProperty() +{ + delete d_ptr; +} + +/*! + Returns the property's current value. + + \sa valueType(), setValue() +*/ +QVariant QtVariantProperty::value() const +{ + return d_ptr->manager->value(this); +} + +/*! + Returns this property's value for the specified \a attribute. + + QtVariantPropertyManager provides a couple of related functions: + \l{QtVariantPropertyManager::attributes()}{attributes()} and + \l{QtVariantPropertyManager::attributeType()}{attributeType()}. + + \sa setAttribute() +*/ +QVariant QtVariantProperty::attributeValue(const QString &attribute) const +{ + return d_ptr->manager->attributeValue(this, attribute); +} + +/*! + Returns the type of this property's value. + + \sa propertyType() +*/ +int QtVariantProperty::valueType() const +{ + return d_ptr->manager->valueType(this); +} + +/*! + Returns this property's type. + + QtVariantPropertyManager provides several related functions: + \l{QtVariantPropertyManager::enumTypeId()}{enumTypeId()}, + \l{QtVariantPropertyManager::flagTypeId()}{flagTypeId()} and + \l{QtVariantPropertyManager::groupTypeId()}{groupTypeId()}. + + \sa valueType() +*/ +int QtVariantProperty::propertyType() const +{ + return d_ptr->manager->propertyType(this); +} + +bool QtVariantProperty::compare(QtProperty* otherProperty)const +{ + bool baseEqual = QtProperty::compare(otherProperty); + if (!baseEqual) + { + return false; + } + const QtVariantProperty* otherVariantProperty + = dynamic_cast(otherProperty); + return (this->value() == otherVariantProperty->value() + && this->valueType() == otherVariantProperty->valueType()); +} + +/*! + Sets the value of this property to \a value. + + The specified \a value must be of the type returned by + valueType(), or of a type that can be converted to valueType() + using the QVariant::canConvert() function; otherwise this function + does nothing. + + \sa value() +*/ +void QtVariantProperty::setValue(const QVariant &value) +{ + d_ptr->manager->setValue(this, value); +} + +/*! + Sets the \a attribute of property to \a value. + + QtVariantPropertyManager provides the related + \l{QtVariantPropertyManager::setAttribute()}{setAttribute()} + function. + + \sa attributeValue() +*/ +void QtVariantProperty::setAttribute(const QString &attribute, const QVariant &value) +{ + d_ptr->manager->setAttribute(this, attribute, value); +} + +class QtVariantPropertyManagerPrivate +{ + QtVariantPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtVariantPropertyManager) +public: + QtVariantPropertyManagerPrivate(); + + bool m_creatingProperty; + bool m_creatingSubProperties; + bool m_destroyingSubProperties; + int m_propertyType; + + void slotValueChanged(QtProperty *property, int val); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotValueChanged(QtProperty *property, double val); + void slotRangeChanged(QtProperty *property, double min, double max); + void slotSingleStepChanged(QtProperty *property, double step); + void slotDecimalsChanged(QtProperty *property, int prec); + void slotValueChanged(QtProperty *property, bool val); + void slotValueChanged(QtProperty *property, const QString &val); + void slotRegExpChanged(QtProperty *property, const QRegExp ®Exp); + void slotValueChanged(QtProperty *property, const QDate &val); + void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); + void slotValueChanged(QtProperty *property, const QTime &val); + void slotValueChanged(QtProperty *property, const QDateTime &val); + void slotValueChanged(QtProperty *property, const QKeySequence &val); + void slotValueChanged(QtProperty *property, const QChar &val); + void slotValueChanged(QtProperty *property, const QLocale &val); + void slotValueChanged(QtProperty *property, const QPoint &val); + void slotValueChanged(QtProperty *property, const QPointF &val); + void slotValueChanged(QtProperty *property, const QSize &val); + void slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max); + void slotValueChanged(QtProperty *property, const QSizeF &val); + void slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max); + void slotValueChanged(QtProperty *property, const QRect &val); + void slotConstraintChanged(QtProperty *property, const QRect &val); + void slotValueChanged(QtProperty *property, const QRectF &val); + void slotConstraintChanged(QtProperty *property, const QRectF &val); + void slotValueChanged(QtProperty *property, const QColor &val); + void slotEnumChanged(QtProperty *property, int val); + void slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames); + void slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons); + void slotValueChanged(QtProperty *property, const QSizePolicy &val); + void slotValueChanged(QtProperty *property, const QFont &val); + void slotValueChanged(QtProperty *property, const QCursor &val); + void slotFlagChanged(QtProperty *property, int val); + void slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames); + void slotPropertyInserted(QtProperty *property, QtProperty *parent, QtProperty *after); + void slotPropertyRemoved(QtProperty *property, QtProperty *parent); + + void valueChanged(QtProperty *property, const QVariant &val); + + int internalPropertyToType(QtProperty *property) const; + QtVariantProperty *createSubProperty(QtVariantProperty *parent, QtVariantProperty *after, + QtProperty *internal); + void removeSubProperty(QtVariantProperty *property); + + QMap m_typeToPropertyManager; + QMap > m_typeToAttributeToAttributeType; + + QMap > m_propertyToType; + + QMap m_typeToValueType; + + + QMap m_internalToProperty; + + const QString m_constraintAttribute; + const QString m_singleStepAttribute; + const QString m_decimalsAttribute; + const QString m_enumIconsAttribute; + const QString m_enumNamesAttribute; + const QString m_flagNamesAttribute; + const QString m_maximumAttribute; + const QString m_minimumAttribute; + const QString m_regExpAttribute; +}; + +QtVariantPropertyManagerPrivate::QtVariantPropertyManagerPrivate() : + m_constraintAttribute(QLatin1String("constraint")), + m_singleStepAttribute(QLatin1String("singleStep")), + m_decimalsAttribute(QLatin1String("decimals")), + m_enumIconsAttribute(QLatin1String("enumIcons")), + m_enumNamesAttribute(QLatin1String("enumNames")), + m_flagNamesAttribute(QLatin1String("flagNames")), + m_maximumAttribute(QLatin1String("maximum")), + m_minimumAttribute(QLatin1String("minimum")), + m_regExpAttribute(QLatin1String("regExp")) +{ +} + +int QtVariantPropertyManagerPrivate::internalPropertyToType(QtProperty *property) const +{ + int type = 0; + QtAbstractPropertyManager *internPropertyManager = property->propertyManager(); + if (qobject_cast(internPropertyManager)) + type = QVariant::Int; + else if (qobject_cast(internPropertyManager)) + type = QtVariantPropertyManager::enumTypeId(); + else if (qobject_cast(internPropertyManager)) + type = QVariant::Bool; + else if (qobject_cast(internPropertyManager)) + type = QVariant::Double; + return type; +} + +QtVariantProperty *QtVariantPropertyManagerPrivate::createSubProperty(QtVariantProperty *parent, + QtVariantProperty *after, QtProperty *internal) +{ + int type = internalPropertyToType(internal); + if (!type) + return 0; + + bool wasCreatingSubProperties = m_creatingSubProperties; + m_creatingSubProperties = true; + + QtVariantProperty *varChild = q_ptr->addProperty(type, internal->propertyName()); + + m_creatingSubProperties = wasCreatingSubProperties; + + varChild->setPropertyName(internal->propertyName()); + varChild->setToolTip(internal->toolTip()); + varChild->setStatusTip(internal->statusTip()); + varChild->setWhatsThis(internal->whatsThis()); + + parent->insertSubProperty(varChild, after); + + m_internalToProperty[internal] = varChild; + propertyToWrappedProperty()->insert(varChild, internal); + return varChild; +} + +void QtVariantPropertyManagerPrivate::removeSubProperty(QtVariantProperty *property) +{ + QtProperty *internChild = wrappedProperty(property); + bool wasDestroyingSubProperties = m_destroyingSubProperties; + m_destroyingSubProperties = true; + delete property; + m_destroyingSubProperties = wasDestroyingSubProperties; + m_internalToProperty.remove(internChild); + propertyToWrappedProperty()->remove(property); +} + +void QtVariantPropertyManagerPrivate::slotPropertyInserted(QtProperty *property, + QtProperty *parent, QtProperty *after) +{ + if (m_creatingProperty) + return; + + QtVariantProperty *varParent = m_internalToProperty.value(parent, 0); + if (!varParent) + return; + + QtVariantProperty *varAfter = 0; + if (after) { + varAfter = m_internalToProperty.value(after, 0); + if (!varAfter) + return; + } + + createSubProperty(varParent, varAfter, property); +} + +void QtVariantPropertyManagerPrivate::slotPropertyRemoved(QtProperty *property, QtProperty *parent) +{ + Q_UNUSED(parent) + + QtVariantProperty *varProperty = m_internalToProperty.value(property, 0); + if (!varProperty) + return; + + removeSubProperty(varProperty); +} + +void QtVariantPropertyManagerPrivate::valueChanged(QtProperty *property, const QVariant &val) +{ + QtVariantProperty *varProp = m_internalToProperty.value(property, 0); + if (!varProp) + return; + emit q_ptr->valueChanged(varProp, val); + emit q_ptr->propertyChanged(varProp); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, int val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, double val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, double min, double max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, double step) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); +} + +void QtVariantPropertyManagerPrivate::slotDecimalsChanged(QtProperty *property, int prec) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_decimalsAttribute, QVariant(prec)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, bool val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QString &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRegExpChanged(QtProperty *property, const QRegExp ®Exp) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_regExpAttribute, QVariant(regExp)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDate &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QTime &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDateTime &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QKeySequence &val) +{ + QVariant v; + qVariantSetValue(v, val); + valueChanged(property, v); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QChar &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QLocale &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPoint &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPointF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSize &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizeF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRect &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRect &constraint) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRectF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRectF &constraint) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QColor &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_enumNamesAttribute, QVariant(enumNames)); +} + +void QtVariantPropertyManagerPrivate::slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + QVariant v; + qVariantSetValue(v, enumIcons); + emit q_ptr->attributeChanged(varProp, m_enumIconsAttribute, v); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizePolicy &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QFont &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QCursor &val) +{ +#ifndef QT_NO_CURSOR + valueChanged(property, QVariant(val)); +#endif +} + +void QtVariantPropertyManagerPrivate::slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_flagNamesAttribute, QVariant(flagNames)); +} + +/*! + \class QtVariantPropertyManager + + \brief The QtVariantPropertyManager class provides and manages QVariant based properties. + + QtVariantPropertyManager provides the addProperty() function which + creates QtVariantProperty objects. The QtVariantProperty class is + a convenience class handling QVariant based properties inheriting + QtProperty. A QtProperty object created by a + QtVariantPropertyManager instance can be converted into a + QtVariantProperty object using the variantProperty() function. + + The property's value can be retrieved using the value(), and set + using the setValue() slot. In addition the property's type, and + the type of its value, can be retrieved using the propertyType() + and valueType() functions respectively. + + A property's type is a QVariant::Type enumerator value, and + usually a property's type is the same as its value type. But for + some properties the types differ, for example for enums, flags and + group types in which case QtVariantPropertyManager provides the + enumTypeId(), flagTypeId() and groupTypeId() functions, + respectively, to identify their property type (the value types are + QVariant::Int for the enum and flag types, and QVariant::Invalid + for the group type). + + Use the isPropertyTypeSupported() function to check if a particular + property type is supported. The currently supported property types + are: + + \table + \header + \o Property Type + \o Property Type Id + \row + \o int + \o QVariant::Int + \row + \o double + \o QVariant::Double + \row + \o bool + \o QVariant::Bool + \row + \o QString + \o QVariant::String + \row + \o QDate + \o QVariant::Date + \row + \o QTime + \o QVariant::Time + \row + \o QDateTime + \o QVariant::DateTime + \row + \o QKeySequence + \o QVariant::KeySequence + \row + \o QChar + \o QVariant::Char + \row + \o QLocale + \o QVariant::Locale + \row + \o QPoint + \o QVariant::Point + \row + \o QPointF + \o QVariant::PointF + \row + \o QSize + \o QVariant::Size + \row + \o QSizeF + \o QVariant::SizeF + \row + \o QRect + \o QVariant::Rect + \row + \o QRectF + \o QVariant::RectF + \row + \o QColor + \o QVariant::Color + \row + \o QSizePolicy + \o QVariant::SizePolicy + \row + \o QFont + \o QVariant::Font + \row + \o QCursor + \o QVariant::Cursor + \row + \o enum + \o enumTypeId() + \row + \o flag + \o flagTypeId() + \row + \o group + \o groupTypeId() + \endtable + + Each property type can provide additional attributes, + e.g. QVariant::Int and QVariant::Double provides minimum and + maximum values. The currently supported attributes are: + + \table + \header + \o Property Type + \o Attribute Name + \o Attribute Type + \row + \o \c int + \o minimum + \o QVariant::Int + \row + \o + \o maximum + \o QVariant::Int + \row + \o + \o singleStep + \o QVariant::Int + \row + \o \c double + \o minimum + \o QVariant::Double + \row + \o + \o maximum + \o QVariant::Double + \row + \o + \o singleStep + \o QVariant::Double + \row + \o + \o decimals + \o QVariant::Int + \row + \o QString + \o regExp + \o QVariant::RegExp + \row + \o QDate + \o minimum + \o QVariant::Date + \row + \o + \o maximum + \o QVariant::Date + \row + \o QPointF + \o decimals + \o QVariant::Int + \row + \o QSize + \o minimum + \o QVariant::Size + \row + \o + \o maximum + \o QVariant::Size + \row + \o QSizeF + \o minimum + \o QVariant::SizeF + \row + \o + \o maximum + \o QVariant::SizeF + \row + \o + \o decimals + \o QVariant::Int + \row + \o QRect + \o constraint + \o QVariant::Rect + \row + \o QRectF + \o constraint + \o QVariant::RectF + \row + \o + \o decimals + \o QVariant::Int + \row + \o \c enum + \o enumNames + \o QVariant::StringList + \row + \o + \o enumIcons + \o iconMapTypeId() + \row + \o \c flag + \o flagNames + \o QVariant::StringList + \endtable + + The attributes for a given property type can be retrieved using + the attributes() function. Each attribute has a value type which + can be retrieved using the attributeType() function, and a value + accessible through the attributeValue() function. In addition, the + value can be set using the setAttribute() slot. + + QtVariantManager also provides the valueChanged() signal which is + emitted whenever a property created by this manager change, and + the attributeChanged() signal which is emitted whenever an + attribute of such a property changes. + + \sa QtVariantProperty, QtVariantEditorFactory +*/ + +/*! + \fn void QtVariantPropertyManager::valueChanged(QtProperty *property, const QVariant &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtVariantPropertyManager::attributeChanged(QtProperty *property, + const QString &attribute, const QVariant &value) + + This signal is emitted whenever an attribute of a property created + by this manager changes its value, passing a pointer to the \a + property, the \a attribute and the new \a value as parameters. + + \sa setAttribute() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtVariantPropertyManager::QtVariantPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtVariantPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_creatingProperty = false; + d_ptr->m_creatingSubProperties = false; + d_ptr->m_destroyingSubProperties = false; + d_ptr->m_propertyType = 0; + + // IntPropertyManager + QtIntPropertyManager *intPropertyManager = new QtIntPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Int] = intPropertyManager; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_minimumAttribute] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_maximumAttribute] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_singleStepAttribute] = QVariant::Int; + d_ptr->m_typeToValueType[QVariant::Int] = QVariant::Int; + connect(intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(intPropertyManager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(intPropertyManager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); + // DoublePropertyManager + QtDoublePropertyManager *doublePropertyManager = new QtDoublePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Double] = doublePropertyManager; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_minimumAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_maximumAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_singleStepAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_decimalsAttribute] = + QVariant::Int; + d_ptr->m_typeToValueType[QVariant::Double] = QVariant::Double; + connect(doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(doublePropertyManager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(doublePropertyManager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + connect(doublePropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + // BoolPropertyManager + QtBoolPropertyManager *boolPropertyManager = new QtBoolPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Bool] = boolPropertyManager; + d_ptr->m_typeToValueType[QVariant::Bool] = QVariant::Bool; + connect(boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + // StringPropertyManager + QtStringPropertyManager *stringPropertyManager = new QtStringPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::String] = stringPropertyManager; + d_ptr->m_typeToValueType[QVariant::String] = QVariant::String; + d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_regExpAttribute] = + QVariant::RegExp; + connect(stringPropertyManager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotValueChanged(QtProperty *, const QString &))); + connect(stringPropertyManager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); + // DatePropertyManager + QtDatePropertyManager *datePropertyManager = new QtDatePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Date] = datePropertyManager; + d_ptr->m_typeToValueType[QVariant::Date] = QVariant::Date; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_minimumAttribute] = + QVariant::Date; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_maximumAttribute] = + QVariant::Date; + connect(datePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotValueChanged(QtProperty *, const QDate &))); + connect(datePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); + // TimePropertyManager + QtTimePropertyManager *timePropertyManager = new QtTimePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Time] = timePropertyManager; + d_ptr->m_typeToValueType[QVariant::Time] = QVariant::Time; + connect(timePropertyManager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotValueChanged(QtProperty *, const QTime &))); + // DateTimePropertyManager + QtDateTimePropertyManager *dateTimePropertyManager = new QtDateTimePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::DateTime] = dateTimePropertyManager; + d_ptr->m_typeToValueType[QVariant::DateTime] = QVariant::DateTime; + connect(dateTimePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotValueChanged(QtProperty *, const QDateTime &))); + // KeySequencePropertyManager + QtKeySequencePropertyManager *keySequencePropertyManager = new QtKeySequencePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::KeySequence] = keySequencePropertyManager; + d_ptr->m_typeToValueType[QVariant::KeySequence] = QVariant::KeySequence; + connect(keySequencePropertyManager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotValueChanged(QtProperty *, const QKeySequence &))); + // CharPropertyManager + QtCharPropertyManager *charPropertyManager = new QtCharPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Char] = charPropertyManager; + d_ptr->m_typeToValueType[QVariant::Char] = QVariant::Char; + connect(charPropertyManager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotValueChanged(QtProperty *, const QChar &))); + // LocalePropertyManager + QtLocalePropertyManager *localePropertyManager = new QtLocalePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Locale] = localePropertyManager; + d_ptr->m_typeToValueType[QVariant::Locale] = QVariant::Locale; + connect(localePropertyManager, SIGNAL(valueChanged(QtProperty *, const QLocale &)), + this, SLOT(slotValueChanged(QtProperty *, const QLocale &))); + connect(localePropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(localePropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(localePropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // PointPropertyManager + QtPointPropertyManager *pointPropertyManager = new QtPointPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Point] = pointPropertyManager; + d_ptr->m_typeToValueType[QVariant::Point] = QVariant::Point; + connect(pointPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPoint &)), + this, SLOT(slotValueChanged(QtProperty *, const QPoint &))); + connect(pointPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(pointPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(pointPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // PointFPropertyManager + QtPointFPropertyManager *pointFPropertyManager = new QtPointFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::PointF] = pointFPropertyManager; + d_ptr->m_typeToValueType[QVariant::PointF] = QVariant::PointF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::PointF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(pointFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPointF &)), + this, SLOT(slotValueChanged(QtProperty *, const QPointF &))); + connect(pointFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(pointFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(pointFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(pointFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // SizePropertyManager + QtSizePropertyManager *sizePropertyManager = new QtSizePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Size] = sizePropertyManager; + d_ptr->m_typeToValueType[QVariant::Size] = QVariant::Size; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_minimumAttribute] = + QVariant::Size; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_maximumAttribute] = + QVariant::Size; + connect(sizePropertyManager, SIGNAL(valueChanged(QtProperty *, const QSize &)), + this, SLOT(slotValueChanged(QtProperty *, const QSize &))); + connect(sizePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSize &, const QSize &)), + this, SLOT(slotRangeChanged(QtProperty *, const QSize &, const QSize &))); + connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(sizePropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizePropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // SizeFPropertyManager + QtSizeFPropertyManager *sizeFPropertyManager = new QtSizeFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::SizeF] = sizeFPropertyManager; + d_ptr->m_typeToValueType[QVariant::SizeF] = QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_minimumAttribute] = + QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_maximumAttribute] = + QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(sizeFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QSizeF &)), + this, SLOT(slotValueChanged(QtProperty *, const QSizeF &))); + connect(sizeFPropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSizeF &, const QSizeF &)), + this, SLOT(slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &))); + connect(sizeFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(sizeFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizeFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // RectPropertyManager + QtRectPropertyManager *rectPropertyManager = new QtRectPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Rect] = rectPropertyManager; + d_ptr->m_typeToValueType[QVariant::Rect] = QVariant::Rect; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Rect][d_ptr->m_constraintAttribute] = + QVariant::Rect; + connect(rectPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRect &)), + this, SLOT(slotValueChanged(QtProperty *, const QRect &))); + connect(rectPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRect &)), + this, SLOT(slotConstraintChanged(QtProperty *, const QRect &))); + connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(rectPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(rectPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // RectFPropertyManager + QtRectFPropertyManager *rectFPropertyManager = new QtRectFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::RectF] = rectFPropertyManager; + d_ptr->m_typeToValueType[QVariant::RectF] = QVariant::RectF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_constraintAttribute] = + QVariant::RectF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(rectFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRectF &)), + this, SLOT(slotValueChanged(QtProperty *, const QRectF &))); + connect(rectFPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRectF &)), + this, SLOT(slotConstraintChanged(QtProperty *, const QRectF &))); + connect(rectFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(rectFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(rectFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // ColorPropertyManager + QtColorPropertyManager *colorPropertyManager = new QtColorPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Color] = colorPropertyManager; + d_ptr->m_typeToValueType[QVariant::Color] = QVariant::Color; + connect(colorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QColor &)), + this, SLOT(slotValueChanged(QtProperty *, const QColor &))); + connect(colorPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(colorPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(colorPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // EnumPropertyManager + int enumId = enumTypeId(); + QtEnumPropertyManager *enumPropertyManager = new QtEnumPropertyManager(this); + d_ptr->m_typeToPropertyManager[enumId] = enumPropertyManager; + d_ptr->m_typeToValueType[enumId] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumNamesAttribute] = + QVariant::StringList; + d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumIconsAttribute] = + iconMapTypeId(); + connect(enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(enumPropertyManager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(enumPropertyManager, SIGNAL(enumIconsChanged(QtProperty *, const QMap &)), + this, SLOT(slotEnumIconsChanged(QtProperty *, const QMap &))); + // SizePolicyPropertyManager + QtSizePolicyPropertyManager *sizePolicyPropertyManager = new QtSizePolicyPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::SizePolicy] = sizePolicyPropertyManager; + d_ptr->m_typeToValueType[QVariant::SizePolicy] = QVariant::SizePolicy; + connect(sizePolicyPropertyManager, SIGNAL(valueChanged(QtProperty *, const QSizePolicy &)), + this, SLOT(slotValueChanged(QtProperty *, const QSizePolicy &))); + connect(sizePolicyPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePolicyPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(sizePolicyPropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePolicyPropertyManager->subEnumPropertyManager(), + SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(sizePolicyPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizePolicyPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // FontPropertyManager + QtFontPropertyManager *fontPropertyManager = new QtFontPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Font] = fontPropertyManager; + d_ptr->m_typeToValueType[QVariant::Font] = QVariant::Font; + connect(fontPropertyManager, SIGNAL(valueChanged(QtProperty *, const QFont &)), + this, SLOT(slotValueChanged(QtProperty *, const QFont &))); + connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(fontPropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(fontPropertyManager->subEnumPropertyManager(), + SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(fontPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + connect(fontPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(fontPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // CursorPropertyManager + QtCursorPropertyManager *cursorPropertyManager = new QtCursorPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Cursor] = cursorPropertyManager; + d_ptr->m_typeToValueType[QVariant::Cursor] = QVariant::Cursor; + connect(cursorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotValueChanged(QtProperty *, const QCursor &))); + // FlagPropertyManager + int flagId = flagTypeId(); + QtFlagPropertyManager *flagPropertyManager = new QtFlagPropertyManager(this); + d_ptr->m_typeToPropertyManager[flagId] = flagPropertyManager; + d_ptr->m_typeToValueType[flagId] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[flagId][d_ptr->m_flagNamesAttribute] = + QVariant::StringList; + connect(flagPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(flagPropertyManager, SIGNAL(flagNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotFlagNamesChanged(QtProperty *, const QStringList &))); + connect(flagPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + connect(flagPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(flagPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // FlagPropertyManager + int groupId = groupTypeId(); + QtGroupPropertyManager *groupPropertyManager = new QtGroupPropertyManager(this); + d_ptr->m_typeToPropertyManager[groupId] = groupPropertyManager; + d_ptr->m_typeToValueType[groupId] = QVariant::Invalid; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtVariantPropertyManager::~QtVariantPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property converted into a QtVariantProperty. + + If the \a property was not created by this variant manager, the + function returns 0. + + \sa createProperty() +*/ +QtVariantProperty *QtVariantPropertyManager::variantProperty(const QtProperty *property) const +{ + const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); + if (it == d_ptr->m_propertyToType.constEnd()) + return 0; + return it.value().first; +} + +/*! + Returns true if the given \a propertyType is supported by this + variant manager; otherwise false. + + \sa propertyType() +*/ +bool QtVariantPropertyManager::isPropertyTypeSupported(int propertyType) const +{ + if (d_ptr->m_typeToValueType.contains(propertyType)) + return true; + return false; +} + +/*! + Creates and returns a variant property of the given \a propertyType + with the given \a name. + + If the specified \a propertyType is not supported by this variant + manager, this function returns 0. + + Do not use the inherited + QtAbstractPropertyManager::addProperty() function to create a + variant property (that function will always return 0 since it will + not be clear what type the property should have). + + \sa isPropertyTypeSupported() +*/ +QtVariantProperty *QtVariantPropertyManager::addProperty(int propertyType, const QString &name) +{ + if (!isPropertyTypeSupported(propertyType)) + return 0; + + bool wasCreating = d_ptr->m_creatingProperty; + d_ptr->m_creatingProperty = true; + d_ptr->m_propertyType = propertyType; + QtProperty *property = QtAbstractPropertyManager::addProperty(name); + d_ptr->m_creatingProperty = wasCreating; + d_ptr->m_propertyType = 0; + + if (!property) + return 0; + + return variantProperty(property); +} + +namespace{ +void addPropertyRecusively(QtVariantPropertyManager * manager, + QtVariantProperty * prop, QtVariantProperty * newProp = 0) + { + if (!newProp) + { + newProp = manager->addProperty(prop->propertyType(), prop->propertyName()); + } + // Copy values + QStringList attributes = manager->attributes(prop->propertyType()); + foreach(const QString& attribute, attributes) + { + newProp->setAttribute(attribute, prop->attributeValue(attribute)); + } + newProp->setPropertyId(prop->propertyId()); + newProp->setStatusTip(prop->statusTip()); + newProp->setWhatsThis(prop->whatsThis()); + newProp->setModified(prop->isModified()); + newProp->setEnabled(prop->isEnabled()); + newProp->setValue(prop->value()); + + foreach(QtProperty * subProp, prop->subProperties()) + { + QtVariantProperty * variantSubProp = dynamic_cast(subProp); + Q_ASSERT(variantSubProp); + QtVariantProperty * newVariantSubProp = + manager->addProperty(variantSubProp->propertyType(), variantSubProp->propertyName()); + newProp->addSubProperty(newVariantSubProp); + addPropertyRecusively(manager, variantSubProp, newVariantSubProp); + } + } +} + +/*! + Set properties used by this manager. + + \sa properties(), addProperty() +*/ +void QtVariantPropertyManager::setProperties(QSet properties) +{ + this->clear(); + foreach(QtProperty * prop, properties) + { + QtVariantProperty * variantProp = dynamic_cast(prop); + if (!variantProp){ continue; } + if (!variantProp->isSubProperty()) + { + addPropertyRecusively(this, variantProp); + } + } +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid variant. + + \sa setValue() +*/ +QVariant QtVariantPropertyManager::value(const QtProperty *property) const +{ + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return QVariant(); + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + return intManager->value(internProp); + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + return doubleManager->value(internProp); + } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { + return boolManager->value(internProp); + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + return stringManager->value(internProp); + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + return dateManager->value(internProp); + } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { + return timeManager->value(internProp); + } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { + return dateTimeManager->value(internProp); + } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { + return keySequenceManager->value(internProp); + } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { + return charManager->value(internProp); + } else if (QtLocalePropertyManager *localeManager = qobject_cast(manager)) { + return localeManager->value(internProp); + } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { + return pointManager->value(internProp); + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + return pointFManager->value(internProp); + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + return sizeManager->value(internProp); + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + return sizeFManager->value(internProp); + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + return rectManager->value(internProp); + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + return rectFManager->value(internProp); + } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { + return colorManager->value(internProp); + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + return enumManager->value(internProp); + } else if (QtSizePolicyPropertyManager *sizePolicyManager = + qobject_cast(manager)) { + return sizePolicyManager->value(internProp); + } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { + return fontManager->value(internProp); +#ifndef QT_NO_CURSOR + } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { + return cursorManager->value(internProp); +#endif + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + return flagManager->value(internProp); + } + return QVariant(); +} + +/*! + Returns the given \a property's value type. + + \sa propertyType() +*/ +int QtVariantPropertyManager::valueType(const QtProperty *property) const +{ + int propType = propertyType(property); + return valueType(propType); +} + +/*! + \overload + + Returns the value type associated with the given \a propertyType. +*/ +int QtVariantPropertyManager::valueType(int propertyType) const +{ + if (d_ptr->m_typeToValueType.contains(propertyType)) + return d_ptr->m_typeToValueType[propertyType]; + return 0; +} + +/*! + Returns the given \a property's type. + + \sa valueType() +*/ +int QtVariantPropertyManager::propertyType(const QtProperty *property) const +{ + const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); + if (it == d_ptr->m_propertyToType.constEnd()) + return 0; + return it.value().second; +} + +/*! + Returns the given \a property's value for the specified \a + attribute + + If the given \a property was not created by \e this manager, or if + the specified \a attribute does not exist, this function returns + an invalid variant. + + \sa attributes(), attributeType(), setAttribute() +*/ +QVariant QtVariantPropertyManager::attributeValue(const QtProperty *property, const QString &attribute) const +{ + int propType = propertyType(property); + if (!propType) + return QVariant(); + + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return QVariant(); + + QMap attributes = it.value(); + QMap::ConstIterator itAttr = attributes.find(attribute); + if (itAttr == attributes.constEnd()) + return QVariant(); + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return QVariant(); + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return intManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return intManager->minimum(internProp); + if (attribute == d_ptr->m_singleStepAttribute) + return intManager->singleStep(internProp); + return QVariant(); + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return doubleManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return doubleManager->minimum(internProp); + if (attribute == d_ptr->m_singleStepAttribute) + return doubleManager->singleStep(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return doubleManager->decimals(internProp); + return QVariant(); + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_regExpAttribute) + return stringManager->regExp(internProp); + return QVariant(); + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return dateManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return dateManager->minimum(internProp); + return QVariant(); + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_decimalsAttribute) + return pointFManager->decimals(internProp); + return QVariant(); + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return sizeManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return sizeManager->minimum(internProp); + return QVariant(); + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return sizeFManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return sizeFManager->minimum(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return sizeFManager->decimals(internProp); + return QVariant(); + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + return rectManager->constraint(internProp); + return QVariant(); + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + return rectFManager->constraint(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return rectFManager->decimals(internProp); + return QVariant(); + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_enumNamesAttribute) + return enumManager->enumNames(internProp); + if (attribute == d_ptr->m_enumIconsAttribute) { + QVariant v; + qVariantSetValue(v, enumManager->enumIcons(internProp)); + return v; + } + return QVariant(); + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_flagNamesAttribute) + return flagManager->flagNames(internProp); + return QVariant(); + } + return QVariant(); +} + +/*! + Returns a list of the given \a propertyType 's attributes. + + \sa attributeValue(), attributeType() +*/ +QStringList QtVariantPropertyManager::attributes(int propertyType) const +{ + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propertyType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return QStringList(); + return it.value().keys(); +} + +/*! + Returns the type of the specified \a attribute of the given \a + propertyType. + + If the given \a propertyType is not supported by \e this manager, + or if the given \a propertyType does not possess the specified \a + attribute, this function returns QVariant::Invalid. + + \sa attributes(), valueType() +*/ +int QtVariantPropertyManager::attributeType(int propertyType, const QString &attribute) const +{ + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propertyType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return 0; + + QMap attributes = it.value(); + QMap::ConstIterator itAttr = attributes.find(attribute); + if (itAttr == attributes.constEnd()) + return 0; + return itAttr.value(); +} + +/*! + \fn void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &value) + + Sets the value of the given \a property to \a value. + + The specified \a value must be of a type returned by valueType(), + or of type that can be converted to valueType() using the + QVariant::canConvert() function, otherwise this function does + nothing. + + \sa value(), QtVariantProperty::setValue(), valueChanged() +*/ +void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &val) +{ + int propType = val.userType(); + if (!propType) + return; + + int valType = valueType(property); + + if (propType != valType && !val.canConvert(static_cast(valType))) + return; + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return; + + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + intManager->setValue(internProp, val.value()); + return; + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + doubleManager->setValue(internProp, val.value()); + return; + } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { + boolManager->setValue(internProp, val.value()); + return; + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + stringManager->setValue(internProp, val.value()); + return; + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + dateManager->setValue(internProp, val.value()); + return; + } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { + timeManager->setValue(internProp, val.value()); + return; + } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { + dateTimeManager->setValue(internProp, val.value()); + return; + } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { + keySequenceManager->setValue(internProp, val.value()); + return; + } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { + charManager->setValue(internProp, val.value()); + return; + } else if (QtLocalePropertyManager *localeManager = qobject_cast(manager)) { + localeManager->setValue(internProp, val.value()); + return; + } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { + pointManager->setValue(internProp, val.value()); + return; + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + pointFManager->setValue(internProp, val.value()); + return; + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + sizeManager->setValue(internProp, val.value()); + return; + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + sizeFManager->setValue(internProp, val.value()); + return; + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + rectManager->setValue(internProp, val.value()); + return; + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + rectFManager->setValue(internProp, val.value()); + return; + } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { + colorManager->setValue(internProp, val.value()); + return; + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + enumManager->setValue(internProp, val.value()); + return; + } else if (QtSizePolicyPropertyManager *sizePolicyManager = + qobject_cast(manager)) { + sizePolicyManager->setValue(internProp, val.value()); + return; + } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { + fontManager->setValue(internProp, val.value()); + return; +#ifndef QT_NO_CURSOR + } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { + cursorManager->setValue(internProp, val.value()); + return; +#endif + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + flagManager->setValue(internProp, val.value()); + return; + } +} + +/*! + Sets the value of the specified \a attribute of the given \a + property, to \a value. + + The new \a value's type must be of the type returned by + attributeType(), or of a type that can be converted to + attributeType() using the QVariant::canConvert() function, + otherwise this function does nothing. + + \sa attributeValue(), QtVariantProperty::setAttribute(), attributeChanged() +*/ +void QtVariantPropertyManager::setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value) +{ + QVariant oldAttr = attributeValue(property, attribute); + if (!oldAttr.isValid()) + return; + + int attrType = value.userType(); + if (!attrType) + return; + + if (attrType != attributeType(propertyType(property), attribute) && + !value.canConvert((QVariant::Type)attrType)) + return; + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return; + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + intManager->setMaximum(internProp, value.value()); + else if (attribute == d_ptr->m_minimumAttribute) + intManager->setMinimum(internProp, value.value()); + else if (attribute == d_ptr->m_singleStepAttribute) + intManager->setSingleStep(internProp, value.value()); + return; + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + doubleManager->setMaximum(internProp, value.value()); + if (attribute == d_ptr->m_minimumAttribute) + doubleManager->setMinimum(internProp, value.value()); + if (attribute == d_ptr->m_singleStepAttribute) + doubleManager->setSingleStep(internProp, value.value()); + if (attribute == d_ptr->m_decimalsAttribute) + doubleManager->setDecimals(internProp, value.value()); + return; + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_regExpAttribute) + stringManager->setRegExp(internProp, value.value()); + return; + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + dateManager->setMaximum(internProp, value.value()); + if (attribute == d_ptr->m_minimumAttribute) + dateManager->setMinimum(internProp, value.value()); + return; + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_decimalsAttribute) + pointFManager->setDecimals(internProp, value.value()); + return; + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + sizeManager->setMaximum(internProp, value.value()); + if (attribute == d_ptr->m_minimumAttribute) + sizeManager->setMinimum(internProp, value.value()); + return; + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + sizeFManager->setMaximum(internProp, value.value()); + if (attribute == d_ptr->m_minimumAttribute) + sizeFManager->setMinimum(internProp, value.value()); + if (attribute == d_ptr->m_decimalsAttribute) + sizeFManager->setDecimals(internProp, value.value()); + return; + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + rectManager->setConstraint(internProp, value.value()); + return; + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + rectFManager->setConstraint(internProp, value.value()); + if (attribute == d_ptr->m_decimalsAttribute) + rectFManager->setDecimals(internProp, value.value()); + return; + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_enumNamesAttribute) + enumManager->setEnumNames(internProp, value.value()); + if (attribute == d_ptr->m_enumIconsAttribute) + enumManager->setEnumIcons(internProp, value.value()); + return; + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_flagNamesAttribute) + flagManager->setFlagNames(internProp, value.value()); + return; + } +} + +/*! + \reimp +*/ +bool QtVariantPropertyManager::hasValue(const QtProperty *property) const +{ + if (propertyType(property) == groupTypeId()) + return false; + return true; +} + +/*! + \reimp +*/ +QString QtVariantPropertyManager::valueText(const QtProperty *property) const +{ + const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + return internProp ? internProp->valueText() : QString(); +} + +/*! + \reimp +*/ +QIcon QtVariantPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + return internProp ? internProp->valueIcon() : QIcon(); +} + +/*! + \reimp +*/ +void QtVariantPropertyManager::initializeProperty(QtProperty *property) +{ + QtVariantProperty *varProp = variantProperty(property); + if (!varProp) + return; + + QMap::ConstIterator it = + d_ptr->m_typeToPropertyManager.find(d_ptr->m_propertyType); + if (it != d_ptr->m_typeToPropertyManager.constEnd()) { + QtProperty *internProp = 0; + if (!d_ptr->m_creatingSubProperties) { + QtAbstractPropertyManager *manager = it.value(); + internProp = manager->addProperty(); + d_ptr->m_internalToProperty[internProp] = varProp; + } + propertyToWrappedProperty()->insert(varProp, internProp); + if (internProp) { + QList children = internProp->subProperties(); + QListIterator itChild(children); + QtVariantProperty *lastProperty = 0; + while (itChild.hasNext()) { + QtVariantProperty *prop = d_ptr->createSubProperty(varProp, lastProperty, itChild.next()); + lastProperty = prop ? prop : lastProperty; + } + } + } +} + +/*! + \reimp +*/ +void QtVariantPropertyManager::uninitializeProperty(QtProperty *property) +{ + const QMap >::iterator type_it = d_ptr->m_propertyToType.find(property); + if (type_it == d_ptr->m_propertyToType.end()) + return; + + PropertyMap::iterator it = propertyToWrappedProperty()->find(property); + if (it != propertyToWrappedProperty()->end()) { + QtProperty *internProp = it.value(); + if (internProp) { + d_ptr->m_internalToProperty.remove(internProp); + if (!d_ptr->m_destroyingSubProperties) { + delete internProp; + } + } + propertyToWrappedProperty()->erase(it); + } + d_ptr->m_propertyToType.erase(type_it); +} + +/*! + \reimp +*/ +QtProperty *QtVariantPropertyManager::createProperty() +{ + if (!d_ptr->m_creatingProperty) + return 0; + + QtVariantProperty *property = new QtVariantProperty(this); + d_ptr->m_propertyToType.insert(property, qMakePair(property, d_ptr->m_propertyType)); + + return property; +} + +///////////////////////////// + +class QtVariantEditorFactoryPrivate +{ + QtVariantEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtVariantEditorFactory) +public: + + QtSpinBoxFactory *m_spinBoxFactory; + QtDoubleSpinBoxFactory *m_doubleSpinBoxFactory; + QtCheckBoxFactory *m_checkBoxFactory; + QtLineEditFactory *m_lineEditFactory; + QtDateEditFactory *m_dateEditFactory; + QtTimeEditFactory *m_timeEditFactory; + QtDateTimeEditFactory *m_dateTimeEditFactory; + QtKeySequenceEditorFactory *m_keySequenceEditorFactory; + QtCharEditorFactory *m_charEditorFactory; + QtEnumEditorFactory *m_comboBoxFactory; + QtCursorEditorFactory *m_cursorEditorFactory; + QtColorEditorFactory *m_colorEditorFactory; + QtFontEditorFactory *m_fontEditorFactory; + + QMap m_factoryToType; + QMap m_typeToFactory; +}; + +/*! + \class QtVariantEditorFactory + + \brief The QtVariantEditorFactory class provides widgets for properties + created by QtVariantPropertyManager objects. + + The variant factory provides the following widgets for the + specified property types: + + \table + \header + \o Property Type + \o Widget + \row + \o \c int + \o QSpinBox + \row + \o \c double + \o QDoubleSpinBox + \row + \o \c bool + \o QCheckBox + \row + \o QString + \o QLineEdit + \row + \o QDate + \o QDateEdit + \row + \o QTime + \o QTimeEdit + \row + \o QDateTime + \o QDateTimeEdit + \row + \o QKeySequence + \o customized editor + \row + \o QChar + \o customized editor + \row + \o \c enum + \o QComboBox + \row + \o QCursor + \o QComboBox + \endtable + + Note that QtVariantPropertyManager supports several additional property + types for which the QtVariantEditorFactory class does not provide + editing widgets, e.g. QPoint and QSize. To provide widgets for other + types using the variant approach, derive from the QtVariantEditorFactory + class. + + \sa QtAbstractEditorFactory, QtVariantPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtVariantEditorFactory::QtVariantEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtVariantEditorFactoryPrivate(); + d_ptr->q_ptr = this; + + d_ptr->m_spinBoxFactory = new QtSpinBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_spinBoxFactory] = QVariant::Int; + d_ptr->m_typeToFactory[QVariant::Int] = d_ptr->m_spinBoxFactory; + + d_ptr->m_doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_doubleSpinBoxFactory] = QVariant::Double; + d_ptr->m_typeToFactory[QVariant::Double] = d_ptr->m_doubleSpinBoxFactory; + + d_ptr->m_checkBoxFactory = new QtCheckBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_checkBoxFactory] = QVariant::Bool; + d_ptr->m_typeToFactory[QVariant::Bool] = d_ptr->m_checkBoxFactory; + + d_ptr->m_lineEditFactory = new QtLineEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_lineEditFactory] = QVariant::String; + d_ptr->m_typeToFactory[QVariant::String] = d_ptr->m_lineEditFactory; + + d_ptr->m_dateEditFactory = new QtDateEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_dateEditFactory] = QVariant::Date; + d_ptr->m_typeToFactory[QVariant::Date] = d_ptr->m_dateEditFactory; + + d_ptr->m_timeEditFactory = new QtTimeEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_timeEditFactory] = QVariant::Time; + d_ptr->m_typeToFactory[QVariant::Time] = d_ptr->m_timeEditFactory; + + d_ptr->m_dateTimeEditFactory = new QtDateTimeEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_dateTimeEditFactory] = QVariant::DateTime; + d_ptr->m_typeToFactory[QVariant::DateTime] = d_ptr->m_dateTimeEditFactory; + + d_ptr->m_keySequenceEditorFactory = new QtKeySequenceEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_keySequenceEditorFactory] = QVariant::KeySequence; + d_ptr->m_typeToFactory[QVariant::KeySequence] = d_ptr->m_keySequenceEditorFactory; + + d_ptr->m_charEditorFactory = new QtCharEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_charEditorFactory] = QVariant::Char; + d_ptr->m_typeToFactory[QVariant::Char] = d_ptr->m_charEditorFactory; + + d_ptr->m_cursorEditorFactory = new QtCursorEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_cursorEditorFactory] = QVariant::Cursor; + d_ptr->m_typeToFactory[QVariant::Cursor] = d_ptr->m_cursorEditorFactory; + + d_ptr->m_colorEditorFactory = new QtColorEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_colorEditorFactory] = QVariant::Color; + d_ptr->m_typeToFactory[QVariant::Color] = d_ptr->m_colorEditorFactory; + + d_ptr->m_fontEditorFactory = new QtFontEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_fontEditorFactory] = QVariant::Font; + d_ptr->m_typeToFactory[QVariant::Font] = d_ptr->m_fontEditorFactory; + + d_ptr->m_comboBoxFactory = new QtEnumEditorFactory(this); + const int enumId = QtVariantPropertyManager::enumTypeId(); + d_ptr->m_factoryToType[d_ptr->m_comboBoxFactory] = enumId; + d_ptr->m_typeToFactory[enumId] = d_ptr->m_comboBoxFactory; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtVariantEditorFactory::~QtVariantEditorFactory() +{ + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtVariantEditorFactory::connectPropertyManager(QtVariantPropertyManager *manager) +{ + QList intPropertyManagers = manager->findChildren(); + QListIterator itInt(intPropertyManagers); + while (itInt.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itInt.next()); + + QList doublePropertyManagers = manager->findChildren(); + QListIterator itDouble(doublePropertyManagers); + while (itDouble.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itDouble.next()); + + QList boolPropertyManagers = manager->findChildren(); + QListIterator itBool(boolPropertyManagers); + while (itBool.hasNext()) + d_ptr->m_checkBoxFactory->addPropertyManager(itBool.next()); + + QList stringPropertyManagers = manager->findChildren(); + QListIterator itString(stringPropertyManagers); + while (itString.hasNext()) + d_ptr->m_lineEditFactory->addPropertyManager(itString.next()); + + QList datePropertyManagers = manager->findChildren(); + QListIterator itDate(datePropertyManagers); + while (itDate.hasNext()) + d_ptr->m_dateEditFactory->addPropertyManager(itDate.next()); + + QList timePropertyManagers = manager->findChildren(); + QListIterator itTime(timePropertyManagers); + while (itTime.hasNext()) + d_ptr->m_timeEditFactory->addPropertyManager(itTime.next()); + + QList dateTimePropertyManagers = manager->findChildren(); + QListIterator itDateTime(dateTimePropertyManagers); + while (itDateTime.hasNext()) + d_ptr->m_dateTimeEditFactory->addPropertyManager(itDateTime.next()); + + QList keySequencePropertyManagers = manager->findChildren(); + QListIterator itKeySequence(keySequencePropertyManagers); + while (itKeySequence.hasNext()) + d_ptr->m_keySequenceEditorFactory->addPropertyManager(itKeySequence.next()); + + QList charPropertyManagers = manager->findChildren(); + QListIterator itChar(charPropertyManagers); + while (itChar.hasNext()) + d_ptr->m_charEditorFactory->addPropertyManager(itChar.next()); + + QList localePropertyManagers = manager->findChildren(); + QListIterator itLocale(localePropertyManagers); + while (itLocale.hasNext()) + d_ptr->m_comboBoxFactory->addPropertyManager(itLocale.next()->subEnumPropertyManager()); + + QList pointPropertyManagers = manager->findChildren(); + QListIterator itPoint(pointPropertyManagers); + while (itPoint.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itPoint.next()->subIntPropertyManager()); + + QList pointFPropertyManagers = manager->findChildren(); + QListIterator itPointF(pointFPropertyManagers); + while (itPointF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itPointF.next()->subDoublePropertyManager()); + + QList sizePropertyManagers = manager->findChildren(); + QListIterator itSize(sizePropertyManagers); + while (itSize.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itSize.next()->subIntPropertyManager()); + + QList sizeFPropertyManagers = manager->findChildren(); + QListIterator itSizeF(sizeFPropertyManagers); + while (itSizeF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itSizeF.next()->subDoublePropertyManager()); + + QList rectPropertyManagers = manager->findChildren(); + QListIterator itRect(rectPropertyManagers); + while (itRect.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itRect.next()->subIntPropertyManager()); + + QList rectFPropertyManagers = manager->findChildren(); + QListIterator itRectF(rectFPropertyManagers); + while (itRectF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itRectF.next()->subDoublePropertyManager()); + + QList colorPropertyManagers = manager->findChildren(); + QListIterator itColor(colorPropertyManagers); + while (itColor.hasNext()) { + QtColorPropertyManager *manager = itColor.next(); + d_ptr->m_colorEditorFactory->addPropertyManager(manager); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + } + + QList enumPropertyManagers = manager->findChildren(); + QListIterator itEnum(enumPropertyManagers); + while (itEnum.hasNext()) + d_ptr->m_comboBoxFactory->addPropertyManager(itEnum.next()); + + QList sizePolicyPropertyManagers = manager->findChildren(); + QListIterator itSizePolicy(sizePolicyPropertyManagers); + while (itSizePolicy.hasNext()) { + QtSizePolicyPropertyManager *manager = itSizePolicy.next(); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->addPropertyManager(manager->subEnumPropertyManager()); + } + + QList fontPropertyManagers = manager->findChildren(); + QListIterator itFont(fontPropertyManagers); + while (itFont.hasNext()) { + QtFontPropertyManager *manager = itFont.next(); + d_ptr->m_fontEditorFactory->addPropertyManager(manager); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->addPropertyManager(manager->subEnumPropertyManager()); + d_ptr->m_checkBoxFactory->addPropertyManager(manager->subBoolPropertyManager()); + } + + QList cursorPropertyManagers = manager->findChildren(); + QListIterator itCursor(cursorPropertyManagers); + while (itCursor.hasNext()) + d_ptr->m_cursorEditorFactory->addPropertyManager(itCursor.next()); + + QList flagPropertyManagers = manager->findChildren(); + QListIterator itFlag(flagPropertyManagers); + while (itFlag.hasNext()) + d_ptr->m_checkBoxFactory->addPropertyManager(itFlag.next()->subBoolPropertyManager()); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtVariantEditorFactory::createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + const int propType = manager->propertyType(property); + QtAbstractEditorFactoryBase *factory = d_ptr->m_typeToFactory.value(propType, 0); + if (!factory) + return 0; + return factory->createEditor(wrappedProperty(property), parent); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtVariantEditorFactory::createEditor(QtProperty *property, QWidget *parent) +{ + // Overlaoded to avoid "-Woverloaded-virtual" warning + return this->QtAbstractEditorFactory::createEditor(property, parent); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtVariantEditorFactory::disconnectPropertyManager(QtVariantPropertyManager *manager) +{ + QList intPropertyManagers = manager->findChildren(); + QListIterator itInt(intPropertyManagers); + while (itInt.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itInt.next()); + + QList doublePropertyManagers = manager->findChildren(); + QListIterator itDouble(doublePropertyManagers); + while (itDouble.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itDouble.next()); + + QList boolPropertyManagers = manager->findChildren(); + QListIterator itBool(boolPropertyManagers); + while (itBool.hasNext()) + d_ptr->m_checkBoxFactory->removePropertyManager(itBool.next()); + + QList stringPropertyManagers = manager->findChildren(); + QListIterator itString(stringPropertyManagers); + while (itString.hasNext()) + d_ptr->m_lineEditFactory->removePropertyManager(itString.next()); + + QList datePropertyManagers = manager->findChildren(); + QListIterator itDate(datePropertyManagers); + while (itDate.hasNext()) + d_ptr->m_dateEditFactory->removePropertyManager(itDate.next()); + + QList timePropertyManagers = manager->findChildren(); + QListIterator itTime(timePropertyManagers); + while (itTime.hasNext()) + d_ptr->m_timeEditFactory->removePropertyManager(itTime.next()); + + QList dateTimePropertyManagers = manager->findChildren(); + QListIterator itDateTime(dateTimePropertyManagers); + while (itDateTime.hasNext()) + d_ptr->m_dateTimeEditFactory->removePropertyManager(itDateTime.next()); + + QList keySequencePropertyManagers = manager->findChildren(); + QListIterator itKeySequence(keySequencePropertyManagers); + while (itKeySequence.hasNext()) + d_ptr->m_keySequenceEditorFactory->removePropertyManager(itKeySequence.next()); + + QList charPropertyManagers = manager->findChildren(); + QListIterator itChar(charPropertyManagers); + while (itChar.hasNext()) + d_ptr->m_charEditorFactory->removePropertyManager(itChar.next()); + + QList localePropertyManagers = manager->findChildren(); + QListIterator itLocale(localePropertyManagers); + while (itLocale.hasNext()) + d_ptr->m_comboBoxFactory->removePropertyManager(itLocale.next()->subEnumPropertyManager()); + + QList pointPropertyManagers = manager->findChildren(); + QListIterator itPoint(pointPropertyManagers); + while (itPoint.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itPoint.next()->subIntPropertyManager()); + + QList pointFPropertyManagers = manager->findChildren(); + QListIterator itPointF(pointFPropertyManagers); + while (itPointF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itPointF.next()->subDoublePropertyManager()); + + QList sizePropertyManagers = manager->findChildren(); + QListIterator itSize(sizePropertyManagers); + while (itSize.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itSize.next()->subIntPropertyManager()); + + QList sizeFPropertyManagers = manager->findChildren(); + QListIterator itSizeF(sizeFPropertyManagers); + while (itSizeF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itSizeF.next()->subDoublePropertyManager()); + + QList rectPropertyManagers = manager->findChildren(); + QListIterator itRect(rectPropertyManagers); + while (itRect.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itRect.next()->subIntPropertyManager()); + + QList rectFPropertyManagers = manager->findChildren(); + QListIterator itRectF(rectFPropertyManagers); + while (itRectF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itRectF.next()->subDoublePropertyManager()); + + QList colorPropertyManagers = manager->findChildren(); + QListIterator itColor(colorPropertyManagers); + while (itColor.hasNext()) { + QtColorPropertyManager *manager = itColor.next(); + d_ptr->m_colorEditorFactory->removePropertyManager(manager); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + } + + QList enumPropertyManagers = manager->findChildren(); + QListIterator itEnum(enumPropertyManagers); + while (itEnum.hasNext()) + d_ptr->m_comboBoxFactory->removePropertyManager(itEnum.next()); + + QList sizePolicyPropertyManagers = manager->findChildren(); + QListIterator itSizePolicy(sizePolicyPropertyManagers); + while (itSizePolicy.hasNext()) { + QtSizePolicyPropertyManager *manager = itSizePolicy.next(); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->removePropertyManager(manager->subEnumPropertyManager()); + } + + QList fontPropertyManagers = manager->findChildren(); + QListIterator itFont(fontPropertyManagers); + while (itFont.hasNext()) { + QtFontPropertyManager *manager = itFont.next(); + d_ptr->m_fontEditorFactory->removePropertyManager(manager); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->removePropertyManager(manager->subEnumPropertyManager()); + d_ptr->m_checkBoxFactory->removePropertyManager(manager->subBoolPropertyManager()); + } + + QList cursorPropertyManagers = manager->findChildren(); + QListIterator itCursor(cursorPropertyManagers); + while (itCursor.hasNext()) + d_ptr->m_cursorEditorFactory->removePropertyManager(itCursor.next()); + + QList flagPropertyManagers = manager->findChildren(); + QListIterator itFlag(flagPropertyManagers); + while (itFlag.hasNext()) + d_ptr->m_checkBoxFactory->removePropertyManager(itFlag.next()->subBoolPropertyManager()); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtvariantproperty.cpp" diff --git a/src/qtvariantproperty.h b/src/qtvariantproperty.h new file mode 100644 index 0000000..5fb83d6 --- /dev/null +++ b/src/qtvariantproperty.h @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + + +#ifndef QTVARIANTPROPERTY_H +#define QTVARIANTPROPERTY_H + +#include "qtpropertybrowser.h" +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +typedef QMap QtIconMap; + +class QtVariantPropertyManager; +class QtVariantPropertyPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantProperty : public QtProperty +{ +public: + ~QtVariantProperty(); + QVariant value() const; + QVariant attributeValue(const QString &attribute) const; + int valueType() const; + int propertyType() const; + + virtual bool compare(QtProperty* otherProperty)const; + + void setValue(const QVariant &value); + void setAttribute(const QString &attribute, const QVariant &value); +protected: + QtVariantProperty(QtVariantPropertyManager *manager); +private: + friend class QtVariantPropertyManager; + QtVariantPropertyPrivate *d_ptr; +}; + +class QtVariantPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtVariantPropertyManager(QObject *parent = 0); + ~QtVariantPropertyManager(); + + virtual QtVariantProperty *addProperty(int propertyType, const QString &name = QString()); + + void setProperties(QSet properties); + + int propertyType(const QtProperty *property) const; + int valueType(const QtProperty *property) const; + QtVariantProperty *variantProperty(const QtProperty *property) const; + + virtual bool isPropertyTypeSupported(int propertyType) const; + virtual int valueType(int propertyType) const; + virtual QStringList attributes(int propertyType) const; + virtual int attributeType(int propertyType, const QString &attribute) const; + + virtual QVariant value(const QtProperty *property) const; + virtual QVariant attributeValue(const QtProperty *property, const QString &attribute) const; + + static int enumTypeId(); + static int flagTypeId(); + static int groupTypeId(); + static int iconMapTypeId(); +public Q_SLOTS: + virtual void setValue(QtProperty *property, const QVariant &val); + virtual void setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QVariant &val); + void attributeChanged(QtProperty *property, + const QString &attribute, const QVariant &val); +protected: + virtual bool hasValue(const QtProperty *property) const; + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); + virtual QtProperty *createProperty(); +private: + QtVariantPropertyManagerPrivate *d_ptr; + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegExp &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QDate &, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QLocale &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSize &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSize &, const QSize &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizeF &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRect &)) + Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRect &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRectF &)) + Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRectF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QColor &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, const QStringList &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, const QMap &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizePolicy &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QFont &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QCursor &)) + Q_PRIVATE_SLOT(d_func(), void slotFlagNamesChanged(QtProperty *, const QStringList &)) + + Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, QtProperty *)) + Q_DECLARE_PRIVATE(QtVariantPropertyManager) + Q_DISABLE_COPY(QtVariantPropertyManager) +}; + +class QtVariantEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtVariantEditorFactory(QObject *parent = 0); + ~QtVariantEditorFactory(); +protected: + void connectPropertyManager(QtVariantPropertyManager *manager); + QWidget *createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent); + QWidget *createEditor(QtProperty *property, QWidget *parent); + void disconnectPropertyManager(QtVariantPropertyManager *manager); +private: + QtVariantEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtVariantEditorFactory) + Q_DISABLE_COPY(QtVariantEditorFactory) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +Q_DECLARE_METATYPE(QIcon) +Q_DECLARE_METATYPE(QtIconMap) +#endif