commit 2b9f84b72b933a0a82b6607efca673f58b410cdb Author: Abhijit Kundu Date: Mon Dec 1 13:41:34 2014 -0500 First commit 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 0000000..888cb6a Binary files /dev/null and b/doc/images/canvas_typed.png differ diff --git a/doc/images/canvas_variant.png b/doc/images/canvas_variant.png new file mode 100644 index 0000000..900ea8a Binary files /dev/null and b/doc/images/canvas_variant.png differ diff --git a/doc/images/decoration.png b/doc/images/decoration.png new file mode 100644 index 0000000..92db0fc Binary files /dev/null and b/doc/images/decoration.png differ diff --git a/doc/images/demo.png b/doc/images/demo.png new file mode 100644 index 0000000..767e77f Binary files /dev/null and b/doc/images/demo.png differ diff --git a/doc/images/extension.png b/doc/images/extension.png new file mode 100644 index 0000000..e3e508d Binary files /dev/null and b/doc/images/extension.png differ diff --git a/doc/images/object_controller.png b/doc/images/object_controller.png new file mode 100644 index 0000000..e490e6d Binary files /dev/null and b/doc/images/object_controller.png differ diff --git a/doc/images/qt-logo.png b/doc/images/qt-logo.png new file mode 100644 index 0000000..794162f Binary files /dev/null and b/doc/images/qt-logo.png differ diff --git a/doc/images/qtbuttonpropertybrowser.png b/doc/images/qtbuttonpropertybrowser.png new file mode 100644 index 0000000..7890fdb Binary files /dev/null and b/doc/images/qtbuttonpropertybrowser.png differ diff --git a/doc/images/qtgroupboxpropertybrowser.png b/doc/images/qtgroupboxpropertybrowser.png new file mode 100644 index 0000000..203c50f Binary files /dev/null and b/doc/images/qtgroupboxpropertybrowser.png differ diff --git a/doc/images/qtpropertybrowser-duplicate.png b/doc/images/qtpropertybrowser-duplicate.png new file mode 100644 index 0000000..0a939fc Binary files /dev/null and b/doc/images/qtpropertybrowser-duplicate.png differ diff --git a/doc/images/qtpropertybrowser.png b/doc/images/qtpropertybrowser.png new file mode 100644 index 0000000..1f0403a Binary files /dev/null and b/doc/images/qtpropertybrowser.png differ diff --git a/doc/images/qttreepropertybrowser.png b/doc/images/qttreepropertybrowser.png new file mode 100644 index 0000000..154c10d Binary files /dev/null and b/doc/images/qttreepropertybrowser.png differ diff --git a/doc/images/simple.png b/doc/images/simple.png new file mode 100644 index 0000000..56048d5 Binary files /dev/null and b/doc/images/simple.png differ 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 0000000..29d1d44 Binary files /dev/null and b/examples/demo/images/down.png differ diff --git a/examples/demo/images/left.png b/examples/demo/images/left.png new file mode 100644 index 0000000..e58177f Binary files /dev/null and b/examples/demo/images/left.png differ diff --git a/examples/demo/images/right.png b/examples/demo/images/right.png new file mode 100644 index 0000000..34b91f0 Binary files /dev/null and b/examples/demo/images/right.png differ diff --git a/examples/demo/images/up.png b/examples/demo/images/up.png new file mode 100644 index 0000000..e437312 Binary files /dev/null and b/examples/demo/images/up.png differ diff --git a/examples/demo/main.cpp b/examples/demo/main.cpp new file mode 100644 index 0000000..7ec7e9b --- /dev/null +++ b/examples/demo/main.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** 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 "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 0000000..a69ef4e Binary files /dev/null and b/src/images/cursor-arrow.png differ diff --git a/src/images/cursor-busy.png b/src/images/cursor-busy.png new file mode 100644 index 0000000..53717e4 Binary files /dev/null and b/src/images/cursor-busy.png differ diff --git a/src/images/cursor-closedhand.png b/src/images/cursor-closedhand.png new file mode 100644 index 0000000..b78dd1d Binary files /dev/null and b/src/images/cursor-closedhand.png differ diff --git a/src/images/cursor-cross.png b/src/images/cursor-cross.png new file mode 100644 index 0000000..fe38e74 Binary files /dev/null and b/src/images/cursor-cross.png differ diff --git a/src/images/cursor-forbidden.png b/src/images/cursor-forbidden.png new file mode 100644 index 0000000..2b08c4e Binary files /dev/null and b/src/images/cursor-forbidden.png differ diff --git a/src/images/cursor-hand.png b/src/images/cursor-hand.png new file mode 100644 index 0000000..d2004ae Binary files /dev/null and b/src/images/cursor-hand.png differ diff --git a/src/images/cursor-hsplit.png b/src/images/cursor-hsplit.png new file mode 100644 index 0000000..a5667e3 Binary files /dev/null and b/src/images/cursor-hsplit.png differ diff --git a/src/images/cursor-ibeam.png b/src/images/cursor-ibeam.png new file mode 100644 index 0000000..097fc5f Binary files /dev/null and b/src/images/cursor-ibeam.png differ diff --git a/src/images/cursor-openhand.png b/src/images/cursor-openhand.png new file mode 100644 index 0000000..9181c85 Binary files /dev/null and b/src/images/cursor-openhand.png differ diff --git a/src/images/cursor-sizeall.png b/src/images/cursor-sizeall.png new file mode 100644 index 0000000..69f13eb Binary files /dev/null and b/src/images/cursor-sizeall.png differ diff --git a/src/images/cursor-sizeb.png b/src/images/cursor-sizeb.png new file mode 100644 index 0000000..f37d7b9 Binary files /dev/null and b/src/images/cursor-sizeb.png differ diff --git a/src/images/cursor-sizef.png b/src/images/cursor-sizef.png new file mode 100644 index 0000000..3b127a0 Binary files /dev/null and b/src/images/cursor-sizef.png differ diff --git a/src/images/cursor-sizeh.png b/src/images/cursor-sizeh.png new file mode 100644 index 0000000..a9f40cb Binary files /dev/null and b/src/images/cursor-sizeh.png differ diff --git a/src/images/cursor-sizev.png b/src/images/cursor-sizev.png new file mode 100644 index 0000000..1edbab2 Binary files /dev/null and b/src/images/cursor-sizev.png differ diff --git a/src/images/cursor-uparrow.png b/src/images/cursor-uparrow.png new file mode 100644 index 0000000..d3e70ef Binary files /dev/null and b/src/images/cursor-uparrow.png differ diff --git a/src/images/cursor-vsplit.png b/src/images/cursor-vsplit.png new file mode 100644 index 0000000..1beda25 Binary files /dev/null and b/src/images/cursor-vsplit.png differ diff --git a/src/images/cursor-wait.png b/src/images/cursor-wait.png new file mode 100644 index 0000000..69056c4 Binary files /dev/null and b/src/images/cursor-wait.png differ diff --git a/src/images/cursor-whatsthis.png b/src/images/cursor-whatsthis.png new file mode 100644 index 0000000..b47601c Binary files /dev/null and b/src/images/cursor-whatsthis.png differ diff --git a/src/qtbuttonpropertybrowser.cpp b/src/qtbuttonpropertybrowser.cpp new file mode 100644 index 0000000..58cfc51 --- /dev/null +++ b/src/qtbuttonpropertybrowser.cpp @@ -0,0 +1,629 @@ +/**************************************************************************** +** +** 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 "qtbuttonpropertybrowser.h" +#include +#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