diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..a30d946 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,151 @@ +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) +endif() +if (POLICY CMP0011) + cmake_policy(SET CMP0011 NEW) +endif() +#if (POLICY CMP0017) + #cmake_policy(SET CMP0017 OLD) +#endif() +if (POLICY CMP0053) + cmake_policy(SET CMP0053 NEW) +endif() +project(libs) +include(SDKMacros.cmake) +set(LIBPROJECT 1) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/pip/") +include_directories(${CMAKE_CURRENT_BINARY_DIR}/pip) +if(APPLE) + include_directories(/usr/local/include) + link_directories(/usr/local/lib) +endif() +if(MINGW) + find_package(MinGW REQUIRED) +endif() +if (DEFINED ANDROID_PLATFORM) + include_directories(${ANDROID_SYSTEM_LIBRARY_PATH}/usr/include) + #set(TARGET_INSTALL_PATH "${ANDROID_SYSTEM_LIBRARY_PATH}/usr/") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${ANDROID_NDK}/sysroot/usr/include") + #message("${ANDROID_SYSTEM_LIBRARY_PATH}/usr/include") + #message("${ANDROID_NDK}/sysroot/usr/include") +endif() +set(INSTALL_PREFIX "") +if(CMAKE_CROSSCOMPILING) + if (DEFINED ANDROID_PLATFORM) + set(INSTALL_PREFIX "${CMAKE_FIND_ROOT_PATH}/") + else() + set(INSTALL_PREFIX "${CMAKE_STAGING_PREFIX}") + endif() +endif() +option(LIB "System install" 1) +option(DEBUG "Build with -g3" 0) +option(QGLVIEW "Build QGLview library and utils" 1) +option(UTILS "Build various utils" 1) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wall") +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") +if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") +endif () +include(SDKMacros.cmake) + +set(PIP_LIBRARY pip) +set(PIP_FOLDERS "." "core" "containers" "thread" "system" "io_devices" "io_utils" "console" "math" "code" "geo" "resources" "opencl" "crypt" "introspection") +set(PIP_INCLUDES) + +if(LIB) + if(WIN32) + if(MINGW) + set(CMAKE_INSTALL_PREFIX ${MINGW_DIR}) + endif() + else() + if(APPLE) + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}/usr/local) + else() + if (DEFINED ANDROID_PLATFORM) + set(CMAKE_INSTALL_PREFIX ${ANDROID_SYSTEM_LIBRARY_PATH}/usr) + else() + if(CMAKE_CROSSCOMPILING) + set(CMAKE_INSTALL_PREFIX ${CMAKE_STAGING_PREFIX}) + else() + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}/usr) + endif() + endif() + endif() + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() + +foreach(F ${PIP_FOLDERS}) + list(APPEND PIP_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/pip/src_main/${F}") +endforeach(F) +#message(${PIP_INCLUDES}) +if(CMAKE_CROSSCOMPILING OR (DEFINED ANDROID_PLATFORM)) + set(PIP_CMG "pip_cmg") + set(PIP_RC "pip_rc") +else() + set(PIP_CMG "${CMAKE_CURRENT_BINARY_DIR}/pip/utils/code_model_generator/pip_cmg") + set(PIP_RC "${CMAKE_CURRENT_BINARY_DIR}/pip/utils/resources_compiler/pip_rc") +endif() +if(WIN32) + set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}/pip") +endif() + +set(QAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/qad) + +include(qad/QtWraps.cmake) +find_qt(Core QUIET) +set(QtVersions) +set(SomeQtFound 0) +foreach(_v ${_QT_VERSIONS_}) + option(Qt${_v} "Build for Qt${_v}" 1) + if (Qt${_v}) + #message("Qt${_v} -> ${LOCAL_FOUND${_v}}") + if (LOCAL_FOUND${_v}) + list(APPEND QtVersions Qt${_v}) + set(SomeQtFound 1) + endif() + endif() +endforeach() + +if(UTILS) + message(STATUS "Building with utils") +else() + message(STATUS "Building only libraries") +endif() + +add_subdirectory(pip) +set(_DIRS) +if (SomeQtFound) + message(STATUS "Building Qt-derived targets for ${QtVersions}") + add_subdirectory(qad) + set(_DIRS piqt piqt_utils qcd_utils) + if (QGLVIEW) + list(APPEND _DIRS qglview) + endif() + foreach(_D ${_DIRS}) + list(APPEND QT_MULTILIB_LIST ${_D}) + endforeach(_D) +else() + message(STATUS "None of Qt found, skip Qt-derived targets") +endif() +list(INSERT _DIRS 0 cd_utils) +foreach(_D ${_DIRS}) + include_directories(${_D}) + add_subdirectory(${_D}) +endforeach(_D) + +if(WIN32) + foreach(PIP_LT ${PIP_LIBS_TARGETS}) + if (SomeQtFound) + qt_install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pip/lib${PIP_LT}.dll" DESTINATION QtBin) + endif() + endforeach() + set(CMAKES "FindMinGW.cmake") + install(FILES ${CMAKES} DESTINATION ${CMAKE_ROOT}/Modules) +endif() diff --git a/test/FindMinGW.cmake b/test/FindMinGW.cmake new file mode 100644 index 0000000..023de89 --- /dev/null +++ b/test/FindMinGW.cmake @@ -0,0 +1,25 @@ +if(${MINGW}) + if(NOT DEFINED MINGW_DIR) + get_filename_component(MINGW_BIN ${CMAKE_CXX_COMPILER} PATH) + get_filename_component(MINGW_DIR ${MINGW_BIN} PATH) + find_path(MINGW_INCLUDE + windows.h + PATHS + ENV INCLUDE + ${MINGW_DIR} + ${MINGW_DIR}/i686-w64-mingw32 + ${MINGW_DIR}/x86_64-w64-mingw32 + PATH_SUFFIXES + include) + #message(STATUS "Find MinGW = ${MINGW_INCLUDE}") + #if(NOT ${MINGW_INCLUDE}) MINGW_INCLUDE = ${MINGW_DIR}/include + find_library(MINGW_LIB m HINTS ${MINGW_BIN}/../lib ${MINGW_INCLUDE}/../lib) + get_filename_component(MINGW_LIB ${MINGW_LIB} PATH) + if (NOT _MGW_MSG) + set(_MGW_MSG 1 CACHE BOOL "msg_mingw" FORCE) + message(STATUS "Found MinGW binary path = ${MINGW_BIN}") + message(STATUS "Found MinGW include path = ${MINGW_INCLUDE}") + message(STATUS "Found MinGW library path = ${MINGW_LIB}") + endif() + endif() +endif(${MINGW}) diff --git a/test/README.txt b/test/README.txt new file mode 100644 index 0000000..1395f22 --- /dev/null +++ b/test/README.txt @@ -0,0 +1,18 @@ +Build steps for i686 architecture: +1. unzip mingw.7z to C:\mingw +2. unzip qt.7z to C:\qt +3. download and install cmake from https://cmake.org/download/ +2. Prepare environment + - set prefix for mingw e.g.: for C:\mingw\7_1_x32\ + set SDK_MINGW_DIR=C:\mingw\7_1_x + - set prefix for Qt5 e.g.: for C:\qt\qt5120_win32\ + set SDK_QT5_DIR=C:\qt\qt5120_win + - set SDK_CMAKE_DIR=C:\cmake +4. run make_libs32.bat + +Build steps for amd64 architecture: + -- follow previos steps, but replace '32' to '64' + +Build steps for both architectures: + -- follow previos steps, but on '4' state run make_libs_all.bat + \ No newline at end of file diff --git a/test/SDKMacros.cmake b/test/SDKMacros.cmake new file mode 100644 index 0000000..a615860 --- /dev/null +++ b/test/SDKMacros.cmake @@ -0,0 +1,25 @@ + +macro(sdk_install _DIR _TARGET _H_FILES _QM_FILES) + if(LIB) + if(WIN32) + if(NOT "x${_H_FILES}" STREQUAL "x") + #message("HFILES ${_H_FILES}") + install(FILES ${_H_FILES} DESTINATION ${MINGW_INCLUDE}/${_DIR}) + endif() + qt_install(TARGETS ${_TARGET} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${_TARGET} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${_TARGET} DESTINATION QtBin) + qt_install(FILES ${_QM_FILES} DESTINATION QtLang) + else() + if (NOT "x${_H_FILES}" STREQUAL "x") + install(FILES ${_H_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${_DIR}) + endif() + qt_install(TARGETS ${_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + qt_install(FILES ${_QM_FILES} DESTINATION QtLang) + endif() + #message(STATUS "Install ${_TARGET} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${_TARGET} DESTINATION bin) + #message(STATUS "Install ${_TARGET} to local \"bin\"") + endif() +endmacro() diff --git a/test/cd_utils/CMakeLists.txt b/test/cd_utils/CMakeLists.txt new file mode 100644 index 0000000..01f9881 --- /dev/null +++ b/test/cd_utils/CMakeLists.txt @@ -0,0 +1,74 @@ +project(cd_utils) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (NOT LIBPROJECT) + find_package(PIP REQUIRED) + option(LIB "System install" 1) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +if (MINGW) + find_package(MinGW REQUIRED) +endif() +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${PIP_INCLUDES}) +#file(GLOB HDRS_UTILS "*.h") +#file(GLOB CPPS_UTILS "*.cpp") +file(GLOB CPPS_UTILS "cdutils_*.cpp") +file(GLOB HDRS_UTILS "cdutils_*.h") +if (DEFINED ENV{QNX_HOST}) + add_library(${PROJECT_NAME} STATIC ${CPPS_UTILS} ${HDRS_UTILS}) +else() + add_library(${PROJECT_NAME} SHARED ${CPPS_UTILS} ${HDRS_UTILS}) +endif() +target_link_libraries(${PROJECT_NAME} ${PIP_LIBRARY}) + +add_executable(cdutilstest "cdutilstest.cpp" "cdtest.h") +target_link_libraries(cdutilstest ${PIP_LIBRARY} ${PROJECT_NAME}) +message(STATUS "Building ${PROJECT_NAME}") + +if(NOT LIBPROJECT) + if(LIB) + if(WIN32) + if(MINGW) + set(CMAKE_INSTALL_PREFIX ${MINGW_DIR}) + endif() + else() + if(APPLE) + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}/usr/local) + else() + if (DEFINED ANDROID_PLATFORM) + set(CMAKE_INSTALL_PREFIX ${ANDROID_SYSTEM_LIBRARY_PATH}/usr) + else() + if(CMAKE_CROSSCOMPILING) + set(CMAKE_INSTALL_PREFIX ${CMAKE_STAGING_PREFIX}) + else() + set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX}/usr) + endif() + endif() + endif() + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() + +if(LIB) + if(WIN32) + install(FILES ${HDRS_UTILS} DESTINATION ${MINGW_INCLUDE}) + install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + install(FILES ${HDRS_UTILS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) + install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/cd_utils/cdtest.h b/test/cd_utils/cdtest.h new file mode 100644 index 0000000..c2e45d6 --- /dev/null +++ b/test/cd_utils/cdtest.h @@ -0,0 +1,10 @@ +#ifndef CDTEST_H +#define CDTEST_H + + +enum KDescription { + First, //f Первый + Second, //b Второй +}; + +#endif // CDTEST_H diff --git a/test/cd_utils/cdutils_c.cpp b/test/cd_utils/cdutils_c.cpp new file mode 100644 index 0000000..30a5fae --- /dev/null +++ b/test/cd_utils/cdutils_c.cpp @@ -0,0 +1,41 @@ +#include "cdutils_c.h" +#include "cdutils_core.h" + +using namespace CDUtils; + +CInterface C; + + +CInterface::CInterface(): Interface(CDType::cdC) { +} + + +void CInterface::sendCommand(const CDType & c) { + core->sendCommand(c); +} + + +void CInterface::connect(const CDType & c, PIObject * o, Handler eh) { + core->registerCHandler(c, o, eh); +} + + +void CInterface::autoConnect(PIObject * o, const PIString & prefix) { + if (!o) return; + uint cid = o->classNameID(); + if (!PIObject::__meta_data().contains(cid)) return; + PIMap eh_map; + PIObject::__MetaData & md(PIObject::__meta_data()[cid]); + PIMap::const_iterator it; + for (it = md.eh_func.constBegin(); it != md.eh_func.constEnd(); ++it) { + eh_map[it.value().func_name] = (Handler)it.value().addr; + //piCout << "func" << it.value().func_name; + } + PIVector cl = C.root().children(); + piForeachC (CDType * c, cl) { + PIString cp = prefix + c->pathString().join("_"); + if (cp.isEmpty()) continue; + if (!eh_map.contains(cp)) continue; + connect(*c, o, eh_map[cp]); + } +} diff --git a/test/cd_utils/cdutils_c.h b/test/cd_utils/cdutils_c.h new file mode 100644 index 0000000..72590ca --- /dev/null +++ b/test/cd_utils/cdutils_c.h @@ -0,0 +1,26 @@ +#ifndef CDUTILS_C_H +#define CDUTILS_C_H + +#include "cdutils_interface.h" + + +namespace CDUtils { + + +class CInterface: public Interface +{ + PIOBJECT_SUBCLASS(CInterface, Interface) +public: + CInterface(); + + void sendCommand(const CDType & c); + void connect(const CDType & c, PIObject * o, Handler eh); + void autoConnect(PIObject * o, const PIString & prefix = PIStringAscii("c_")); + +}; + +} + +extern CDUtils::CInterface C; + +#endif // CDUTILS_C_H diff --git a/test/cd_utils/cdutils_core.cpp b/test/cd_utils/cdutils_core.cpp new file mode 100644 index 0000000..c03c327 --- /dev/null +++ b/test/cd_utils/cdutils_core.cpp @@ -0,0 +1,606 @@ +#include "cdutils_core.h" +#include "cdutils_parser.h" +#include "piconfig.h" +#include "piiobytearray.h" +#include "piiostring.h" +#include "pifile.h" + +using namespace CDUtils; + +const char CDCore::app_config[] = + "include = cd_ip.conf\n\ + port_rec = 2\n\ + port_send = 1\n\ + [connection]\n\ + device.cd = peer://cd_app:cd_pult #s\n\ + []\n\ + connectionmodel = AAAM2Xja7VXLTttAFD12QpsikKjUSixYlLbKEpIUtVIlVEfqhk2FWuiGRRolUYtoHgrmpYiv6IItf8AveMMH9E/YsG6Ph3sde5hGoQoblLGuPHfunTPjc49nADxDA110+LTYC7FrPCAPeAO+vZu+aX7c/8PGd45WCJC0OGcfT6FDnmfSTPtwhZFt3HjgDs/Qtu5jPbZHtI/x50XfIzMQbdwEolbg9INP4ku++myPaUtCHYRaT2j1ldIh3VP60/Qff8vSfXLu9BP6JX9K/0TVH6jqVe22P1X/fao/oddWu/paDs1vBf9Jv/EZ91clbyHqv7BL6sscDOd4v4WTqs6jzaHGJ8QJerxlpJSpdZ7IWFJvDW7I2JxZqIM62k6A57RZmMQGmlyrxdV+WGBnmR01mXPI267hBKwp4FeBeo9VPtssxyb7rzHg1B7T9nCMU45U8BZlWuVWtIcD/CRGOqtsbW09851tXsHN0UTlLIAdASjSXnLyLn+H7L2+xbGYvC63Ezqg543egkLmn8qnRF6USbM4Qp9godkhzI777Ne5bCIt/5UtGz2o/yGby0nKpjqmbOa1ynkjmyzIrzvIZUeBPjvlUmbh32EFJbGyJZhR8YcvlS+3TpjhqeWSyvUkpbI9plSWtcKLcsK05beOJVEnhaEFfHEH+RwpeMcpn1JKGqWMNOL+G6wZyahlpdVOtufKfbDS+guLke9O\n\ + "; + +const char CDCore::pult_config[] = + "include = cd_ip.conf\n\ + port_rec = 1\n\ + port_send = 2\n\ + [connection]\n\ + device.cd = peer://cd_pult:cd_app #s\n\ + []\n\ + connectionmodel = AAAM2Xja7VXLTttAFD12QpsikKjUSixYlLbKEpIUtVIlVEfqhk2FWuiGRRolUYtoHgrmpYiv6IItf8AveMMH9E/YsG6Ph3sde5hGoQoblLGuPHfunTPjc49nADxDA110+LTYC7FrPCAPeAO+vZu+aX7c/8PGd45WCJC0OGcfT6FDnmfSTPtwhZFt3HjgDs/Qtu5jPbZHtI/x50XfIzMQbdwEolbg9INP4ku++myPaUtCHYRaT2j1ldIh3VP60/Qff8vSfXLu9BP6JX9K/0TVH6jqVe22P1X/fao/oddWu/paDs1vBf9Jv/EZ91clbyHqv7BL6sscDOd4v4WTqs6jzaHGJ8QJerxlpJSpdZ7IWFJvDW7I2JxZqIM62k6A57RZmMQGmlyrxdV+WGBnmR01mXPI267hBKwp4FeBeo9VPtssxyb7rzHg1B7T9nCMU45U8BZlWuVWtIcD/CRGOqtsbW09851tXsHN0UTlLIAdASjSXnLyLn+H7L2+xbGYvC63Ezqg543egkLmn8qnRF6USbM4Qp9godkhzI777Ne5bCIt/5UtGz2o/yGby0nKpjqmbOa1ynkjmyzIrzvIZUeBPjvlUmbh32EFJbGyJZhR8YcvlS+3TpjhqeWSyvUkpbI9plSWtcKLcsK05beOJVEnhaEFfHEH+RwpeMcpn1JKGqWMNOL+G6wZyahlpdVOtufKfbDS+guLke9O\n\ + "; + + +int __Core_Initializer__::count_(0); +CDCore * __Core_Initializer__::__instance__(0); +const uchar header_direct = 0x80; +const uchar header_transfer = 0x81; + + +__Core_Initializer__::__Core_Initializer__() { + count_++; + if (count_ > 1) return; + __instance__ = new CDCore(); +} + + +__Core_Initializer__::~__Core_Initializer__() { + count_--; + if (count_ < 0) { + count_ = 0; + return; + } + if (count_ > 0) return; + if (__instance__) { + delete __instance__; + __instance__ = 0; + } +} + + + + +CDCore::CDCore() { + setName("CDCore"); + x_timer.setName("__S__.CDCore.x_timer"); + datatr.setPacketSize(960); + CONNECTU(&connection, dataReceivedEvent, this, dataReceived); + CONNECTU(PICout::Notifier::object(), finished, this, piCoutFinished); + /*PIString s(app_config); + connection.configureFromString(&s); + connection.start();*/ + need_rebuild_x = x_pult_side = false; + k_.cd_type_ = CDType::cdK; + x_.cd_type_ = CDType::cdX; + c_.cd_type_ = CDType::cdC; + m_.cd_type_ = CDType::cdM; + initRoot(&k_); + initRoot(&x_); + initRoot(&c_); + initRoot(&m_); + + CONNECTU(&sendt, started, this, sendThread) + CONNECTU(&datatr, sendRequest, this, dtSendRequest) + CONNECTU(&datatr, receiveFinished, this, dtReceiveFinished) + CONNECTU(&x_timer, tickEvent, this, xTimerTick) + + /*k_[1] = KType(1, "123", "120+3", "comment"); + k_[2] = KType(2, "1", "2", "comm"); + k_[4] = KType(4, "-0.6", "-6/10", "mment"); + k_.section(10)[5] = KType(5, "8", "2*2*2", "88"); + k_.section(10).section(50)[100] = KType(100, "8", "2*2*2", "88"); + k_.section(11)[3] = KType(3, "1", "1", "88"); + k_.section(11)[4] = KType(4, "0", "0", "88"); + k_.section(11)[6] = KType(6, "0", "0", "88");*/ + //piCout << s; +} + + +CDCore::~CDCore() { + x_timer.stop(true); + datatr.stop(); + sendt.stop(); + sendt.waitForFinish(10); + connection.stop(); +} + + +void CDCore::cd_write(CDSection * cd, PIIODevice * d) { + cd->write(d, PIString()); +} + + +void CDCore::cd_read(CDSection * cd, PIIODevice * d) { + PIConfig conf(d, PIIODevice::ReadOnly); + cd->read(&(conf.rootEntry())); + if (cd->cd_type_ == CDType::cdX) + x_selected = cd->collectX(); + initRoot(cd); + raiseChangedGlobal(cd->cd_type_); + /*PIVector ds = connection.allDevices(); + piForeach(PIIODevice * d, ds) { + if (d) + piCoutObj << d->constructFullPath() << d->isOpened(); + }*/ +} + + +void CDCore::cd_parse(CDSection * cd, PIIODevice * d) { + *cd = CDParser::parse(d, cd->cd_type_); + initRoot(cd); + raiseChangedGlobal(cd->cd_type_); +} + + +void CDCore::cd_update(CDSection * cd, PIIODevice * d, UpdateModeFlags mode) { + CDSection ucd = *cd; + cd_parse(cd, d); + /*bool kn = true; + if (!ucd.isEmpty()) + if (!ucd.isSameStructure(k_)) { + piCout << "ask for save names"; + K_KeepNamesRequest(&kn); + }*/ + ucd.update(*cd, mode); + //piCout << k_.count() << ucd.count(); + *cd = ucd; + initRoot(cd); + raiseChangedGlobal(cd->cd_type_); +} + + +void CDCore::cd_calculate(CDSection * cd) { + cd->calculate(); + raiseChangedGlobal(cd->cd_type_); +} + + +void CDCore::cd_send(CDSection * cd, CDPacketType pt, bool direct) { + if (!cd) return; + PIByteArray ba, sba; + PIIOByteArray iob(&ba, PIIODevice::ReadWrite); + cd_write(cd, &iob); + //piCoutObj << PIString(ba); + sba = makeHeader(pt, 0); + sba << ba; + if (direct) + sendDirect(sba); + else + sendThreaded(sba); +} + + +void CDCore::send(CDType::cdT cdt) { + CDPacketType pt = CD_Ping; + switch (cdt) { + case CDType::cdK: pt = CD_KSend; break; + case CDType::cdX: pt = CD_XSend; break; + case CDType::cdC: pt = CD_CSend; break; + case CDType::cdM: pt = CD_MSend; break; + default: break; + } + piCoutObj << "send" << typeLetter(cdt); + cd_send(root(cdt), pt); +} + + +void CDCore::request(CDType::cdT cdt) { + CDPacketType pt = CD_Ping; + switch (cdt) { + case CDType::cdK: pt = CD_KQuery; break; + case CDType::cdX: pt = CD_XQuery; break; + case CDType::cdC: pt = CD_CQuery; break; + case CDType::cdM: pt = CD_MQuery; break; + default: break; + } + piCoutObj << "request" << typeLetter(cdt); + PIByteArray sba = makeHeader(pt, 0); + sendThreaded(sba); +} + + +void CDCore::initApp() { + init(appConfig(), false); +} + + +void CDCore::initPult() { + init(pultConfig(), true); +} + + +void CDCore::init(const PIString & configuration, bool pult) { + PIString c = configuration; + //piCoutObj << "init" << c; + connection.stop(); + connection.removeAllDevices(); + connection.configureFromString(&c); + connection.start(); + x_pult_side = pult; +} + + +void CDCore::stop() { + x_timer.stop(); + x_timer.waitForFinish(1000); + connection.stop(); +} + + +void CDCore::release() { + stop(); + connection.removeAllDevices(); +} + + +void CDCore::startX(double freq) { + //piCout << "start x" << x_timer.isRunning() << freq; + if (!x_timer.isRunning()) + x_timer.start(1000. / piMaxd(freq, 0.01)); +} + + +void CDCore::stopX() { + x_timer.stop(); + x_timer.waitForFinish(1000); +} + + +void CDCore::sendCommand(const CDType & c) { + //piCoutObj << "C_sendCommand" << c; + PIByteArray sba = makeHeader(CD_Command, 0); + sba << c.path(); + sendDirect(sba); +} + + +void CDCore::registerCHandler(const CDType & c, PIObject * o, Handler h) { + PIString sp = pathToString(c.path()); + if (sp.isEmpty() || !h) return; + //piCout << "register" << sp; + c_handlers[sp] = OHPair(o, h); +} + + +void CDCore::sendMessage(const CDType & m, MessageType mt, const PIString & msg) { + if (msg.isEmpty() || (m.cd_type() != CDType::cdM)) return; + PIByteArray sba = makeHeader(CD_Message, 0); + sba << m.path() << int(mt) << msg; + sendDirect(sba); +} + + +CDSection * CDCore::root(CDType::cdT cdt) { + switch (cdt) { + case CDType::cdK: return &k_; break; + case CDType::cdX: return &x_; break; + case CDType::cdC: return &c_; break; + case CDType::cdM: return &m_; break; + default: break; + } + return 0; +} + + +PIString CDCore::typeLetter(CDType::cdT cdt) { + switch (cdt) { + case CDType::cdK: return PIStringAscii("k"); break; + case CDType::cdX: return PIStringAscii("x"); break; + case CDType::cdC: return PIStringAscii("c"); break; + case CDType::cdM: return PIStringAscii("m"); break; + default: break; + } + return PIString(); +} + + +CDCore * CDCore::instance() { + /*static CDCore * ret = new CDCore(); + return ret;*/ + return __Core_Initializer__::__instance__; +} + + +bool CDCore::destroy() { + if (!__Core_Initializer__::__instance__) return false; +// piCout << "delete Core ..."; + delete __Core_Initializer__::__instance__; +// piCout << "delete Core ok"; + __Core_Initializer__::__instance__ = 0; + __Core_Initializer__::count_ = 0; + return true; +} + + +void CDUtils::CDCore::K_DirectChange(PIDeque path, PIString value) { +// piCoutObj << "K_DirectChange"; + PacketKDirectChange p; + p.path = path; + p.value = value; + PIByteArray sba = makeHeader(CD_KDirectChange, 0); + sba << p; + sendDirect(sba); +} + + +void CDCore::sendThread() { + if (send_data.size_s() < 4) return; + PacketHeader h; + memcpy(&h, send_data.data(), sizeof(h)); + bool ok = datatr.send(send_data); + switch (h.type) { + case CD_KSend: + if (ok) K_Sended(); + else K_SendFail(); + break; + case CD_KQuery: + if (!ok) K_ReceiveFail(); + break; + case CD_XSend: + if (ok) X_Sended(); + else X_SendFail(); + break; + case CD_XQuery: + if (!ok) X_ReceiveFail(); + break; + case CD_CSend: + if (ok) C_Sended(); + else C_SendFail(); + break; + case CD_CQuery: + if (!ok) C_ReceiveFail(); + break; + case CD_MSend: + if (ok) M_Sended(); + else M_SendFail(); + break; + case CD_MQuery: + if (!ok) M_ReceiveFail(); + break; + default: break; + } +} + + +void CDCore::xTimerTick() { + //piCout << "x tick" << x_pult_side; + PIByteArray ba; + x_mutex.lock(); + if (x_pult_side) { + ba = makeHeader(CD_XRequest, 0); + if (need_rebuild_x) { + x_selected = x_.collectX(); + //piCout << "collectX" << x_selected.size(); + need_rebuild_x = false; + } + ba << x_selected; + //piCout << "x pult send" << x_selected.size(); + } else { + ba = makeHeader(CD_XValues, 0); + ba << x_selected; + piForeachC (PIDeque & p, x_selected) { + x_[p].writeX(ba); + } + //piCout << "x app" << x_selected.size(); + } + x_mutex.unlock(); + sendDirect(ba); +} + + +void CDCore::piCoutFinished(int id, PIString * buffer) { + if (!buffer || !(id == 1)) return; + PIString sp = buffer->takeRange("[", "]"); + PIDeque p = CDCore::stringToPath(sp); + sendMessage(m_[p], Log, *buffer); + delete buffer; +} + + +void CDCore::initRoot(CDSection * r) { + r->name = "__root__"; + r->alias = "root"; + r->makePath(); + r->calculate(); +} + + +PIByteArray CDCore::makeHeader(CDPacketType type, int session_id) const { + PacketHeader h; + h.type = type; + h.session_id = session_id; + PIByteArray ret; ret << h; + return ret; +} + + +void CDCore::sendDirect(PIByteArray & ba) { + ba.push_front(header_direct); + connection.writeByName("cd", ba); +} + + +void CDCore::sendThreaded(PIByteArray & ba) { + if (sendt.isRunning()) { + piCoutObj << "Send in process, abort"; + return; + } + send_data = ba; + sendt.startOnce(); +} + + +void CDCore::procReceivedPacket(PIByteArray & ba) { + PacketHeader h; + ba >> h; + switch(h.type) { + case CD_Ping: + //piCoutObj << "ping"; + break; + case CD_KQuery: + send(CDType::cdK); + break; + case CD_KSend: { + PIByteArray k; + ba >> k; + k << uchar(0); + PIString s = PIString::fromUTF8((const char *)k.data()); + PIIOString ios(&s); + cd_read(&k_, &ios); + K_Received(); + piCoutObj << "K received"; + } break; + case CD_KDirectChange: { + PacketKDirectChange p; + ba >> p; + k_[p.path].setValue(p.value); + } break; + case CD_XQuery: + send(CDType::cdX); + break; + case CD_XSend: { + PIByteArray x; + ba >> x; + x << uchar(0); + PIString s = PIString::fromUTF8((const char *)x.data()); + PIIOString ios(&s); + cd_read(&x_, &ios); + x_selected = x_.collectX(); + X_Received(); + piCoutObj << "X received"; + } break; + case CD_XRequest: { + if (x_pult_side) break; + //break; + x_mutex.lock(); + x_selected.clear(); + ba >> x_selected; + //piCout << "X req" << x_selected.size(); + x_.setSelectedX(false); + piForeachC (PIDeque & p, x_selected) { + x_[p].x_enabled = true; + } + x_mutex.unlock(); + } break; + case CD_XValues: { + if (!x_pult_side) break; + PIVector > x_vals; + ba >> x_vals; + x_mutex.lock(); + piForeachC (PIDeque & p, x_vals) { + x_[p].readX(ba); + } + x_mutex.unlock(); + X_ReceivedX(x_vals); + } break; + case CD_CQuery: + send(CDType::cdC); + break; + case CD_CSend: { + piCoutObj << "C received"; + PIByteArray c; + ba >> c; + c << uchar(0); + PIString s = PIString::fromUTF8((const char *)c.data()); + PIIOString ios(&s); + cd_read(&c_, &ios); + C_Received(); + } break; + case CD_Command: { + piCoutObj << "C command"; + PIDeque p; + ba >> p; + if (p.isEmpty()) return; + PIString sp = pathToString(p); + OHPair h = c_handlers.value(sp, OHPair(0, 0)); + //piCoutObj << "found" << sp << h.first; + if (h.first && h.second) h.second(h.first); + } break; + case CD_MQuery: + send(CDType::cdM); + break; + case CD_MSend: { + piCoutObj << "M received"; + PIByteArray c; + ba >> c; + c << uchar(0); + PIString s = PIString::fromUTF8((const char *)c.data()); + PIIOString ios(&s); + cd_read(&m_, &ios); + M_Received(); + } break; + case CD_Message: { + PIDeque p; + ba >> p; + piCoutObj << "M message" << p; + if (p.isEmpty()) return; + int t = 0; + PIString msg; + ba >> t >> msg; + //piCoutObj << "found" << sp << h.first; + //piCoutObj << "M message invoke"; + M_Message(p, t, msg); + } break; + default: break; + } +} + + +void CDCore::raiseChangedGlobal(CDType::cdT cdt) { + switch (cdt) { + case CDType::cdK: K_ChangedGlobal(); break; + case CDType::cdX: X_ChangedGlobal(); break; + case CDType::cdC: C_ChangedGlobal(); break; + case CDType::cdM: M_ChangedGlobal(); break; + default: break; + } +} + + +PIString CDCore::pathToString(const PIDeque & p) { + PIString ret; + for (int i = 0; i < p.size_s(); ++i) { + if (!ret.isEmpty()) ret += "."; + ret << p[i]; + } + return ret; +} + + +PIDeque CDCore::stringToPath(const PIString & p) { + PIDeque ret; + PIStringList sl = p.split("."); + piForeachC (PIString & s, sl) + ret << s.toInt(); + return ret; +} + + +void CDUtils::CDCore::dataReceived(const PIString & from, const PIByteArray & data) { + //piCoutObj << "dataReceived" << from << data.size(); + PIIODevice * d = connection.deviceByName("cd"); + if (d && d == connection.deviceByFullPath(from)) { + if (data.size() >= sizeof(4)) { + PIByteArray ba = data; + uchar header = ba.take_front(); + if (header == header_transfer) { + datatr.received(ba); + } + if (header == header_direct) { + procReceivedPacket(ba); + } + } + } +} + + +void CDCore::dtSendRequest(PIByteArray & data) { + data.push_front(header_transfer); + connection.writeByName("cd", data); + //piCoutObj << "send" << data.size() << ret; +} + + +void CDCore::dtReceiveFinished(bool ok) { + if (!ok) return; + PIByteArray ba = datatr.data(); + procReceivedPacket(ba); +} + diff --git a/test/cd_utils/cdutils_core.h b/test/cd_utils/cdutils_core.h new file mode 100644 index 0000000..ab26bcb --- /dev/null +++ b/test/cd_utils/cdutils_core.h @@ -0,0 +1,127 @@ +#ifndef CDUTILS_CORE_H +#define CDUTILS_CORE_H + +#include "cdutils_types.h" +#include "cdutils_protocol.h" +#include "piconnection.h" +#include "pidatatransfer.h" + + +namespace CDUtils { + +class CDCore; + + +class __Core_Initializer__ { +public: + __Core_Initializer__(); + ~__Core_Initializer__(); + static int count_; + static CDCore * __instance__; +}; + + +class CDCore: public PIObject +{ + PIOBJECT(CDUtils::CDCore) + friend class __Core_Initializer__; + friend class CDSection; + friend class Interface; + friend class XInterface; +public: + static CDCore * instance(); + static bool destroy(); + + EVENT(K_Sended) + EVENT(K_SendFail) + EVENT(K_Received) + EVENT(K_ReceiveFail) + EVENT(K_ChangedGlobal) + EVENT_HANDLER2(void, K_DirectChange, PIDeque, path, PIString, value); + + EVENT(X_Sended) + EVENT(X_SendFail) + EVENT(X_Received) + EVENT(X_ReceiveFail) + EVENT(X_ChangedGlobal) + EVENT1(X_ReceivedX, PIVector >, pathes) + + EVENT(C_Sended) + EVENT(C_SendFail) + EVENT(C_Received) + EVENT(C_ReceiveFail) + EVENT(C_ChangedGlobal) + + EVENT(M_Sended) + EVENT(M_SendFail) + EVENT(M_Received) + EVENT(M_ReceiveFail) + EVENT(M_ChangedGlobal) + EVENT3(M_Message, PIDeque, path, int, type, PIString, msg) + + void cd_write (CDSection * cd, PIIODevice * d); + void cd_read (CDSection * cd, PIIODevice * d); + void cd_parse (CDSection * cd, PIIODevice * d); + void cd_update (CDSection * cd, PIIODevice * d, UpdateModeFlags mode); + void cd_calculate(CDSection * cd); + void cd_send (CDSection * cd, CDPacketType pt, bool direct = false); + void send(CDType::cdT cdt); + void request(CDType::cdT cdt); + void initApp(); + void initPult(); + void init(const PIString & configuration, bool pult = false); + void stop(); + void release(); + void startX(double freq = 20.); + void stopX(); + void sendCommand(const CDType & c); + void registerCHandler(const CDType & c, PIObject * o, Handler h); + bool inProgress() {return sendt.isRunning();} + void sendMessage(const CDType & m, MessageType mt, const PIString & msg); + + CDSection * root(CDType::cdT cdt); + PIString typeLetter(CDType::cdT cdt); + static PIString pathToString(const PIDeque & p); + static PIDeque stringToPath(const PIString & p); + + static PIString pultConfig() {return PIString(pult_config);} + static PIString appConfig() {return PIString(app_config);} + +private: + CDCore(); + ~CDCore(); + EVENT_HANDLER2(void, dataReceived, const PIString &, from, const PIByteArray &, data); + EVENT_HANDLER1(void, dtSendRequest, PIByteArray &, data); + EVENT_HANDLER1(void, dtReceiveFinished, bool, ok); + EVENT_HANDLER(void, sendThread); + EVENT_HANDLER(void, xTimerTick); + EVENT_HANDLER2(void, piCoutFinished, int, id, PIString*, buffer); + void initRoot(CDSection * r); + PIByteArray makeHeader(CDPacketType type, int session_id = 0) const; + void sendDirect(PIByteArray & ba); + void sendThreaded(PIByteArray & ba); + void procReceivedPacket(PIByteArray & ba); + void raiseChangedGlobal(CDType::cdT cdt); + + typedef PIPair OHPair; + + static const char app_config[], pult_config[]; + PIConnection connection; + PIDataTransfer datatr; + PIByteArray send_data; + PIThread sendt; + PITimer x_timer; + CDSection k_, x_, c_, m_; + PIMutex x_mutex; + PIVector > x_selected; + PIMap c_handlers; + bool need_rebuild_x, x_pult_side; + +}; + + +static __Core_Initializer__ __Core_initializer__; + +} + +#endif // CDUTILS_CORE_H diff --git a/test/cd_utils/cdutils_interface.cpp b/test/cd_utils/cdutils_interface.cpp new file mode 100644 index 0000000..2967801 --- /dev/null +++ b/test/cd_utils/cdutils_interface.cpp @@ -0,0 +1,217 @@ +#include "cdutils_interface.h" +#include "cdutils_core.h" +#include "piconfig.h" +#include "pifile.h" + +using namespace CDUtils; + + +Interface::Interface(CDType::cdT type_) { + core = CDCore::instance(); + s = core->root(type_); + type = type_; + //piCoutObj << (void*)this << core; + file_ = core->typeLetter(type_) + PIStringAscii(".dat"); + file_size = 0; + switch (type) { + case CDType::cdK: + CONNECTU(core, K_Sended, this, sended); + CONNECTU(core, K_SendFail, this, sendFailed); + CONNECTU(core, K_Received, this, received); + CONNECTU(core, K_ReceiveFail, this, receiveFailed); + CONNECTU(core, K_ChangedGlobal, this, changedGlobal); + break; + case CDType::cdX: + CONNECTU(core, X_Sended, this, sended); + CONNECTU(core, X_SendFail, this, sendFailed); + CONNECTU(core, X_Received, this, received); + CONNECTU(core, X_ReceiveFail, this, receiveFailed); + CONNECTU(core, X_ChangedGlobal, this, changedGlobal); + break; + case CDType::cdC: + CONNECTU(core, C_Sended, this, sended); + CONNECTU(core, C_SendFail, this, sendFailed); + CONNECTU(core, C_Received, this, received); + CONNECTU(core, C_ReceiveFail, this, receiveFailed); + CONNECTU(core, C_ChangedGlobal, this, changedGlobal); + break; + case CDType::cdM: + CONNECTU(core, M_Sended, this, sended); + CONNECTU(core, M_SendFail, this, sendFailed); + CONNECTU(core, M_Received, this, received); + CONNECTU(core, M_ReceiveFail, this, receiveFailed); + CONNECTU(core, M_ChangedGlobal, this, changedGlobal); + break; + default: break; + } +} + + +bool Interface::test(int v) { + return s->test(v); +} + + +CDType & Interface::operator [](const PIString & name_) { + return (*s)[name_]; +} + + +const CDType Interface::operator [](const PIString & name_) const { + return (*s)[name_]; +} + + +CDType & Interface::operator [](const PIDeque & path_) { + return (*s)[path_]; +} + + +const CDType Interface::operator [](const PIDeque & path_) const { + return (*s)[path_]; +} + + +CDType & Interface::operator [](int v) { + //piCout << (void*)this << "[]" << core; + return (*s)[v]; +} + + +const CDType Interface::operator [](int v) const { + //piCout << (void*)this << "[]" << core; + return (*s)[v]; +} + + +CDSection & Interface::section(int v) { +// CDSection & ret = s->section(v); +// piCout << "[get section]" << v << ret.name; + return s->section(v); +} + + +const CDSection Interface::section(int v) const { + return s->section(v); +} + + +CDSection & Interface::section(const PIDeque &path) { + PIDeque spath = path; + CDSection * rs = s; + while (!spath.isEmpty()) { + rs = &(rs->section(spath.take_front())); + } + return *rs; +} + + +CDSection & Interface::root() { + return *s; +} + + +const CDSection & Interface::root() const { + return *s; +} + + +int Interface::count(bool recursive) const { + return s->count(recursive); +} + + +bool Interface::exists(PIDeque path) const { + return s->exists(path); +} + + +void Interface::setFileName(const PIString & _file) { + file_ = _file; +} + + +bool Interface::configure(const PIString & config) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry & e(conf.getValue(core->typeLetter(s->cd_type_))); + bool ret = false; + setFileName(e.getValue("file", file(), &ret)); + return ret; +} + + +void Interface::reinitConnection(const PIString & configuration) { + core->init(configuration); +} + + +void Interface::releaseConnection() { + core->release(); +} + + +void Interface::write(PIIODevice * d) { + core->cd_write(s, d); +} + + +void Interface::read(PIIODevice * d) { + core->cd_read(s, d); +} + + +void Interface::parse(PIIODevice * d) { + core->cd_parse(s, d); +} + + +void Interface::update(PIIODevice * d, UpdateModeFlags mode) { + core->cd_update(s, d, mode); +} + + +void Interface::calculate() { + core->cd_calculate(s); +} + + +PIString Interface::appConfig() { + return core->appConfig(); +} + + +PIString Interface::pultConfig() { + return core->pultConfig(); +} + + +void Interface::readFile() { + if (file_.isEmpty()) return; + PIFile f(file_, PIIODevice::ReadOnly); + read(&f); + file_size = f.size(); +} + + +void Interface::writeFile() { + if (file_.isEmpty()) return; + PIFile f(file_, PIIODevice::ReadWrite); + f.clear(); + write(&f); + file_size = f.size(); +} + + +bool Interface::inProgress() { + return core->inProgress(); +} + + +void Interface::send() { + core->send(type); +} + + +void Interface::request() { + core->request(type); +} diff --git a/test/cd_utils/cdutils_interface.h b/test/cd_utils/cdutils_interface.h new file mode 100644 index 0000000..c44a005 --- /dev/null +++ b/test/cd_utils/cdutils_interface.h @@ -0,0 +1,76 @@ +#ifndef CDUTILS_INTERFACE_H +#define CDUTILS_INTERFACE_H + +#include "cdutils_types.h" +#include "piobject.h" + + +namespace CDUtils { + +class CDCore; + + +class Interface: public PIObject +{ + PIOBJECT(CDUtils::Interface) +public: + Interface(CDType::cdT type_); + + bool test(int v); + CDType & operator [](int v); + const CDType operator [](int v) const; + CDType & operator [](const PIString & name_); + const CDType operator [](const PIString & name_) const; + CDType & operator [](const PIDeque & path_); + const CDType operator [](const PIDeque & path_) const; + CDSection & section(int v); + const CDSection section(int v) const; + CDSection & section(const PIDeque & path); + CDSection & root(); + const CDSection & root() const; + + int count(bool recursive = true) const; + const PIString file() const {return file_;} + int fileSize() const {return file_size;} + CDType::cdT cdType() const {return type;} + bool exists(PIDeque path) const; + + void setFileName(const PIString & _file); + bool configure(const PIString & config); + void reinitConnection(const PIString & configuration); + void releaseConnection(); + void write(PIIODevice * d); + void read(PIIODevice * d); + void parse(PIIODevice * d); + void update(PIIODevice * d, UpdateModeFlags mode = SaveByName); + void calculate(); + + PIString appConfig(); + PIString pultConfig(); + + void readFile(); + void writeFile(); + bool inProgress(); + + EVENT(sended) + EVENT(sendFailed) + EVENT(received) + EVENT(receiveFailed) + EVENT(changedGlobal) + EVENT_HANDLER(void, send); + EVENT_HANDLER(void, request); + +protected: + CDCore * core; + CDSection * s; + CDType::cdT type; + PIString file_; + int file_size; + +}; + + +} + + +#endif // CDUTILS_INTERFACE_H diff --git a/test/cd_utils/cdutils_k.cpp b/test/cd_utils/cdutils_k.cpp new file mode 100644 index 0000000..6ee850a --- /dev/null +++ b/test/cd_utils/cdutils_k.cpp @@ -0,0 +1,20 @@ +#include "cdutils_k.h" +#include "cdutils_core.h" + +using namespace CDUtils; + +KInterface K; + + +KInterface::KInterface(): Interface(CDType::cdK) { +} + + +void KInterface::directChange(const CDType & k) { + core->K_DirectChange(k.path(), k.value()); +} + + +void KInterface::directChange(const CDType & k, double v) { + core->K_DirectChange(k.path(), PIString::fromNumber(v)); +} diff --git a/test/cd_utils/cdutils_k.h b/test/cd_utils/cdutils_k.h new file mode 100644 index 0000000..b49e697 --- /dev/null +++ b/test/cd_utils/cdutils_k.h @@ -0,0 +1,27 @@ +#ifndef CDUTILS_K_H +#define CDUTILS_K_H + +#include "cdutils_interface.h" + + +namespace CDUtils { + + +class KInterface: public Interface +{ + PIOBJECT_SUBCLASS(KInterface, Interface) +public: + KInterface(); + + EVENT1(keepNamesRequest, bool*, kn) + + void directChange(const CDType & k); + void directChange(const CDType & k, double v); + +}; + +} + +extern CDUtils::KInterface K; + +#endif // CDUTILS_K_H diff --git a/test/cd_utils/cdutils_m.cpp b/test/cd_utils/cdutils_m.cpp new file mode 100644 index 0000000..cd326cd --- /dev/null +++ b/test/cd_utils/cdutils_m.cpp @@ -0,0 +1,22 @@ +#include "cdutils_m.h" +#include "cdutils_core.h" + +using namespace CDUtils; + +MInterface M; + + +MInterface::MInterface(): Interface(CDType::cdM) { + CONNECTU(core, M_Message, this, messageReceived); +} + + +void MInterface::messageBox(const CDType & m, const PIString & msg) { + core->sendMessage(m, MessageBox, msg); +} + + +PICout MInterface::createPICout(const CDType & m) const { + PIString * buff = new PIString("[" + CDCore::pathToString(m.path()) + "]"); + return PICout(buff, 1); +} diff --git a/test/cd_utils/cdutils_m.h b/test/cd_utils/cdutils_m.h new file mode 100644 index 0000000..cbc73e8 --- /dev/null +++ b/test/cd_utils/cdutils_m.h @@ -0,0 +1,36 @@ +#ifndef CDUTILS_M_H +#define CDUTILS_M_H + +#include "cdutils_interface.h" + + +namespace CDUtils { + + +class MInterface: public Interface +{ + PIOBJECT_SUBCLASS(MInterface, Interface) +public: + MInterface(); + + PICout operator [](int v) {return createPICout((*s)[v]);} + PICout operator [](int v) const {return createPICout((*s)[v]);} + PICout operator [](const PIString & name_) {return createPICout((*s)[name_]);} + PICout operator [](const PIString & name_) const {return createPICout((*s)[name_]);} + PICout operator [](const PIDeque & path_) {return createPICout((*s)[path_]);} + PICout operator [](const PIDeque & path_) const {return createPICout((*s)[path_]);} + + void messageBox(const CDType & m, const PIString & msg); + + EVENT3(messageReceived, PIDeque, path, int, type, PIString, msg) + +private: + PICout createPICout(const CDType & m) const; + +}; + +} + +extern CDUtils::MInterface M; + +#endif // CDUTILS_M_H diff --git a/test/cd_utils/cdutils_parser.cpp b/test/cd_utils/cdutils_parser.cpp new file mode 100644 index 0000000..50e0600 --- /dev/null +++ b/test/cd_utils/cdutils_parser.cpp @@ -0,0 +1,196 @@ +#include "cdutils_parser.h" +#include "cdutils_types.h" +#include "piiostring.h" +#include "piiobytearray.h" +#include "pifile.h" + +using namespace CDUtils; + +enum Phase { + eName = 1, + eBracketOpen, + eBracketClose, + eMemberName, + eMemberEqual, + eMemberValue, + eMemberComma, + eComment, + eMultiComment +}; + + +void removeComment(PIString & line, PIString * type, PIString * comment) { + int ci = line.find("//"); + if (ci >= 0) { + if (comment) *comment = line.right(line.size_s() - ci - 2); + line.cutRight(line.size_s() - ci).trim(); + if (type && comment && !line.isEmpty()) { + *type = comment->takeLeft(1); + comment->trim(); + } + } +} + + +void parseEnumLine(PIString & line, int * value, PIString * type, PIString * comment) { + removeComment(line, type, comment); + int ci = line.find("="); + if (ci >= 0) { + if (value) *value = line.right(line.size_s() - ci - 1).trim().toInt(); + line.cutRight(line.size_s() - ci).trim(); + } + if (line.trim().endsWith(",")) line.cutRight(1); +} + + +void parseInsert(PIString line, PIString & alias, PIStringList & out) { + out.clear(); + int ci = line.find("="); + if (ci < 0) return; + alias = line.right(line.size_s() - ci - 1).trim(); + line.cutRight(line.size_s() - ci).trim(); + while (line.find("[") > 0) { + int is = line.find("["), ie = line.find("]"); + PIString arr = line.mid(is + 1, ie - is - 1); + out << arr; + line.cutMid(is, ie - is + 1); + } + if (!line.isEmpty()) out.insert(0, line); +} + + +PIVector enumValues(const PIString & e, const PIMap & sections, PIStringList & enames) { + PIVector ret; + enames.clear(); + if (sections.contains(e)) { + ret = sections[e].indexes(); + enames = sections[e].index_names(); + } else { + int v = e.toInt(); + if (v < 2) return ret; + for (int i = 0; i < v; ++i) { + ret << i; + enames << "";//PIString::fromNumber(i); + } + } + return ret; +} + + +CDSection CDParser::parse(PIIODevice * d, int cdsection_type) { + CDType::cdT et = (CDType::cdT)cdsection_type; + if (!d) return CDSection(et); + if (!d->canRead()) return CDSection(et); + //piCout << "[CDSection] parse start"; + CDSection cs(et); + CDType ck; + PIMap sections; + PIMap enum_values; + PIString content, line, alias, type, comment; + PIStringList iarr; + if (PIStringAscii(d->className()) == PIStringAscii("PIFile")) { + PIByteArray c = ((PIFile*)d)->readAll(); + c << uchar(0); + content = PIString::fromUTF8((const char *)c.data()); + } + if (PIStringAscii(d->className()) == PIStringAscii("PIIOString")) content = *(((PIIOString*)d)->string()); + if (PIStringAscii(d->className()) == PIStringAscii("PIIOByteArray")) content = PIString(*(((PIIOByteArray*)d)->byteArray())); + PIIOString ios(&content); + //int phase = 0; + int cind = -1; + while ((cind = content.find("enum", cind)) >= 0) { + ios.seek(cind); + line = ios.readLine().trim(); + type.clear(); + comment.clear(); + removeComment(line, &type, &comment); + if (line.find("{") < 0) { + cind += 4; + continue; + } + line.cutLeft(line.find("enum") + 4).trim(); + line.cutRight(line.size_s() - line.find("{")).trim(); + if (line.isEmpty()) { + cind += 4; + continue; + } + cs = CDSection(et); + cs.name = line; + //piCout << "enum" << cs.name; + int cev = 0; +// cevalues.clear(); + while (!ios.isEnd()) { + line = ios.readLine().trim(); + comment.clear(); + removeComment(line, &type, &comment); + if (line.find("}") >= 0) break; + if (line.isEmpty()) { + if (comment.find("=") >= 0) { + parseInsert(comment, alias, iarr); + if (!iarr.isEmpty()) { +// piCout << "#" << enum_values; + if (!enum_values.contains(alias)) { + piCout << "Parse error: can`t find section alias \"" << alias << "\"!"; + return CDSection(et); + } + if (!sections.contains(iarr.front())) { + piCout << "Parse error: can`t find section \"" << iarr.front() << "\"!"; + return CDSection(et); + } + //piCout << "insert" << alias << iarr; + int aval = enum_values.value(alias); + CDSection is = sections.value(iarr.take_front()), ts; + int ibpos = is.name.size_s(); + piForeachRC (PIString & a, iarr) { + PIStringList enames; + PIVector evals = enumValues(a, sections, enames); + //piCout << a << evals; + for (int i = 0; i < evals.size_s(); ++i) { + ts.section(evals[i]) = is; + ts.section(evals[i]).alias = enames[i]; + } + ts.name = is.name; + ts.name.insert(ibpos, PIString("[") << a << "]"); + is = ts; + ts = CDSection(et); + } + is.alias = alias; + cs.section(aval) = is; + } + } + } else { + parseEnumLine(line, &cev, &type, &comment); + //piCout << line << "=" << cev << "//" << type << comment; + ck = CDType(cev, line, type, "", "", comment, et); + if (type == "e") { + if (comment.startsWith("${")) { + comment.cutLeft(1); + PIString en = comment.inBrackets('{', '}'); + comment.cutLeft(en.size_s() + 2).trim(); + ck.setEnumValues(sections.value(en).enumValues()); + ck.setComment(comment); + //piCout << "enum" << en << ck.enumValues(); + } + } + cs[cev] = ck; + //cevalues[line] = cev; + enum_values[line] = cev; + ++cev; + } + } + //piCout << cs.name << cs.k; + sections[cs.name] = cs; +// piCout << "#" << cevalues; +// enum_values << cevalues; + cind += 4; + } +// piCout << "[CDSection] parse end"; + switch (et) { + case CDType::cdK: return sections.value("KDescription"); + case CDType::cdX: return sections.value("XDescription"); + case CDType::cdC: return sections.value("CDescription"); + case CDType::cdM: return sections.value("MDescription"); + default: return CDSection(et); + } + return CDSection(et); +} diff --git a/test/cd_utils/cdutils_parser.h b/test/cd_utils/cdutils_parser.h new file mode 100644 index 0000000..6c39c6f --- /dev/null +++ b/test/cd_utils/cdutils_parser.h @@ -0,0 +1,19 @@ +#ifndef CDUTILS_PARSER_H +#define CDUTILS_PARSER_H + + +class PIIODevice; + +namespace CDUtils { + +class CDSection; + +namespace CDParser { + +CDSection parse(PIIODevice * d, int cdsection_type); + +} + +} + +#endif // CDUTILS_PARSER_H diff --git a/test/cd_utils/cdutils_protocol.h b/test/cd_utils/cdutils_protocol.h new file mode 100644 index 0000000..b835359 --- /dev/null +++ b/test/cd_utils/cdutils_protocol.h @@ -0,0 +1,53 @@ +#ifndef CDUTILS_PROTOCOL_H +#define CDUTILS_PROTOCOL_H + +#include "pibytearray.h" + +namespace CDUtils { + + enum CDPacketType { + CD_Ping, + CD_Pong, + + CD_KQuery, + CD_KSend, + CD_KDirectChange, + + CD_XQuery, + CD_XSend, + CD_XRequest, + CD_XValues, + + CD_CQuery, + CD_CSend, + CD_Command, + + CD_MQuery, + CD_MSend, + CD_Message, + }; + +# pragma pack(push,1) + + struct PacketHeader { + int type; // CDPacketType + int session_id; + }; + + struct PacketKDirectChange { + PIDeque path; + PIString value; + }; + +# pragma pack(pop) + + +inline PIByteArray & operator <<(PIByteArray & s, const PacketHeader & v) {s << v.type << v.session_id; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PacketHeader & v) {s >> v.type >> v.session_id; return s;} + +inline PIByteArray & operator <<(PIByteArray & s, const PacketKDirectChange & v) {s << v.path << v.value; return s;} +inline PIByteArray & operator >>(PIByteArray & s, PacketKDirectChange & v) {s >> v.path >> v.value; return s;} + +} + +#endif // CDUTILS_PROTOCOL_H diff --git a/test/cd_utils/cdutils_types.cpp b/test/cd_utils/cdutils_types.cpp new file mode 100644 index 0000000..ae8ce05 --- /dev/null +++ b/test/cd_utils/cdutils_types.cpp @@ -0,0 +1,763 @@ +#include "cdutils_types.h" +#include "piconfig.h" +#include "pifile.h" +#include "pievaluator.h" +#include "cdutils_core.h" + +using namespace CDUtils; + +//int cdtype_debug_cnt = 1; + +const int cd_x_history_max_size = 4000; + +CDType::CDType() { + index_ = -1; + value_d = 0.; + value_i = 0; + value_b = calculated = x_enabled = false; + cd_type_ = cdNull; + parent = 0; + avg_size = 1; + mode_ = rmode_ = X_Current; +// debug_cnt = cdtype_debug_cnt; +// cdtype_debug_cnt++; +// piCout << "[CDType]" << "create Null" << debug_cnt; +} + + + + +CDType::CDType(int i, const PIString & n, const PIString & t, const PIString & v, const PIString & f, const PIString & c, cdT cd_t) { + index_ = i; + name_ = n.trimmed(); + type_ = t.trimmed(); + value_s = v.trimmed(); + formula_ = f.trimmed(); + comment_ = c.trimmed(); + value_d = v.toDouble(); + value_i = v.toInt(); + value_b = v.toBool(); + cd_type_ = cd_t; + calculated = x_enabled = false; + parent = 0; + avg_size = 1; + mode_ = rmode_ = X_Current; + if (type_ == "e") { + enum_values = parseEnumComment(comment_); +// piCout << enum_values.size() << enum_values; + } +// piCout << type_.size() << type_.toUTF8(); +// piCout << formula_.size() << formula_.toUTF8(); +// piCout << comment_.size() << comment_.toUTF8(); +// debug_cnt = cdtype_debug_cnt; +// cdtype_debug_cnt++; +// piCout << "[CDType] create" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK); +} + + +CDType & CDType::operator =(double x) { + value_d = x; + value_i = x; + value_b = x > 0.; + if (mode_ == X_All_Avg) { + avg_h << x; + double val = 0; + if (avg_h.size_s() >= avg_size) { + for (int i = 0; i < avg_h.size_s(); i++) + val += avg_h[i]; + val /= avg_h.size(); + avg_h.clear(); + if (history.size() < cd_x_history_max_size) + history << val; + } + } + return *this; +} + + +PIString CDType::type() const { + if (type_.trimmed().isEmpty()) return "f"; + // piCout << "type =" << type_.trimmed() << ";" << type_ << "#"; + return type_; +} + + +PIString CDType::value() const { + if (type_ == "b") return PIString::fromBool(value_b); + return value_s; +} + + +PIVariant CDType::variantValue() const { + if (type_.isEmpty()) return PIVariant(value()); + switch (type_[0].toAscii()) { + case 'b': return PIVariant(toBool()); break; + case 'n': return PIVariant(toInt()); break; + case 'f': return PIVariant(toDouble()); break; + case 'c': return PIVariant(PIVariantTypes::Color(toInt())); break; + case 'e': { + PIVariantTypes::Enum e = enum_values; + e.selectValue(toInt()); + return PIVariant(e); + break; + } + default: break; + } + return PIVariant(value()); +} + + +void CDType::setValue(const PIString & value_) { + formula_ = value_; + value_d = formula_.toDouble(); + value_i = formula_.toInt(); + value_b = formula_.toBool(); +} + + +void CDType::setVariantValue(const PIVariant & value_) { + setValue(PIString::fromNumber(value_.toDouble())); +} + + +void CDType::setFormula(const PIString & f) { + formula_ = f; + calculated = false; + //PIEvaluator e; + //calculate(&e); +} + + +PIStringList CDType::pathString() const { + PIStringList ret; + CDSection * ps = CDCore::instance()->root(cd_type_); + if (!ps) return ret; + for (int i = 0; i < path_.size_s() - 1; ++i) { + ps = &(ps->section(path_[i])); + if (!ps->alias.isEmpty()) ret << ps->alias; + else ret << PIString::fromNumber(path_[i]); + } + if (!name_.isEmpty()) ret << name_; + else ret << PIString::fromNumber(index_); + return ret; +} + + +void CDType::readX(PIByteArray & ba) { + if (ba.size() < 5) return; + uchar t(0); ba >> t; + rmode_ = (XMode)t; + switch (rmode_) { + case X_Current: + ba >> value_d; + break; + case X_All_Avg: { + PIVector ah; + ba >> ah; + history << ah; + if (!history.isEmpty()) + value_d = history.back(); + } break; + default: break; + } + value_i = value_d; + value_b = value_d > 0.; +} + + +void CDType::writeX(PIByteArray & ba) { + ba << uchar(mode_); + switch (mode_) { + case X_Current: + ba << value_d; + break; + case X_All_Avg: + ba << history; + history.clear(); + break; + default: break; + } +} + + +bool CDType::calculate(PIEvaluator * e, PIVector stack) { + if (stack.contains(this)) { + error_ = "Circular dependencies: "; + piForeachC (CDType * k, stack) + error_ << k->name() << " -> "; + error_ << name(); + //piCout << error_; + return false; + } + stack << this; + if (calculated) return true; + calculated = true; + error_.clear(); + if (!parent) return true; + //piCout << "calc" << name_ << (parent ? parent->alias : "root"); + value_s = formula_.trimmed(); + for (;;) { + int ki = value_s.find("K["); + if (ki < 0) break; + int ke = value_s.find("]", ki + 2); + if (ke < 0) break; + PIString kp = value_s.mid(ki + 2, ke - ki - 2); + //piCout << kp; + CDType & k((*parent)[kp]); + k.calculate(e, stack); + value_s.replace(ki, ke - ki + 1, PIString::fromNumber(k.value_d)); + } + value_d = formula_.toDouble(); + value_i = formula_.toInt(); + value_b = formula_.toBool(); + double ev = 0.; + if (!e->check(value_s) && value_d == 0. && value_i == 0 && !value_b) { + PIString f = formula_.trimmed().toLowerCase(); + if (f != "off" && f != "false" && f != "no" && !value_b) { + error_ = e->error(); + return false; + } + } else + if (e->isCorrect()) + ev = e->evaluate().real(); + //piCout << value_s << value_i << value_d << ev; + //if ((value_d == 0.) || (piAbsd(value_d) < piAbsd(ev))) value_d = ev; + //if ((value_i == 0) || (piAbsd(value_i) < piAbsd(ev))) value_i = int(ev); + if ((value_d == 0.) || (ev != 0.)) value_d = ev; + if ((value_i == 0) || (ev != 0.)) value_i = int(ev); + value_b = value_b || (ev > 0.); + if (value_i != 0) { + if (value_d == 0.) value_d = value_i; + value_b = value_i > 0; + } + if (value_d != 0.) { + if (value_i == 0) value_i = value_d; + value_b = value_d > 0.; + } + if (value_b) { + if (value_d == 0.) value_d = 1.; + if (value_i == 0) value_i = 1; + } + value_s = PIString::fromNumber(value_d); + return true; +} + + +PIVariantTypes::Enum CDType::parseEnumComment(PIString c) { + PIVariantTypes::Enum ret; + if (c.isEmpty()) return ret; + if (type_ == "e") { + PIStringList sl = c.inBrackets('{', '}').split(","); + int cval = 0; + piForeach (PIString & s, sl) { + s.trim(); + if (s.isEmpty()) continue; + if (s[0].isDigit()) { + int ind = s.find("-"); + if (ind > 0) { + cval = s.left(ind).toInt(); + s.cutLeft(ind + 1).trim(); + } + } + ret << PIVariantTypes::Enumerator(cval, s); + ++cval; + } + } + //piCout << c << "=" << ret; + return ret; +} + + +//CDType::CDType(const CDType &cdt) { +// index_ = cdt.index_; +// name_ = cdt.name_; +// type_ = cdt.type_; +// value_s = cdt.value_s; +// formula_ = cdt.formula_; +// comment_ = cdt.comment_; +// value_d = cdt.value_d; +// value_i = cdt.value_i; +// value_b = cdt.value_b; +// cd_type_ = cdt.cd_type_; +// debug_cnt = cdtype_debug_cnt; +// cdtype_debug_cnt++; +// piCout << "[CDType] copy" << debug_cnt << "->" << cdt.debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK); +//} + + +//CDType &CDType::operator =(const CDType &cdt) { +// index_ = cdt.index_; +// name_ = cdt.name_; +// type_ = cdt.type_; +// value_s = cdt.value_s; +// formula_ = cdt.formula_; +// comment_ = cdt.comment_; +// value_d = cdt.value_d; +// value_i = cdt.value_i; +// value_b = cdt.value_b; +// cd_type_ = cdt.cd_type_; +// piCout << "[CDType] assign" << debug_cnt << "=" << cdt.debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK); +// //debug_cnt = cdt.debug_cnt; +// return *this; +//} + + +//CDType::~CDType() { +// piCout << "[CDType] delete" << debug_cnt << index_ << ":" << name_ << ":" << type_ << ":" << value_s << ":" << value_d << ":" << formula_ << ":" << comment_ << ":" << (cd_type_ == cdK); +//} + + + +CDSection::CDSection(CDType::cdT type_) { + cd_type_ = type_; + null.cd_type_ = type_; +} + + +CDSection & CDSection::section(int v) { + CDSection & ret(s[v]); + ret.cd_type_ = cd_type_; + return ret; +} + + +const CDSection CDSection::section(int v) const { + CDSection & ret(s[v]); + ret.cd_type_ = cd_type_; + return s[v]; +} + + +bool CDSection::exists(PIDeque path) const { + if (path.isEmpty()) return false; + if (path.size_s() == 1) return cd.contains(path[0]); + int si = path[0]; + if (!s.contains(si)) return false; + path.remove(0, 1); + return s[si].exists(path); +} + + +int CDSection::count(bool recursive) const { + int ret = cd.size_s(); + if (recursive) { + PIMap::const_iterator i; + for (i = s.constBegin(); i != s.constEnd(); ++i) + ret += i->second.count(recursive); + } + return ret; +} + + +int CDSection::sectionsCount() const { + return s.size(); +} + + +PIStringList CDSection::index_names() const { + PIStringList ret; + PIMap::const_iterator i; + for (i = cd.begin(); i != cd.end(); ++i) + ret << i->second.name(); + return ret; +} + + +void CDSection::calculate() { + prepareCalculate(); + if (cd_type_ != CDType::cdK) return; + PIEvaluator e; + calculateRecursive(&e); +} + + +CDType & CDSection::getByName(const PIString & name_) { + PIStringList np = name_.split("."); + if (np.isEmpty()) return null; + //piCout << np; + CDSection * cs = this, * ns = 0; + if (np.front().isEmpty()) { + if (np.size_s() < 2) return null; + cs = CDCore::instance()->root(cd_type_); + np.pop_front(); + } + for (int i = 0; i < np.size_s() - 1; ++i) { + if (np[i].isEmpty()) return null; + bool isd = np[i][0].isDigit() || (np[i][0] == '-'); + int dv = 0; + if (isd) dv = np[i].toInt(); + ns = 0; + PIMap::iterator it; + //piCout << np[i] << isd << dv; + for (it = cs->s.begin(); it != cs->s.end(); ++it) { + bool f = false; + if (isd) f = (dv == it.key()); + else f = (np[i] == it.value().alias); + //piCout << "s..." << it.key() << it.value().alias << f; + if (f) { + ns = &(it.value()); + break; + } + } + //piCout << ns; + if (!ns) return null; + cs = ns; + } + PIMap::iterator it; + if (np.back().isEmpty()) return null; + bool isd = np.back()[0].isDigit() || (np.back()[0] == '-'); + int dv = 0; + if (isd) dv = np.back().toInt(); + //piCout << np.back() << isd << dv; + for (it = cs->cd.begin(); it != cs->cd.end(); ++it) { + bool f = false; + if (isd) f = (dv == it.key()); + else f = (np.back() == it.value().name()); + //piCout << "k..." << it.key() << it.value().name() << f; + if (f) + return cs->cd[it.key()]; + } + return null; +} + + +CDType & CDSection::getByPath(const PIDeque & path_) { + if (path_.isEmpty()) return null; + CDSection * s = this; + for (int i = 0; i < path_.size_s() - 1; ++i) + s = &(s->section(path_[i])); + if (!s) return null; + return (*s)[path_.back()]; +} + + +void CDSection::write(PIIODevice * d, const PIString & prefix) { + if (!d) return; + if (cd.isEmpty() && s.isEmpty()) return; +// piCout << "[CDSection] write start"; + PIString l; + PIStringList cdtl; + cdtl << "null" << "k" << "x" << "c" << "m"; + if (prefix.isEmpty()) l = "[" + cdtl[cd_type_] + "]"; + else l = "[" + prefix + "." + cdtl[cd_type_] + "]"; + l += "\n"; + d->write(l.toUTF8()); + l = "name = " + name + " \n"; + d->write(l.toUTF8()); + l = "alias = " + alias + " \n"; + d->write(l.toUTF8()); + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) { + CDType & ck(i.value()); + if (ck.cd_type() != cd_type_) continue; + switch (cd_type_) { + case CDType::cdNull: break; + case CDType::cdK: + l.clear(); l << ck.index() << ".f = " << ck.formula() << " #s " << ck.comment() << " \n"; + d->write(l.toUTF8()); + l.clear(); l << ck.index() << ".v = " << ck.value() << " #" << ck.type() << " " << ck.name() << " \n"; + d->write(l.toUTF8()); + if (!ck.enumValues().enum_list.isEmpty()) { + l.clear(); l << ck.index() << ".ev = {"; + //PIVector el = ck.enumValues(); + piForeachC (PIVariantTypes::Enumerator & e, ck.enumValues().enum_list) + l << e.value << " - " << e.name << ", "; + l.cutRight(2); + l << "} \n"; + d->write(l.toUTF8()); + } + break; + case CDType::cdX: + l.clear(); l << ck.index() << ".name = " << ck.name() << " #s " << ck.comment() << " \n"; + d->write(l.toUTF8()); + l.clear(); l << ck.index() << ".mode = " << ck.xmode() << " #e (0 - cur, 1 - all_avg) " << "\n"; + d->write(l.toUTF8()); + l.clear(); l << ck.index() << ".avg = " << ck.avg() << " #n " << "\n"; + d->write(l.toUTF8()); + l.clear(); l << ck.index() << ".sel = " << (ck.isSelectedX() ? "1" : "0") << " #n " << "\n"; + d->write(l.toUTF8()); + break; + case CDType::cdC: + case CDType::cdM: + l.clear(); l << ck.index() << ".name = " << ck.name() << " #s " << ck.comment() << " \n"; + d->write(l.toUTF8()); + break; + } + } + if (!s.isEmpty()) { + if (prefix.isEmpty()) l = "s"; + else l = prefix + ".s"; + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) { + j.value().write(d, l + "." + PIString::fromNumber(j.key())); + } + } + if (prefix.isEmpty()) { + l = "[]\n"; + d->write(l.toUTF8()); + } +// piCout << "[CDSection] write end"; +} + + +void CDSection::read(const void * ep) { +// piCout << "[CDSection] read start"; + PIStringList cdtl; + cdtl << "null" << "k" << "x" << "c" << "m"; + cd.clear(); + s.clear(); + PIConfig::Entry & e(*(PIConfig::Entry*)ep); + name = e.getValue(cdtl[cd_type_] + ".name").value(); + alias = e.getValue(cdtl[cd_type_] + ".alias").value(); + PIConfig::Entry & cdl = e.getValue(cdtl[cd_type_]); + for (int i = 0; i < cdl.childCount(); ++i) { + const PIConfig::Entry * e(cdl.child(i)); + bool ok = false; + int id = e->name().toInt(-1, &ok); +// piCout << "[read]" << ke->name() << ke->value() << ok; +// PIString n = ke->getValue("v").comment(); +// PIString t = n.takeLeft(1); + if (ok) { + CDType c; + PIString ev; + switch (cd_type_) { + case CDType::cdNull: break; + case CDType::cdK: + c = CDType(id, e->getValue("v").comment(), e->getValue("v").type(), e->getValue("v").value(), e->getValue("f").value(), e->getValue("f").comment(), cd_type_); + ev = e->getValue("ev", ""); + if (!ev.isEmpty()) + c.enum_values = c.parseEnumComment(ev); + break; + case CDType::cdX: + c = CDType(id, e->getValue("name").value(), PIString(), PIString(), PIString() , e->getValue("name").comment(), cd_type_); + c.setXMode((CDType::XMode)e->getValue("mode", int(CDType::X_Current)).value().toInt()); + c.setAvg((CDType::XMode)e->getValue("avg", 1).value().toInt()); + c.x_enabled = e->getValue("sel", false).value().toBool(); + break; + case CDType::cdC: + case CDType::cdM: + c = CDType(id, e->getValue("name").value(), PIString(), PIString(), PIString() , e->getValue("name").comment(), cd_type_); + break; + } + cd[id] = c; + } + } + PIConfig::Entry & sl = e.getValue("s"); + for (int i = 0; i < sl.childCount(); ++i) { + const PIConfig::Entry * se(sl.child(i)); + int sid = se->name().toInt(); + CDSection & rs(s[sid]); + rs.cd_type_ = cd_type_; + rs.read(se); + } +// piCout << "[CDSection] read end"; +} + + +void CDSection::update(CDSection & v, UpdateModeFlags mode) { + if (mode[SaveByIndex] && mode[SaveByName]) { + piCout << "[CDSection] update error: SaveByIndex | SaveByName mode is denied!"; + return; + } + //piCout << "[CDSection] update start"; + //piCout << "before" << k.size() << v.k.size(); + + PIMap prev_cd_f_bi; + PIMap prev_cd_f_bn; + PIMap::iterator i; + if (mode[SaveByIndex]) { + for (i = cd.begin(); i != cd.end(); ++i) + prev_cd_f_bi[i.key()] = i.value().formula(); + } + if (mode[SaveByName]) { + for (i = cd.begin(); i != cd.end(); ++i) + prev_cd_f_bn[i.value().name_] = i.value().formula(); + } + if (!mode[Merge]) + cd.clear(); + for (i = v.cd.begin(); i != v.cd.end(); ++i) { + int id = i.key(); + PIString n = i.value().name(); + cd[id] = i.value(); + if (mode[SaveByIndex]) { + if (prev_cd_f_bi.contains(id)) + cd[id].setFormula(prev_cd_f_bi[id]); + } + if (mode[SaveByName]) { + if (prev_cd_f_bn.contains(n)) + cd[id].setFormula(prev_cd_f_bn[n]); + } + } + + PIMap prev_s_bi; + PIMap prev_s_bn; + PIMap::iterator j; + if (mode[SaveByIndex]) { + for (j = s.begin(); j != s.end(); ++j) + prev_s_bi[j.key()] = j.value(); + } + if (mode[SaveByName]) { + for (j = s.begin(); j != s.end(); ++j) + prev_s_bn[j.value().alias] = j.value(); + } + if (!mode[Merge]) + s.clear(); + for (j = v.s.begin(); j != v.s.end(); ++j) { + int id = j.key(); + PIString n = j.value().alias; + s[id] = j.value(); + if (mode[SaveByIndex]) { + if (prev_s_bi.contains(id)) + s[id] = prev_s_bi[id]; + } + if (mode[SaveByName]) { + if (prev_s_bn.contains(n)) + s[id] = prev_s_bn[n]; + } + s[id].update(j.value(), mode); + } + + /*PISet used; + for (i = k.begin(); i != k.end(); ++i) { + if (v.k.contains(i.key())) { + PIString f = k[i.key()].formula_; + CDType & cdt = v.k[i.key()]; + cdt.formula_ = f; + k[i.key()] = cdt; + used << i.key(); + } + if (mode) { + CDType & ck(k[i.key()]); + if (prev_k_f_bn.contains(ck.name_)) + ck.setFormula(prev_k_f_bn[ck.name_]); + } + } + //piCout << " after" << k.size(); + for (i = v.k.begin(); i != v.k.end(); ++i) { + if (!used.contains(i.key())) + k[i.key()] = i.value(); + CDType & ck(k[i.key()]); + ck.setFormula(prev_k_f_bn.value(ck.name_)); + } + used.clear(); + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) { + if (v.s.contains(j.key())) + j.value().update(v.s[j.key()], mode); + used << j.key(); + } + for (j = v.s.begin(); j != v.s.end(); ++j) { + if (!used.contains(j.key())) + s[j.key()] = j.value(); + }*/ +// piCout << "[CDSection] update end"; +} + + +bool CDSection::isSameStructure(CDSection & v) { + PIMap cd_ids; + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) + cd_ids[i.value().name()] = i.key(); + for (i = v.cd.begin(); i != v.cd.end(); ++i) { + if (!cd_ids.contains(i.value().name())) continue; + //piCout << i.key() << k[i.key()].name() << i.value().name(); + if (cd[cd_ids[i.value().name()]].index() != i.key()) + return false; + } + PIMap::iterator j; + for (j = v.s.begin(); j != v.s.end(); ++j) { + if (!s.contains(j.key())) continue; + if (!s[j.key()].isSameStructure(j.value())) + return false; + } + return true; +} + + +void CDSection::prepareCalculate() { + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) { + i.value().parent = this; + i.value().calculated = false; + } + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) + j.value().prepareCalculate(); +} + + +void CDSection::calculateRecursive(PIEvaluator * e) { + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) + i.value().calculate(e); + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) + j.value().calculateRecursive(e); +} + + +void CDSection::setSelectedX(bool yes) { + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) + i.value().x_enabled = yes; + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) + j.value().setSelectedX(yes); +} + + +PIVector > CDSection::collectX() const { + PIVector > ret; + PIMap::const_iterator i; + for (i = cd.begin(); i != cd.end(); ++i) { + if (i.value().x_enabled) + ret << i.value().path(); + } + PIMap::const_iterator j; + for (j = s.constBegin(); j != s.constEnd(); ++j) + ret << j.value().collectX(); + return ret; +} + + +void CDSection::makePath(PIDeque p) { + PIDeque tp; + PIMap::iterator i; + for (i = cd.begin(); i != cd.end(); ++i) { + tp = p; + tp << i.key(); + i.value().path_ = tp; + //piCout << "path for" << i.value().name() << tp; + } + PIMap::iterator j; + for (j = s.begin(); j != s.end(); ++j) { + tp = p; + tp << j.key(); + j.value().makePath(tp); + } +} + + +PIVector CDSection::children(bool recursive) const { + PIVector ret; + PIMap::const_iterator i; + for (i = cd.begin(); i != cd.end(); ++i) + ret << const_cast(&(i.value())); + if (!recursive) return ret; + PIMap::const_iterator j; + for (j = s.constBegin(); j != s.constEnd(); ++j) + ret << j.value().children(true); + return ret; +} + + +PIVariantTypes::Enum CDSection::enumValues() const { + PIVariantTypes::Enum ret; + PIMap::const_iterator i; + for (i = cd.constBegin(); i != cd.constEnd(); ++i) + ret << PIVariantTypes::Enumerator(i.key(), i.value().name()); + return ret; +} + + diff --git a/test/cd_utils/cdutils_types.h b/test/cd_utils/cdutils_types.h new file mode 100644 index 0000000..676020c --- /dev/null +++ b/test/cd_utils/cdutils_types.h @@ -0,0 +1,173 @@ +#ifndef CDUTILS_TYPES_H +#define CDUTILS_TYPES_H + +#include "pistring.h" +#include "pimap.h" +#include "pivariant.h" + +class PIIODevice; +class PIEvaluator; +class CD_Pult; +class CDItem; +class CDItemModel; + +namespace CDUtils { + +class CDSection; + +enum UpdateMode { + SaveByIndex = 0x01, + SaveByName = 0x02, + Merge = 0x04 +}; + +enum MessageType { + Log = 1, + MessageBox, +}; + +typedef PIFlags UpdateModeFlags; + +class CDType { + friend class CDSection; + friend class CDCore; + friend class Interface; + friend class XInterface; +public: + enum cdT {cdNull, cdK, cdX, cdC, cdM}; + enum XMode {X_Current, X_All_Avg}; + + CDType(); + CDType(int i, const PIString & n, const PIString & t, const PIString & v, const PIString & f, const PIString & c, cdT cd_t); + + CDType & operator =(double x); + int index() const {return index_;} + PIString name() const {return name_;} + PIString type() const; + PIString value() const; + PIVariant variantValue() const; + PIString formula() const {return formula_;} + PIString comment() const {return comment_;} + double toDouble() const {return value_d;} + int toInt() const {return value_i;} + bool toBool() const {return value_b;} + cdT cd_type() const {return cd_type_;} + void setValue(const PIString & value_); + void setVariantValue(const PIVariant & value_); + void setFormula(const PIString & formula); + void setComment(const PIString & comment) {comment_ = comment;} + operator double() const {return value_d;} + const PIVariantTypes::Enum & enumValues() const {return enum_values;} + void setEnumValues(const PIVariantTypes::Enum & ev) {enum_values = ev;} + const PIString & errorString() const {return error_;} + PIDeque path() const {return path_;} + PIStringList pathString() const; + + void setXMode(XMode mode) {mode_ = mode;} + void setAvg(int avg) {avg_size = avg;} + XMode xmode() const {return mode_;} + XMode xmode_rec() const {return rmode_;} + int avg() const {return avg_size;} + bool isSelectedX() const {return x_enabled;} + void readX(PIByteArray & ba); + void writeX(PIByteArray & ba); + + PIVector history; + +protected: + bool calculate(PIEvaluator * e, PIVector stack = PIVector()); + PIVariantTypes::Enum parseEnumComment(PIString c); + cdT cd_type_; + int index_; + PIString name_, type_; + PIString value_s, formula_, comment_, error_; + PIVariantTypes::Enum enum_values; + CDSection * parent; + PIDeque path_; + double value_d; + int value_i; + bool value_b, calculated, x_enabled; + PIVector avg_h; + int avg_size; + XMode mode_, rmode_; +}; + + +class CDSection { + friend class CDCore; + friend class Interface; + friend class XInterface; + friend class ::CD_Pult; + friend class ::CDItem; + friend class ::CDItemModel; +public: + + CDSection(CDType::cdT type_ = CDType::cdNull); + + bool test(int v) {return cd.value(v).toBool();} +// CDType & operator [](int v) {if (!k.contains(v)) k[v].index_ = v; return k[v];} + CDType & operator [](int v) {return cd[v];} + const CDType operator [](int v) const {return cd[v];} + CDType & operator [](const PIString & name_) {return getByName(name_);} + const CDType operator [](const PIString & name_) const {return const_cast(this)->getByName(name_);} + CDType & operator [](const PIDeque & path_) {return getByPath(path_);} + const CDType operator [](const PIDeque & path_) const {return const_cast(this)->getByPath(path_);} + CDSection & section(int v); + const CDSection section(int v) const; + + bool isEmpty() const {return cd.isEmpty() && s.isEmpty();} + bool exists(PIDeque path) const; + int count(bool recursive = true) const; + int sectionsCount() const; + PIVector indexes() const {return cd.keys();} + PIStringList index_names() const; + void calculate(); + void makePath(PIDeque p = PIDeque()); + PIVector children(bool recursive = true) const; + PIVariantTypes::Enum enumValues() const; + + PIString name; + PIString alias; + +protected: + CDSection(PIMap k_, PIMap s_) { + cd = k_; + s = s_; + } + CDType & getByName(const PIString & name_); + CDType & getByPath(const PIDeque & path_); + void write(PIIODevice * d, const PIString & prefix = PIString()); + void read(const void * ep); + void update(CDSection & v, UpdateModeFlags mode = SaveByName); + bool isSameStructure(CDSection & v); + void prepareCalculate(); + void calculateRecursive(PIEvaluator * e); + void setSelectedX(bool yes); + PIVector > collectX() const; + + PIMap cd; + mutable PIMap s; + CDType null; + CDType::cdT cd_type_; +}; + +} + + +inline PICout operator <<(PICout s, const CDUtils::CDType & v) { + s.space(); + s.setControl(0, true); + switch (v.cd_type()) { + case CDUtils::CDType::cdK : s << "K["; break; + case CDUtils::CDType::cdX : s << "X["; break; + case CDUtils::CDType::cdC : s << "C["; break; + case CDUtils::CDType::cdM : s << "M["; break; + default : s << "Null["; break; + } + s << v.name() << "(" << v.index() << ")] = " << v.value(); + s.restoreControl(); + return s; +} + + +#endif // CDUTILS_TYPES_H diff --git a/test/cd_utils/cdutils_x.cpp b/test/cd_utils/cdutils_x.cpp new file mode 100644 index 0000000..2b2d24a --- /dev/null +++ b/test/cd_utils/cdutils_x.cpp @@ -0,0 +1,55 @@ +#include "cdutils_x.h" +#include "cdutils_core.h" + +using namespace CDUtils; + +XInterface X; + + +XInterface::XInterface(): Interface(CDType::cdX) { + CONNECTU(core, X_ReceivedX, this, receivedX); +} + + +void XInterface::setEnabled(const CDType & x, bool en) { + core->x_mutex.lock(); + CDType & t((*s)[x.path()]); + if (t.cd_type() != CDType::cdX) { + core->x_mutex.unlock(); + return; + } + t.x_enabled = en; + //piCout << t << "x_enabled" << en; + core->need_rebuild_x = true; + core->x_mutex.unlock(); +} + + +PIVector > XInterface::enabledList() const { + return CDCore::instance()->x_selected; +} + + +void XInterface::setEnabledList(const PIVector > & l) { + CDCore::instance()->x_selected = l; +} + + +void XInterface::lock() { + CDCore::instance()->x_mutex.lock(); +} + + +void XInterface::unlock() { + CDCore::instance()->x_mutex.unlock(); +} + + +void XInterface::start(double freq) { + core->startX(freq); +} + + +void XInterface::stop() { + core->stopX(); +} diff --git a/test/cd_utils/cdutils_x.h b/test/cd_utils/cdutils_x.h new file mode 100644 index 0000000..bbfc56a --- /dev/null +++ b/test/cd_utils/cdutils_x.h @@ -0,0 +1,37 @@ +#ifndef CDUTILS_X_H +#define CDUTILS_X_H + +#include "cdutils_interface.h" + + +namespace CDUtils { + + +class XInterface: public Interface +{ + PIOBJECT_SUBCLASS(XInterface, Interface) +public: + XInterface(); + + EVENT1(keepNamesRequest, bool*, xn) + EVENT1(receivedX, PIVector >, pathes) + + void enable(const CDType & x) {setEnabled(x, true);} + void disable(const CDType & x) {setEnabled(x, false);} + void setEnabled(const CDType & x, bool en); + void setDisabled(const CDType & x, bool dis) {setEnabled(x, !dis);} + PIVector > enabledList() const; + void setEnabledList(const PIVector > & l); + void lock(); + void unlock(); + + void start(double freq = 20.); + void stop(); + +}; + +} + +extern CDUtils::XInterface X; + +#endif // CDUTILS_X_H diff --git a/test/cd_utils/cdutilstest.cpp b/test/cd_utils/cdutilstest.cpp new file mode 100644 index 0000000..ff35970 --- /dev/null +++ b/test/cd_utils/cdutilstest.cpp @@ -0,0 +1,97 @@ +#include "cdutils_k.h" +#include "cdutils_x.h" +#include "cdutils_c.h" +#include "cdutils_m.h" +#include "cdutils_core.h" +#include "cdtest.h" +#include "pip.h" +#include "k_description.h" + +using namespace CDUtils; + +class Core : public PIObject +{ + PIOBJECT(Core) + public: + Core() { + CDCore::instance()->initApp(); +// piCout << "testCore"; + CONNECTU(&timer, tickEvent, this, timerDone); + CONNECTU(&X, received, this, xrecv); + CONNECTU(&C, received, this, crecv); + t = 0.; + } + + void load() { + rf.open("k.dat", PIIODevice::ReadWrite); + K.read(&rf); + rf.close(); + } + + void save() { + rf.open("k_out.txt", PIIODevice::ReadWrite); + rf.resize(0); + K.write(&rf); + rf.close(); +// rf.open("k_out.txt", PIIODevice::ReadWrite); +// K.read(&rf); +// rf.close(); +// rf.open("k_out2.txt", PIIODevice::ReadWrite); +// rf.resize(0); +// K.write(&rf); +// rf.close(); +// rf.open("k_out2.txt", PIIODevice::ReadWrite); +// K.read(&rf); +// rf.close(); +// rf.open("k_out3.txt", PIIODevice::ReadWrite); +// rf.resize(0); +// K.write(&rf); +// rf.close(); + } + + void test() { + X.lock(); + X[KD::Frequency] = 100; + X.section(KD::Spectrometer)[KD::Temperature_default] = sin(t); + t += 0.01; + X.unlock(); + /*piCout << "count" << K.count(); + piCout << K[First]; + piCout << K[Second];*/ + } + + EVENT_HANDLER(void, ksend) {piCout << "sended k";} + EVENT_HANDLER(void, crecv) { + piCout << "received c"; + C.connect(C.section(KD::Logs).section(KD::Spec).section(KD::Formats)[KD::Binary], this, HANDLER(cmd)); + C.autoConnect(this); + } + EVENT_HANDLER(void, xrecv) { + piCout << "received x"; + if (!timer.isRunning()) timer.start(10); + X.start(); + } + EVENT_HANDLER(void, timerDone) {test();} + EVENT_HANDLER(void, cmd) {piCout << "command cmd";} + EVENT_HANDLER(void, c_Pause) { + piCout << "command pause"; + M[KD::Main] << "rec command" << C[KD::Pause]; + M.messageBox(M.root()[KD::Core], "init successfull"); + } + EVENT_HANDLER(void, c_Spectrometer_Connection) {piCout << "command spec_conn";} + +private: + PIFile rf; + PITimer timer; + double t; +}; + + +int main(int argc, char *argv[]) { + X.start(); + piSleep(1); + //CDCore::instance()->destroy(); + piCout << "DELETED"; + return 0; +} + diff --git a/test/cd_utils/k_description.h b/test/cd_utils/k_description.h new file mode 100644 index 0000000..09d53ce --- /dev/null +++ b/test/cd_utils/k_description.h @@ -0,0 +1,147 @@ +#ifndef K_DESCRIPTION_H +#define K_DESCRIPTION_H + +namespace KD { + +enum Sections { + Startup, + Spectrometer, + Switch, + Formats, + Logs, + Detector, + CoreOutput, +}; + +enum LogFormat { + Text, //b text + Binary, //b binary +}; + +enum LogType { + Data, //b write data logs + Spec, //b write spectrogram logs + ARINC, //b ARINC +}; + +enum KLogConfig { + // LogFormat = Formats + StartupWrite, //b + ApplyBinaryLogHeader, //b Apply settings under ID = 255 for binary log + MaximumWriteFrequency, //f Maximum frequency for log in Hz, or 0 for unlimited +}; + +enum SwitchType { + BaySpec, + DiCon, +}; + +enum SpectrometerConnection { + TCP, + USB, +}; + +enum PeakSearchMode { + Left, + Max, + Right, +}; + +enum CoreMode { + ModeSpectrometer, + ModePlayer, + ModePeaks, +}; + +enum KSpectrometer { + Connection, //e ${SpectrometerConnection} + Temperature_compensation, //b Use temperature sensor or default temperature witout compensation + Temperature_default, //f Default temperature using if compensation disabled +}; + +enum KSwitch { + Enabled, //b 0 or 1 Use optical switch or not + Wait, //n Delay after switching channel, ms + Autoscan, //e {0 - scan with our forces, 1 - SDK autoscan} + Autoscan_offset, //n Offset for SDK autoscan + Type, //e ${SwitchType} +}; + +enum KDetector { + Threshold_Min, //f + Threshold_Max, //f + SideSize, //f + SideOffset, //f + PeaksSearch, //e ${PeakSearchMode} +}; + +enum KCoreOutput { + SendSpectrum, //b Send spectrum data or empty vector + SendOnlyCurrentValue, //b Send current values or all stored in 20Hz +}; + +enum CoreOutputChannel { + GUI, + SecGUI, + ThirdGUI, +}; + +enum KDescription { + // KSpectrometer = Spectrometer + // KSwitch = Switch + // KDetector = Detector + // KCoreOutput[CoreOutputChannel] = CoreOutput + // KLogConfig[LogType] = Logs + Gratings_history, //n Gratings peak values history, count + Amplitude_history_size, //n Count of history values to calculate sensors amplitude + Fourier_enabled, //b Global fourier enable flag + Fourier_size, //n Size of fourier window, in counts + Fourier_YScale, //f scale for fourier amplitude + Fourier_Max_or_Density, //n 0 - Maximum in fourier window, 1 - Density of fourier window + Mode, //e ${CoreMode} Work mode + SynchronizationEnabled, //b When enabled, start logs and reset time when sync signal received + SynchronizationMode, //e {0 - disabled, 1 - new file, 2 - column in log} + LambdasAutoReset, //b Set lambdas_0 to grating values on first data receive + Gauss_size, //n + //Peak_max_offset, //f in pixels + //Peak_LF_coeff, //f 1. - no filtering + GratingOverloadMax, //n + GratingOverloadMin, //n + SendLED, //b Send to LED Arduino serial port current state + SynchronizationClearValues, //b clear gratings and sensors values and history on sync + PeaksModeFrequency, //f + PeaksModeAdditionNm_1, //f Addition nm to peaks in ModePeaks, channel 1 + PeaksModeAdditionNm_2, //f Addition nm to peaks in ModePeaks, channel 2 + PeaksModeAdditionNm_3, //f Addition nm to peaks in ModePeaks, channel 3 + PeaksModeAdditionNm_4, //f Addition nm to peaks in ModePeaks, channel 4 +}; + +enum XDescription { + // KSpectrometer = Spectrometer + // KSwitch = Switch + // KDetector = Detector + // KCoreOutput[CoreOutputChannel] = CoreOutput + // KLogConfig[LogType] = Logs + State, //b + Frequency, //n cur freq + //Fourier_enabled, //b Global fourier enable flag +}; + +enum CDescription { + // KSpectrometer = Spectrometer + // KCoreOutput[CoreOutputChannel] = CoreOutput + // KLogConfig[LogType] = Logs + Halt, //b + Reboot, //n cur freq + Pause, //b Global fourier enable flag +}; + +enum MDescription { + Main, //b + Core, //s + Warnings, //b Global fourier enable flag +}; + +} + +#endif // K_DESCRIPTION_H diff --git a/test/kx_utils/CMakeLists.txt b/test/kx_utils/CMakeLists.txt new file mode 100644 index 0000000..cf3a787 --- /dev/null +++ b/test/kx_utils/CMakeLists.txt @@ -0,0 +1,76 @@ +project(kx_utils) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (NOT LIBPROJECT) + find_package(PIP REQUIRED) +endif () +if (MINGW) + find_package(MinGW REQUIRED) +endif() +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${PIP_INCLUDES}) +set(KX_PULT_NAME "kx_pult") +option(KX_PULT "Build ${KX_PULT_NAME}" 1) +option(LIB "System install" 1) +option(DEBUG "Build with -g3" 0) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wall") +if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") +endif() +set(CPPS_UTILS "kx_coeffs.cpp" "piprotocol.cpp") +set(HDRS_UTILS "kx_coeffs.h" "kx_protocol_x.h" "kx_protocol_c.h" "piprotocol.h") +if (DEFINED ENV{QNX_HOST}) + add_library(${PROJECT_NAME} STATIC ${CPPS_UTILS}) +else() + add_library(${PROJECT_NAME} SHARED ${CPPS_UTILS}) +endif() +target_link_libraries(${PROJECT_NAME} ${PIP_LIBRARY}) +message(STATUS "Building ${PROJECT_NAME}") +if (NOT DEFINED ENV{QNX_HOST}) + if (KX_PULT) + find_package(Qt4 REQUIRED) + find_package(QAD REQUIRED) + find_package(OpenGL REQUIRED) + include_directories(${QT_INCLUDES} ${QAD_INCLUDES}) + set(CPPS "kx_pult.cpp" "kx_pult.h" "kx_pult.ui" "main_kx_pult.cpp") + set(MOCS "kx_pult.h") + file(GLOB UIS "*.ui") + file(GLOB RES "*.qrc") + qt4_wrap_cpp(CMOCS ${MOCS} OPTIONS -nw) + qt4_wrap_ui(CUIS ${UIS}) + qt4_add_resources(RESS ${RES}) + add_executable(${KX_PULT_NAME} WIN32 ${CPPS} ${CMOCS} ${CUIS} ${RESS} ${MOCS}) + set(LIBS ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTOPENGL_LIBRARY} ${OPENGL_LIBRARIES} ${PIP_LIBRARY} qad_graphic4 qad_utils4 qad_widgets4) + target_link_libraries(${KX_PULT_NAME} ${LIBS} ${PROJECT_NAME}) + endif() +endif() +if (LIB) + if (WIN32) + set(CMAKE_INSTALL_PREFIX ${MINGW_DIR}) + install(FILES ${HDRS_UTILS} DESTINATION ${MINGW_INCLUDE}) + install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + set(CMAKE_INSTALL_PREFIX /usr/local) + else() + if (DEFINED ANDROID_PLATFORM) + set(CMAKE_INSTALL_PREFIX ${ANDROID_SYSTEM_LIBRARY_PATH}/usr) + else() + set(CMAKE_INSTALL_PREFIX /usr) + endif() + endif() + install(FILES ${HDRS_UTILS} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) + install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() +if (NOT DEFINED ENV{QNX_HOST}) + if (KX_PULT) + install(TARGETS ${KX_PULT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + endif() +endif() diff --git a/test/kx_utils/clean b/test/kx_utils/clean new file mode 100644 index 0000000..e1d5feb --- /dev/null +++ b/test/kx_utils/clean @@ -0,0 +1,4 @@ +#! /bin/bash +VERBOSE=1 make clean +rm -rvf CMakeFiles +rm -vf CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *.user* *~ *cxx moc_* ui_* qrc_* *.o diff --git a/test/kx_utils/clean.bat b/test/kx_utils/clean.bat new file mode 100644 index 0000000..3a657d9 --- /dev/null +++ b/test/kx_utils/clean.bat @@ -0,0 +1,4 @@ +#make clean +del /q /f /s CMakeFiles +rmdir /q /s CMakeFiles +del /q /f CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *.user* *~ *cxx moc_* ui_* qrc_* *.o *.exe *.a *.dll *.lib core *.qrc.depends diff --git a/test/kx_utils/icons/accessories-calculator.png b/test/kx_utils/icons/accessories-calculator.png new file mode 100644 index 0000000..1b1dd1e Binary files /dev/null and b/test/kx_utils/icons/accessories-calculator.png differ diff --git a/test/kx_utils/icons/alpha.png b/test/kx_utils/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/test/kx_utils/icons/alpha.png differ diff --git a/test/kx_utils/icons/application-exit.png b/test/kx_utils/icons/application-exit.png new file mode 100644 index 0000000..ed5f8b2 Binary files /dev/null and b/test/kx_utils/icons/application-exit.png differ diff --git a/test/kx_utils/icons/archive-extract.png b/test/kx_utils/icons/archive-extract.png new file mode 100644 index 0000000..f6f2a5d Binary files /dev/null and b/test/kx_utils/icons/archive-extract.png differ diff --git a/test/kx_utils/icons/configure.png b/test/kx_utils/icons/configure.png new file mode 100644 index 0000000..5ce478b Binary files /dev/null and b/test/kx_utils/icons/configure.png differ diff --git a/test/kx_utils/icons/dialog-close.png b/test/kx_utils/icons/dialog-close.png new file mode 100644 index 0000000..2c2f99e Binary files /dev/null and b/test/kx_utils/icons/dialog-close.png differ diff --git a/test/kx_utils/icons/document-export.png b/test/kx_utils/icons/document-export.png new file mode 100644 index 0000000..9e1b6a6 Binary files /dev/null and b/test/kx_utils/icons/document-export.png differ diff --git a/test/kx_utils/icons/document-import.png b/test/kx_utils/icons/document-import.png new file mode 100644 index 0000000..97e683a Binary files /dev/null and b/test/kx_utils/icons/document-import.png differ diff --git a/test/kx_utils/icons/document-open.png b/test/kx_utils/icons/document-open.png new file mode 100644 index 0000000..8ba5441 Binary files /dev/null and b/test/kx_utils/icons/document-open.png differ diff --git a/test/kx_utils/icons/document-save-.png b/test/kx_utils/icons/document-save-.png new file mode 100644 index 0000000..7fa489c Binary files /dev/null and b/test/kx_utils/icons/document-save-.png differ diff --git a/test/kx_utils/icons/document-save.png b/test/kx_utils/icons/document-save.png new file mode 100644 index 0000000..8072aea Binary files /dev/null and b/test/kx_utils/icons/document-save.png differ diff --git a/test/kx_utils/icons/edit-clear-.png b/test/kx_utils/icons/edit-clear-.png new file mode 100644 index 0000000..631ed44 Binary files /dev/null and b/test/kx_utils/icons/edit-clear-.png differ diff --git a/test/kx_utils/icons/edit-clear-locationbar-rtl.png b/test/kx_utils/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..6c4b83b Binary files /dev/null and b/test/kx_utils/icons/edit-clear-locationbar-rtl.png differ diff --git a/test/kx_utils/icons/edit-clear.png b/test/kx_utils/icons/edit-clear.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/test/kx_utils/icons/edit-clear.png differ diff --git a/test/kx_utils/icons/edit-copy.png b/test/kx_utils/icons/edit-copy.png new file mode 100644 index 0000000..5cdeb5f Binary files /dev/null and b/test/kx_utils/icons/edit-copy.png differ diff --git a/test/kx_utils/icons/edit-delete.png b/test/kx_utils/icons/edit-delete.png new file mode 100644 index 0000000..87cd0b0 Binary files /dev/null and b/test/kx_utils/icons/edit-delete.png differ diff --git a/test/kx_utils/icons/edit-find-.png b/test/kx_utils/icons/edit-find-.png new file mode 100644 index 0000000..64a1e28 Binary files /dev/null and b/test/kx_utils/icons/edit-find-.png differ diff --git a/test/kx_utils/icons/edit-find.png b/test/kx_utils/icons/edit-find.png new file mode 100644 index 0000000..9a462c0 Binary files /dev/null and b/test/kx_utils/icons/edit-find.png differ diff --git a/test/kx_utils/icons/edit-guides.png b/test/kx_utils/icons/edit-guides.png new file mode 100644 index 0000000..d264839 Binary files /dev/null and b/test/kx_utils/icons/edit-guides.png differ diff --git a/test/kx_utils/icons/edit-paste.png b/test/kx_utils/icons/edit-paste.png new file mode 100644 index 0000000..a4e0a02 Binary files /dev/null and b/test/kx_utils/icons/edit-paste.png differ diff --git a/test/kx_utils/icons/expand_s_x.png b/test/kx_utils/icons/expand_s_x.png new file mode 100644 index 0000000..b66cb40 Binary files /dev/null and b/test/kx_utils/icons/expand_s_x.png differ diff --git a/test/kx_utils/icons/expand_s_y.png b/test/kx_utils/icons/expand_s_y.png new file mode 100644 index 0000000..c2e8f78 Binary files /dev/null and b/test/kx_utils/icons/expand_s_y.png differ diff --git a/test/kx_utils/icons/expand_x.png b/test/kx_utils/icons/expand_x.png new file mode 100644 index 0000000..217e3ea Binary files /dev/null and b/test/kx_utils/icons/expand_x.png differ diff --git a/test/kx_utils/icons/expand_y.png b/test/kx_utils/icons/expand_y.png new file mode 100644 index 0000000..fedf936 Binary files /dev/null and b/test/kx_utils/icons/expand_y.png differ diff --git a/test/kx_utils/icons/item-add.png b/test/kx_utils/icons/item-add.png new file mode 100644 index 0000000..8a422c7 Binary files /dev/null and b/test/kx_utils/icons/item-add.png differ diff --git a/test/kx_utils/icons/item.png b/test/kx_utils/icons/item.png new file mode 100644 index 0000000..50bfddb Binary files /dev/null and b/test/kx_utils/icons/item.png differ diff --git a/test/kx_utils/icons/layer-visible-off.png b/test/kx_utils/icons/layer-visible-off.png new file mode 100644 index 0000000..5f87dce Binary files /dev/null and b/test/kx_utils/icons/layer-visible-off.png differ diff --git a/test/kx_utils/icons/layer-visible-on.png b/test/kx_utils/icons/layer-visible-on.png new file mode 100644 index 0000000..b240552 Binary files /dev/null and b/test/kx_utils/icons/layer-visible-on.png differ diff --git a/test/kx_utils/icons/list-add.png b/test/kx_utils/icons/list-add.png new file mode 100644 index 0000000..1e03be9 Binary files /dev/null and b/test/kx_utils/icons/list-add.png differ diff --git a/test/kx_utils/icons/media-playback-pause.png b/test/kx_utils/icons/media-playback-pause.png new file mode 100644 index 0000000..5c7d128 Binary files /dev/null and b/test/kx_utils/icons/media-playback-pause.png differ diff --git a/test/kx_utils/icons/media-playback-stop.png b/test/kx_utils/icons/media-playback-stop.png new file mode 100644 index 0000000..650874f Binary files /dev/null and b/test/kx_utils/icons/media-playback-stop.png differ diff --git a/test/kx_utils/icons/media-record.png b/test/kx_utils/icons/media-record.png new file mode 100644 index 0000000..f926a8f Binary files /dev/null and b/test/kx_utils/icons/media-record.png differ diff --git a/test/kx_utils/icons/node-add.png b/test/kx_utils/icons/node-add.png new file mode 100644 index 0000000..80b5c61 Binary files /dev/null and b/test/kx_utils/icons/node-add.png differ diff --git a/test/kx_utils/icons/node.png b/test/kx_utils/icons/node.png new file mode 100644 index 0000000..fc8194e Binary files /dev/null and b/test/kx_utils/icons/node.png differ diff --git a/test/kx_utils/icons/view-grid.png b/test/kx_utils/icons/view-grid.png new file mode 100644 index 0000000..0f1d70c Binary files /dev/null and b/test/kx_utils/icons/view-grid.png differ diff --git a/test/kx_utils/icons/view-refresh.png b/test/kx_utils/icons/view-refresh.png new file mode 100644 index 0000000..afa2a9d Binary files /dev/null and b/test/kx_utils/icons/view-refresh.png differ diff --git a/test/kx_utils/icons/zoom-fit-best.png b/test/kx_utils/icons/zoom-fit-best.png new file mode 100644 index 0000000..07cfc98 Binary files /dev/null and b/test/kx_utils/icons/zoom-fit-best.png differ diff --git a/test/kx_utils/icons/zoom-fit-height.png b/test/kx_utils/icons/zoom-fit-height.png new file mode 100644 index 0000000..32e2190 Binary files /dev/null and b/test/kx_utils/icons/zoom-fit-height.png differ diff --git a/test/kx_utils/kx_coeffs.cpp b/test/kx_utils/kx_coeffs.cpp new file mode 100644 index 0000000..940ff36 --- /dev/null +++ b/test/kx_utils/kx_coeffs.cpp @@ -0,0 +1,194 @@ +#include "kx_coeffs.h" +#include "kx_protocol_x.h" + +double X[KX_X_COUNT]; +PIVector K; + + +KX_Coefficients::KX_Coefficients(const PIString & config, const PIString & name, bool _pult) { + setName("kx_coeffs"); + PIConfig conf(config, PIIODevice::ReadOnly); + fname = conf.getValue(name).getValue("file", "k.dat"); + readCoeffs(); + int ns = piMaxi(conf.getValue(name).getValue("count", 0), K.size_s()); + if (ns > K.size_s()) { + K.resize(ns, 0.f); + formulas.resize(ns); + writeCoeffs(); + } + config_ = config; + name_ = name; + pult_ = _pult; + k_protocol = 0; + renew(); + //readCoeffs(); +} + + +KX_Coefficients::~KX_Coefficients() { + stop(); + k_protocol->stop(); + delete k_protocol; +} + + +void KX_Coefficients::readCoeffs() { + { + PIConfig conf(fname, PIIODevice::ReadOnly); + int cnt = conf.rootEntry().childCount(); + K.resize(cnt); + formulas.resize(cnt); + PIString kn, cf; + for (int i = 0; i < cnt; i++) { + kn = "k" + PIString::fromNumber(i); + K[i] = (double)conf.getValue(kn, 0.f); + cf = conf.getValue(kn + ".formula", ""); + if (cf.isEmpty()) cf = PIString::fromNumber(K[i]); + formulas[i] = cf; + } + } + k_content = PIFile(fname, PIIODevice::ReadOnly).readAll(); + //piCout << k_content.size(); + piCoutObj << "Readed" << K.size_s() << "coefficients in" << k_content.size_s() << "bytes"; + coeffs_cnt = K.size_s(); +} + + +void KX_Coefficients::writeCoeffs() { + PIFile::remove(fname); + PIFile conf(fname, PIIODevice::ReadWrite); + //conf.clear(); + for (int i = 0; i < K.size_s(); i++) { + conf << "k" + PIString::fromNumber(i) << " = " << PIString::fromNumber(K[i]).replaceAll(",", ".") << "\n"; + conf << "k" + PIString::fromNumber(i) << ".formula = " << formulas[i] << "\n"; + } + //conf.close(); + //conf.writeAll(); + k_content = conf.readAll(); + //piCout << k_content.size(); + piCoutObj << "Written" << K.size_s() << "coefficients in" << k_content.size_s() << "bytes"; + coeffs_cnt = K.size_s(); +} + + +void KX_Coefficients::setKFile(const PIString & f) { + fname = f; + readCoeffs(); +} + + +void KX_Coefficients::sendCoeffs() { + stop(); + int curcnt, pcnt = (k_content.size_s() - 1) / __BYTES_IN_PACKET + 1; + k_protocol->to_k.full_count = k_content.size_s(); + for (int i = 0; i < pcnt; i++) { + k_protocol->to_k.first_index = i * __BYTES_IN_PACKET; + if (i == pcnt - 1) curcnt = k_content.size_s() % __BYTES_IN_PACKET; + else curcnt = __BYTES_IN_PACKET; + //cout << "send " << curcnt << " coeffs\n"; + k_protocol->to_k.count = curcnt; + memcpy(k_protocol->to_k.coeffs, k_content.data(k_protocol->to_k.first_index), curcnt); + //for (int j = 0; j < curcnt; j++) k_protocol->to_k.coeffs[j] = K.at(k_protocol->to_k.first_index + j); + k_protocol->send(); + piMSleep(5); + } + //cout << "waiting for commit ...\n"; + waitingCommit = true; + tm.reset(); + start(10); +} + + +void KX_Coefficients::receiveCoeffs() { + stop(); + k_protocol->to_k.full_count = k_protocol->to_k.first_index = k_protocol->to_k.count = -6; + k_protocol->send(); + //cout << "request send\n"; + //cout << "waiting for receive ...\n"; + waitingCommit = false; + tm.reset(); + start(10); +} + + +void KX_Coefficients::received(bool correct) { + //cout << "kpi received " << correct << endl; + if (!correct) return; + k_syns = k_protocol->from_k; + if (waitingCommit) { + if (k_syns.first_index != -1 || + k_syns.full_count != -1 || + k_syns.count != -k_content.size_s()) return; + //cout << "commit received\n"; + waitingCommit = false; + stop(); + //cout << "send succeed\n"; + //emit sendSucceed(); + sendSucceed(); + return; + } else { + if (k_syns.first_index == -6 && k_syns.full_count == -6 && k_syns.count == -6) { + //cout << "request received\n"; + sendCoeffs(); + return; + }; + } + if (k_syns.first_index < 0 || k_syns.count < 0) return; + if (k_syns.first_index == 0) { + //cout << "first receive\n"; + k_tmp.resize(k_syns.full_count); + received_cnt = 0; + } + //for (int i = 0; i < k_syns.count; i++) K.at(k_syns.first_index + i) = k_syns.coeffs[i]; + //piCout << k_tmp.size() << k_syns.first_index << k_syns.count; + memcpy(k_tmp.data(k_syns.first_index), k_syns.coeffs, k_syns.count); + received_cnt += k_syns.count; + tm.reset(); + //cout << "received " << k_syns.count << " bytes with " << k_syns.first_index << " first\n"; + if (received_cnt < k_syns.full_count) return; + //cout << "finally received " << received_cnt << " bytes\n"; + stop(); + PIFile::remove(fname); + PIFile conf(fname, PIIODevice::ReadWrite); + conf.write(k_tmp.data(), k_tmp.size_s()); + conf.close(); + readCoeffs(); + //writeCoeffs(); + k_protocol->to_k.full_count = k_protocol->to_k.first_index = -1; + k_protocol->to_k.count = -received_cnt; + k_protocol->send(); + waitingCommit = false; + //emit receiveSucceed(); + receiveSucceed(); + //cout << "receive succeed\n"; +} + + +void KX_Coefficients::renew() { + if (k_protocol != 0) { + k_protocol->stop(); + delete k_protocol; + } + k_protocol = new __KPi(config_, name_, pult_); + CONNECT1(void, bool, k_protocol, received, this, received) + waitingCommit = false; +} + + +void KX_Coefficients::tick(void * , int ) { + //piCout << "timer" << tm.elapsed_s(); + if (tm.elapsed_s() < 10.) return; + if (waitingCommit) { + //cout << "commit timeout\n"; + //cout << "send failed\n"; + //emit sendFailed(); + sendFailed(); + } else { + //cout << "receive timeout\n"; + //cout << "receive failed\n"; + //emit receiveFailed(); + receiveFailed(); + } + waitingCommit = false; + stop(); +} diff --git a/test/kx_utils/kx_coeffs.h b/test/kx_utils/kx_coeffs.h new file mode 100644 index 0000000..e6d31b7 --- /dev/null +++ b/test/kx_utils/kx_coeffs.h @@ -0,0 +1,95 @@ +#ifndef KX_COEFFS_H +#define KX_COEFFS_H + +#include "piprotocol.h" + +#define __BYTES_IN_PACKET 4000 + +extern PIVector K; + +class __KPi: public PIProtocol +{ + PIOBJECT_SUBCLASS(__KPi, PIProtocol) +public: +#pragma pack (push, 1) + struct KX_K_Sync { + uchar type; + uchar addr_to; + int count; + int full_count; + int first_index; + uchar coeffs[__BYTES_IN_PACKET]; + uint checksum; + }; +#pragma pack (pop) + __KPi(const PIString & config, const PIString & name, bool _pult = false): PIProtocol(config, name, &from_k, 2, &(from_k.count), sizeof(from_k) - 2, &to_k, sizeof(to_k)) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry ce = conf.getValue(name); + to_k.type = from_k.type = ce.getValue("type", 0xA); + to_k.addr_to = ce.getValue("addr_k", 0x15); + from_k.addr_to = ce.getValue("addr_pult", 0x75); + if (_pult) piSwap(to_k.addr_to, from_k.addr_to); + packetExtractor()->setHeader(PIByteArray(&from_k, 2)); + start(); + } + KX_K_Sync to_k, from_k; +private: + bool validate() { + return (from_k.checksum == checksum_i(&from_k, sizeof(from_k) - 4)); + } + bool aboutSend() { + to_k.checksum = checksum_i(&to_k, sizeof(to_k) - 4); + return true; + } +}; + + +class KX_Coefficients: public PITimer +{ + PIOBJECT_SUBCLASS(KX_Coefficients, PITimer) + friend class KX_Pult; +public: + KX_Coefficients(const PIString & config, const PIString & name = "k", bool _pult = false); + ~KX_Coefficients(); + + void readCoeffs(); + void writeCoeffs(); + + void sendCoeffs(); + void receiveCoeffs(); + + bool isReady() const {return !waitingCommit;} + const PIString & fileName() const {return fname;} + const PIString * fileName_ptr() const {return &fname;} + int coeffsCount() {coeffs_cnt = K.size_s(); return coeffs_cnt;} + const int * coeffsCount_ptr() const {return &coeffs_cnt;} + + const PIString & formula(int index) const {return formulas[index];} + void setFormula(int index, const PIString & f) {formulas[index] = f;} + + void setKFile(const PIString & f); + + EVENT(sendSucceed) + EVENT(sendFailed) + EVENT(receiveSucceed) + EVENT(receiveFailed) + + + __KPi * k_protocol; + +private: + EVENT_HANDLER1(void, received, bool, correct); + void renew(); + void tick(void *, int); + + __KPi::KX_K_Sync k_syns; + bool pult_, waitingCommit; + int received_cnt, coeffs_cnt; + PIString fname, config_, name_; + PIStringList formulas; + PIByteArray k_content, k_tmp; + PITimeMeasurer tm; + +}; + +#endif // KX_COEFFS_H diff --git a/test/kx_utils/kx_protocol_c.h b/test/kx_utils/kx_protocol_c.h new file mode 100644 index 0000000..66a5441 --- /dev/null +++ b/test/kx_utils/kx_protocol_c.h @@ -0,0 +1,126 @@ +#ifndef KX_PROTOCOL_C_H +#define KX_PROTOCOL_C_H + +#include "piprotocol.h" + + +#pragma pack (push, 1) + + +struct KX_C_Header { + uchar type; + uchar addr_to; +}; + + + +struct KX_C_Command: KX_C_Header { + KX_C_Command() {command = -1;} + int command; + uint checksum; +}; + + +struct KX_C_Event: KX_C_Header { + KX_C_Event() {event = -1;} + PISystemTime time; + int event; + uint checksum; +}; + + +#pragma pack (pop) + + +// Client side + +class KX_Protocol_C: public PIProtocol +{ + PIOBJECT_SUBCLASS(KX_Protocol_C, PIProtocol) +public: + KX_Protocol_C(const PIString & config, const PIString & name = "c"): PIProtocol(config, name, &from_buff, 2, &(from_buff.command), sizeof(from_buff) - 2, &to_pult, sizeof(to_pult)) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry ce = conf.getValue(name); + to_pult.type = from_buff.type = ce.getValue("type", 0xC); + to_pult.addr_to = ce.getValue("addr_pult", 0x15); + from_buff.addr_to = ce.getValue("addr_c", 0x75); + from_pult = from_buff; + packetExtractor()->setSplitMode(PIPacketExtractor::Header); + packetExtractor()->setHeader(PIByteArray(&from_buff, 2)); + startReceive(); + } + + void sendEvent(int ev) { + to_pult.time = PISystemTime::current(); + to_pult.event = ev; + send(); + } + EVENT1(commandReceived, int, command) + + KX_C_Command from_pult; + KX_C_Event to_pult; + +private: + bool validate() { + if (checksum_i(&from_buff, sizeof(from_buff) - 4) != from_buff.checksum) return false; + from_pult = from_buff; + commandReceived(from_pult.command); + return true; + } + bool aboutSend() { + to_pult.checksum = checksum_i(&to_pult, sizeof(to_pult) - 4); + return true; + } + + KX_C_Command from_buff; + +}; + + + +// Pult side + +class __KX_Protocol_C: public PIProtocol +{ + PIOBJECT_SUBCLASS(__KX_Protocol_C, PIProtocol) +public: + __KX_Protocol_C(const PIString & config, const PIString & name): PIProtocol(config, name, &from_buff, 2, &(from_buff.time), sizeof(from_buff) - 2, &to_c, sizeof(to_c)) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry ce = conf.getValue(name); + to_c.type = from_buff.type = ce.getValue("type", 0xC); + to_c.addr_to = ce.getValue("addr_c", 0x75); + from_buff.addr_to = ce.getValue("addr_pult", 0x15); + from_c = from_buff; + packetExtractor()->setSplitMode(PIPacketExtractor::Header); + packetExtractor()->setHeader(PIByteArray(&from_buff, 2)); + startReceive(); + } + + void sendCommand(int cmd) { + to_c.command = cmd; + send(); + } + EVENT2(eventReceived, int, event, PISystemTime, time) + + KX_C_Event from_c; + KX_C_Command to_c; + +private: + bool validate() { + if (checksum_i(&from_buff, sizeof(from_buff) - 4) != from_buff.checksum) return false; + from_c = from_buff; + eventReceived(from_c.event, from_c.time); + return true; + } + bool aboutSend() { + //piCout << "send command" << to_c.command; + to_c.checksum = checksum_i(&to_c, sizeof(to_c) - 4); + return true; + } + + KX_C_Event from_buff; + +}; + + +#endif // KX_PROTOCOL_C_H diff --git a/test/kx_utils/kx_protocol_x.h b/test/kx_utils/kx_protocol_x.h new file mode 100644 index 0000000..4805a50 --- /dev/null +++ b/test/kx_utils/kx_protocol_x.h @@ -0,0 +1,121 @@ +#ifndef KX_PROTOCOL_X_H +#define KX_PROTOCOL_X_H + +#include "piprotocol.h" + +#define KX_X_PACKET_NUM 20 +#define KX_X_COUNT 16384 + +extern double X[KX_X_COUNT]; + + +#pragma pack (push, 1) + + +struct KX_X_Header { + uchar type; + uchar addr_to; +}; + + + +struct KX_X_Numbers: KX_X_Header { + int x_num[KX_X_PACKET_NUM]; + uint checksum; +}; + + +struct KX_X_Data: KX_X_Header { + int x_num[KX_X_PACKET_NUM]; + double x_data[KX_X_PACKET_NUM]; + uint checksum; +}; + + +#pragma pack (pop) + + +// Client side + +class KX_Protocol_X: public PIProtocol +{ + PIOBJECT_SUBCLASS(KX_Protocol_X, PIProtocol) +public: + KX_Protocol_X(const PIString & config, const PIString & name = "x"): PIProtocol(config, name, &from_buff, 2, &(from_buff.x_num[0]), sizeof(from_buff) - 2, &to_pult, sizeof(to_pult)) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry ce = conf.getValue(name); + to_pult.type = from_buff.type = ce.getValue("type", 0xB); + to_pult.addr_to = ce.getValue("addr_pult", 0x15); + from_buff.addr_to = ce.getValue("addr_x", 0x75); + from_pult = from_buff; + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + to_pult.x_num[i] = from_pult.x_num[i] = -1; + packetExtractor()->setSplitMode(PIPacketExtractor::Header); + packetExtractor()->setHeader(PIByteArray(&from_buff, 2)); + start(); + } + + KX_X_Numbers from_pult; + KX_X_Data to_pult; + +private: + bool validate() { + if (checksum_i(&from_buff, sizeof(from_buff) - 4) != from_buff.checksum) return false; + from_pult = from_buff; + return true; + } + bool aboutSend() { + for (int i = 0; i < KX_X_PACKET_NUM; ++i) { + to_pult.x_num[i] = from_pult.x_num[i]; + to_pult.x_data[i] = from_pult.x_num[i] < 0 ? 0. : X[from_pult.x_num[i]]; + } + to_pult.checksum = checksum_i(&to_pult, sizeof(to_pult) - 4); + return true; + } + + KX_X_Numbers from_buff; + +}; + + + +// Pult side + +class __KX_Protocol_X: public PIProtocol +{ + PIOBJECT_SUBCLASS(__KX_Protocol_X, PIProtocol) +public: + __KX_Protocol_X(const PIString & config, const PIString & name): PIProtocol(config, name, &from_buff, 2, &(from_buff.x_num[0]), sizeof(from_buff) - 2, &to_x, sizeof(to_x)) { + PIConfig conf(config, PIIODevice::ReadOnly); + PIConfig::Entry ce = conf.getValue(name); + to_x.type = from_buff.type = ce.getValue("type", 0xB); + to_x.addr_to = ce.getValue("addr_x", 0x75); + from_buff.addr_to = ce.getValue("addr_pult", 0x15); + from_x = from_buff; + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + to_x.x_num[i] = -1; + packetExtractor()->setSplitMode(PIPacketExtractor::Header); + packetExtractor()->setHeader(PIByteArray(&from_buff, 2)); + start(); + } + + KX_X_Data from_x; + KX_X_Numbers to_x; + +private: + bool validate() { + if (checksum_i(&from_buff, sizeof(from_buff) - 4) != from_buff.checksum) return false; + from_x = from_buff; + return true; + } + bool aboutSend() { + to_x.checksum = checksum_i(&to_x, sizeof(to_x) - 4); + return true; + } + + KX_X_Data from_buff; + +}; + + +#endif // KX_PROTOCOL_X_H diff --git a/test/kx_utils/kx_pult.conf b/test/kx_utils/kx_pult.conf new file mode 100644 index 0000000..e69de29 diff --git a/test/kx_utils/kx_pult.cpp b/test/kx_utils/kx_pult.cpp new file mode 100644 index 0000000..2e5fab9 --- /dev/null +++ b/test/kx_utils/kx_pult.cpp @@ -0,0 +1,848 @@ +#include +#include +#include +#include "kx_pult.h" +#include "ui_kx_pult.h" +#include "piqt.h" +#include "qpiconfig.h" + + +bool isNormalDouble(const double & v) { +#ifdef WINDOWS + return true; +#else + return !isnan(v) && !isinf(v); +#endif +} + + +XCheck::XCheck(int index): QWidget() { + index_ = index; + setProperty("index", index); + setMouseTracking(true); + check.setText(QString::number(index + 1) + " "); + check.setAutoFillBackground(true); + spin.setMaximum(KX_X_COUNT - 1); + QBoxLayout * l = new QBoxLayout(QBoxLayout::LeftToRight); + l->setMargin(0); + l->setSpacing(2); + l->addWidget(&check); + l->addWidget(&spin); + setLayout(l); + //check.installEventFilter(this); + //spin.installEventFilter(this); + connect(&spin, SIGNAL(valueChanged(int)), this, SLOT(spinChanged(int))); + connect(&check, SIGNAL(toggled(bool)), this, SLOT(checkChanged_(bool))); +} + + +bool XCheck::eventFilter(QObject * o, QEvent * e) { + if (e->type() == QEvent::Enter) + qApp->postEvent(this, new QEvent(e->type())); + return QWidget::eventFilter(o, e); +} + + + + +KX_Pult::KX_Pult(): QMainWindow(), config_("kx_pult.conf"), name_x("x"), name_c("c"), +config(piqt(config_), QIODevice::ReadWrite), coeffs(config_, "k", true) { + //cout << sizeof(coeffsK.k_protocol->to_k) << endl; + ui = new Ui::KX_Pult(); + ui->setupUi(this); + ui->configWidget->setQPIConfig(&config); + ui->configWidget->expandAll(); + ui->list->viewport()->installEventFilter(this); + ui->treeK->viewport()->installEventFilter(this); + ui->scrollArea->setAutoFillBackground(false); + ui->scrollAreaWidgetContents->setAutoFillBackground(false); + ui->widget->setAutoFillBackground(false); + ui->label_17->setFixedSize(preferredIconSize(1.25, this)); + log_menu.addAction(ui->actionClear); + prot_x = 0; + prot_c = 0; + show_x = config.getValue("show_x", true); + if (!show_x) + ui->tabWidget->removeTab(1); + session.setFile("session_KX_Pult.conf"); + session.addEntry(this); + session.addEntry(ui->tabWidget); + session.addEntry(ui->checkKHideEmpty); + session.addEntry(ui->checkKHideNormal); + session.addEntry(ui->checkKHideExpressions); + session.addEntry(ui->checkKAutoCalculate); + needWrite = isPause = false; + timer = 0; + //x.resize(KX_X_PACKET_NUM); + //k.resize(K_NUM); + QPalette pal = palette(); + QColor col; + ui->graphic->setGraphicsCount(0); + for (int i = 0; i < KX_X_PACKET_NUM; ++i) { + XCheck * xc = new XCheck(i); + xc->installEventFilter(this); + connect(xc, SIGNAL(valueChanged(int, int)), this, SLOT(changedX(int,int))); + connect(xc, SIGNAL(checkChanged(int, bool)), this, SLOT(toggledX(int, bool))); + col = QColor::fromHsv(360 / KX_X_PACKET_NUM * i, 255, 200); + pal.setColor(QPalette::Button, col); + pal.setColor(QPalette::Window, col); + pal.setColor(QPalette::WindowText, invertColor(col)); + xc->check.setPalette(pal); + ui->graphic->addGraphic(QString::number(i), col); + ui->graphic->setGraphicVisible(false, i); + checks << xc; + ((QGridLayout * )(ui->widgetChecks->layout()))->addWidget(xc, (i / 10) * 2, i % 10); + QLabel * lbl = new QLabel("0"); lbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + values << lbl; + ((QGridLayout * )(ui->widgetChecks->layout()))->addWidget(lbl, (i / 10) * 2 + 1, i % 10); + //xc->show(); + } + renew(); + icon_record = QIcon(":/icons/media-record.png"); + icon_stop = QIcon(":/icons/media-playback-stop.png"); + outdir = dir.absolutePath(); + if (!dir.exists()) dir.mkdir(outdir); + outdir += "/"; + ui->treeK->setColumnWidth(0, 60); + ui->treeK->setColumnWidth(1, 250); + ui->treeK->setColumnWidth(3, 100); + ui->treeK->setColumnWidth(4, 100); + //ui->table->setK(coeffsK.k()->data(), coeffsK.count()); + ui->spinSize->setValue(K.size_s()); + addToList(trUtf8("Read K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(coeffs.fileName())).arg(K.size_s()).arg(coeffs.k_content.size_s()), Qt::darkMagenta); + CONNECT(void, &coeffs, sendFailed, this, pip_sendFailed); + CONNECT(void, &coeffs, sendSucceed, this, pip_sendSucceed); + CONNECT(void, &coeffs, receiveFailed, this, pip_receiveFailed); + CONNECT(void, &coeffs, receiveSucceed, this, pip_receiveSucceed); + connect(this, SIGNAL(q_k_sendFailed()), this, SLOT(k_sendFailed()), Qt::QueuedConnection); + connect(this, SIGNAL(q_k_sendSucceed()), this, SLOT(k_sendSucceed()), Qt::QueuedConnection); + connect(this, SIGNAL(q_k_receiveFailed()), this, SLOT(k_receiveFailed()), Qt::QueuedConnection); + connect(this, SIGNAL(q_k_receiveSucceed()), this, SLOT(k_receiveSucceed()), Qt::QueuedConnection); + connect(&timer_diag, SIGNAL(timeout()), this, SLOT(updateDiag())); + connect(&session, SIGNAL(loading(QPIConfig&)), this, SLOT(loading(QPIConfig&))); + connect(&session, SIGNAL(saving(QPIConfig&)), this, SLOT(saving(QPIConfig&))); + connect(ui->checkKHideEmpty, SIGNAL(toggled(bool)), this, SLOT(filterTree())); + connect(ui->checkKHideNormal, SIGNAL(toggled(bool)), this, SLOT(filterTree())); + connect(ui->checkKHideExpressions, SIGNAL(toggled(bool)), this, SLOT(filterTree())); + connect(ui->lineKSearch, SIGNAL(textChanged(QString)), this, SLOT(filterTree())); + session.load(); + updateKDesc(); + updateCDesc(); + timer_diag.start(40); + timer_update = startTimer(25); +} + + +KX_Pult::~KX_Pult() { + session.save(); +} + + +void KX_Pult::loading(QPIConfig & conf) { + kdesc_file = conf.getValue("kdesc_file").stringValue(); + cdesc_file = conf.getValue("cdesc_file").stringValue(); +} + + +void KX_Pult::saving(QPIConfig & conf) { + conf.setValue("kdesc_file", kdesc_file); + conf.setValue("cdesc_file", cdesc_file); +} + + +bool KX_Pult::eventFilter(QObject * o, QEvent * e) { + if (o == ui->list->viewport()) { + if (e->type() == QEvent::ContextMenu) { + clear_target = 0; + log_menu.popup(((QContextMenuEvent*)e)->globalPos()); + } + return QMainWindow::eventFilter(o, e); + } + if (o == ui->treeK->viewport()) { + if (e->type() == QEvent::ContextMenu) { + clear_target = 1; + log_menu.popup(((QContextMenuEvent*)e)->globalPos()); + } + return QMainWindow::eventFilter(o, e); + } + int ind = o->property("index").toInt(); + //qDebug() << "event" << i << e->type(); + switch (e->type()) { + case QEvent::Enter: + ui->graphic->setAutoUpdate(false); + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + ui->graphic->setGraphicLineWidth(ind == i ? 3. : 1., i); + ui->graphic->setAutoUpdate(true); + ui->graphic->update(); + break; + case QEvent::Leave: + ui->graphic->setAutoUpdate(false); + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + ui->graphic->setGraphicLineWidth(1., i); + ui->graphic->setAutoUpdate(true); + ui->graphic->update(); + break; + default: break; + } + return QMainWindow::eventFilter(o, e); +} + + +void KX_Pult::timerEvent(QTimerEvent * e) { + if (e->timerId() == timer_update) { + if (need_update) { + need_update = false; + ui->graphic->updateGraphics(); + } + } + if (e->timerId() == timer) { + static QString sPI = QString::number(atan(1) * 4., 'f', 14).leftJustified(14); + static int cnt = 0; + int si = qMax(cnt - 6, 0); + ++cnt; + cnt %= 23; + ui->labelWait->setText(QString(si, QChar(' ')) + sPI.mid(cnt - 6, 6).trimmed()); + if (!coeffs.isReady()) return; + //ui->table->setK(coeffsK.k()->data(), coeffsK.count()); + //ui->table->showK(); + } +} + + +void KX_Pult::setControlsEnable(bool enable) { + foreach (XCheck * i, checks) + i->setEnabled(enable); + ui->buttonShowAll->setEnabled(enable); + ui->buttonHideAll->setEnabled(enable); +} + + +void KX_Pult::setX(const KX_X_Data & data) { + if (!show_x) return; + //ui->graphic->lock(); + for (int i = 0; i < KX_X_PACKET_NUM; ++i) { + if (!isNormalDouble(data.x_data[i])) continue; + ui->graphic->addPoint(data.x_data[i], i, false); + values[i]->setText(QString("(%1): %2").arg(data.x_num[i]).arg(data.x_data[i])); + } + //ui->graphic->unlock(); + if (!isPause) { + need_update = true; + } + if (!needWrite) return; + stream << QString::number(tm.elapsed() / 1000., 'f', 3) << " " << QTime::currentTime().toString("hh:mm:ss") << " " << wcnt++; + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + stream << " " << QString::number(data.x_data[i], 'f', 4); + stream << "\n"; +} + + +void KX_Pult::addToList(const QString & s, const QColor & c) { + ui->list->addItem(QDateTime::currentDateTime().toString("dd/MM/yyyy hh:ss - ") + s); + ui->list->item(ui->list->count() - 1)->setTextColor(c); + ui->list->scrollToBottom(); +} + + +void KX_Pult::on_buttonRecord_clicked() { + static bool isRec = false; + int cinc = 0; + QString str; + isRec = !isRec; + setControlsEnable(!isRec); + if (isRec) { + tm.restart(); + file.close(); + file.setFileName(outdir + getNewFileName(cinc)); + while (file.exists()) + file.setFileName(outdir + getNewFileName(cinc++)); + file.open(QIODevice::ReadWrite); + stream.setDevice(&file); + stream << "T V C"; + for (int i = 0; i < KX_X_PACKET_NUM; ++i) + stream << " X" + QString::number(checks[i]->spin.value()); + stream << "\n"; + wcnt = 0; + needWrite = true; + ui->buttonRecord->setText(trUtf8("Finish record")); + ui->buttonRecord->setIcon(icon_stop); + emit recordStarted(QFileInfo(file).completeBaseName()); + } else { + needWrite = false; + stream.setDevice(0); + file.close(); + ui->buttonRecord->setText(trUtf8("Start record")); + ui->buttonRecord->setIcon(icon_record); + emit recordStopped(QFileInfo(file).completeBaseName()); + } +} + + +void KX_Pult::on_actionClear_triggered() { + switch (clear_target) { + case 0: + ui->list->clear(); + break; + case 1: + clearSelected(); + break; + default: break; + } +} + + +void KX_Pult::clearSelected() { + QList si = ui->treeK->selectedItems(); + ui->treeK->setUpdatesEnabled(false); + ui->treeK->blockSignals(true); + foreach (QTreeWidgetItem * i, si) { + int ki = i->text(0).toInt(); + i->setText(2, ""); + coeffs.setFormula(ki, ""); + } + ui->treeK->blockSignals(false); + ui->treeK->setUpdatesEnabled(true); + if (ui->checkKAutoCalculate->isChecked()) { + QApplication::processEvents(); + calculate(); + } +} + + +QString KX_Pult::typeName(const QString & n) const { + if (n.isEmpty()) return ""; + switch (n[0].toLatin1()) { + case 'l': return trUtf8("list"); break; + case 'b': return trUtf8("bool"); break; + case 'n': return trUtf8("int"); break; + case 'f': return trUtf8("double"); break; + case 'c': return trUtf8("color"); break; + case 'r': return trUtf8("rect"); break; + case 'a': return trUtf8("rect"); break; + case 'p': return trUtf8("point"); break; + case 'v': return trUtf8("vector"); break; + case 'i': return trUtf8("IP"); break; + case 'e': return trUtf8("enum"); break; + case 'F': return trUtf8("file"); break; + case 'D': return trUtf8("dir"); break; + } + return ""; +} + + +void KX_Pult::received(bool ok) { + if (!ok) return; + setX(prot_x->from_x); +} + + +void KX_Pult::on_treeK_itemClicked(QTreeWidgetItem * item, int column) { + Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (column == 2) f |= Qt::ItemIsEditable; + item->setFlags(f); +} + + +void KX_Pult::on_treeK_itemChanged(QTreeWidgetItem * item, int column) { + if (column != 2) return; + int ki = item->text(0).toInt(); + coeffs.setFormula(ki, piqt(item->text(column))); + if (ui->checkKAutoCalculate->isChecked()) + calculate(); +} + + +QString KX_Pult::getNewFileName(int inc) { + dir.refresh(); + dir.setNameFilters(QStringList("Experiment_*.txt")); + return "Experiment_" + QDateTime::currentDateTime().toString("dd_MM_yy__hh_mm_ss") + "__" + + QString::number(dir.entryList().count() + inc) + ".txt"; +} + + +void KX_Pult::on_buttonSendK_clicked() { + on_buttonWrite_clicked(); + coeffs.sendCoeffs(); + if (timer != 0) killTimer(timer); + timer = startTimer(100); +} + + +void KX_Pult::on_buttonReceiveK_clicked() { + coeffs.receiveCoeffs(); + if (timer != 0) killTimer(timer); + timer = startTimer(100); +} + + +void KX_Pult::on_buttonShowAll_clicked() { + for (int i = 0; i < KX_X_PACKET_NUM; ++i) { + checks[i]->check.setChecked(true); + //ui->graphic->setGraphicVisible(true, i); + } +} + + +void KX_Pult::on_buttonHideAll_clicked() { + for (int i = 0; i < KX_X_PACKET_NUM; ++i) { + checks[i]->check.setChecked(false); + //ui->graphic->setGraphicVisible(false, i); + } +} + + +void KX_Pult::on_buttonRead_clicked() { + coeffs.readCoeffs(); + addToList(trUtf8("Read K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(coeffs.fileName())).arg(K.size_s()).arg(coeffs.k_content.size_s()), Qt::darkMagenta); + updateTree(); +} + + +void KX_Pult::on_buttonWrite_clicked() { + coeffs.writeCoeffs(); + addToList(trUtf8("Write K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(coeffs.fileName())).arg(K.size_s()).arg(coeffs.k_content.size_s()), Qt::darkMagenta); +} + + +void KX_Pult::on_buttonResize_clicked() { + K.resize(ui->spinSize->value()); + coeffs.formulas.resize(ui->spinSize->value()); + ui->spinSize->setStyleSheet(""); + updateTree(); +} + + +void KX_Pult::on_buttonSetKDesc_clicked() { + QString ret = QFileDialog::getOpenFileName(this, trUtf8("Select *.h file with K description"), kdesc_file, "C/C++ header files(*.h *.hpp);;All files(*)"); + if (ret.isEmpty()) return; + kdesc_file = QDir::current().relativeFilePath(ret); + updateKDesc(true); +} + + +void KX_Pult::on_buttonSetCDesc_clicked() { + QString ret = QFileDialog::getOpenFileName(this, trUtf8("Select *.h file with C description"), cdesc_file, "C/C++ header files(*.h *.hpp);;All files(*)"); + if (ret.isEmpty()) return; + cdesc_file = QDir::current().relativeFilePath(ret); + updateCDesc(); +} + + +void KX_Pult::on_spinSize_valueChanged(int) { + ui->spinSize->setStyleSheet(""); +} + + +void KX_Pult::k_sendFailed() { + stopWait(); + addToList(trUtf8("K not sended"), Qt::darkRed); +} + + +void KX_Pult::k_sendSucceed() { + stopWait(); + addToList(trUtf8("K sended"), Qt::darkGreen); +} + + +void KX_Pult::k_receiveFailed() { + stopWait(); + addToList(trUtf8("K not received"), Qt::darkRed); +} + + +void KX_Pult::k_receiveSucceed() { + stopWait(); + addToList(trUtf8("K received"), Qt::darkGreen); + addToList(trUtf8("Write K file \"%1\": %2 coeffs, %3 bytes").arg(PI2QString(coeffs.fileName())).arg(K.size_s()).arg(coeffs.k_content.size_s()), Qt::darkMagenta); + ui->spinSize->setValue(K.size_s()); + updateTree(); + //ui->table->setK(coeffsK.k()->data(), coeffsK.count()); +} + + +void KX_Pult::on_spinBuffer_editingFinished() { + ui->graphic->setHistorySize(ui->spinBuffer->value()); +} + + +void KX_Pult::stopWait() { + killTimer(timer); + timer = 0; + ui->labelWait->setText(" "); +} + + +void KX_Pult::updateGraph() { + ui->graphic->updateGraphics(); +} + + +void KX_Pult::updateDiag() { + ui->labelKReceiver->setText(piqt(coeffs.k_protocol->receiverDeviceName() + " - " + coeffs.k_protocol->receiverDeviceState())); + ui->labelKSender->setText(piqt(coeffs.k_protocol->senderDeviceName())); + ui->spinKSended->setValue(coeffs.k_protocol->sendCount()); + ui->spinKReceived->setValue(coeffs.k_protocol->receiveCount()); + ui->spinKWrong->setValue(coeffs.k_protocol->wrongCount()); + ui->spinKMissed->setValue(coeffs.k_protocol->missedCount()); + ui->labelKType->setText("0x" + QString::number(coeffs.k_protocol->from_k.type, 16).toUpper().rightJustified(2, '0')); + ui->labelKAddrPult->setText("0x" + QString::number(coeffs.k_protocol->from_k.addr_to, 16).toUpper().rightJustified(2, '0')); + ui->labelKAddr->setText("0x" + QString::number(coeffs.k_protocol->to_k.addr_to, 16).toUpper().rightJustified(2, '0')); + + ui->labelXReceiver->setText(piqt(prot_x->receiverDeviceName() + " - " + prot_x->receiverDeviceState())); + ui->labelXSender->setText(piqt(prot_x->senderDeviceName())); + ui->spinXSended->setValue(prot_x->sendCount()); + ui->spinXReceived->setValue(prot_x->receiveCount()); + ui->spinXWrong->setValue(prot_x->wrongCount()); + ui->spinXMissed->setValue(prot_x->missedCount()); + ui->labelXType->setText("0x" + QString::number(prot_x->from_x.type, 16).toUpper().rightJustified(2, '0')); + ui->labelXAddrPult->setText("0x" + QString::number(prot_x->from_x.addr_to, 16).toUpper().rightJustified(2, '0')); + ui->labelXAddr->setText("0x" + QString::number(prot_x->to_x.addr_to, 16).toUpper().rightJustified(2, '0')); + + ui->labelCReceiver->setText(piqt(prot_c->receiverDeviceName() + " - " + prot_c->receiverDeviceState())); + ui->labelCSender->setText(piqt(prot_c->senderDeviceName())); + ui->spinCSended->setValue(prot_c->sendCount()); + ui->spinCReceived->setValue(prot_c->receiveCount()); + ui->spinCWrong->setValue(prot_c->wrongCount()); + ui->spinCMissed->setValue(prot_c->missedCount()); + ui->labelCType->setText("0x" + QString::number(prot_c->from_c.type, 16).toUpper().rightJustified(2, '0')); + ui->labelCAddrPult->setText("0x" + QString::number(prot_c->from_c.addr_to, 16).toUpper().rightJustified(2, '0')); + ui->labelCAddr->setText("0x" + QString::number(prot_c->to_c.addr_to, 16).toUpper().rightJustified(2, '0')); +} + + +int KX_Pult::parseHeader(const QString & file, QMap & map) { + map.clear(); + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) { + updateTree(); + addToList(trUtf8("Update descriptions from \"%1\": error").arg(file), Qt::darkRed); + return 0; + } + addToList(trUtf8("Update descriptions from \"%1\"").arg(file), Qt::darkMagenta); + QTextStream s(&f); + int cind = -1; + bool found = false; + //qDebug() << "\nparse" << file; + while (!s.atEnd()) { + QString line = s.readLine().trimmed(), num, name, type, comment; + int i = line.indexOf("//"); + if (i >= 0) { + comment = line.right(line.length() - i - 2); + type = comment.left(1); + comment = comment.right(comment.length() - 1).trimmed(); + line = line.left(i).trimmed(); + } + if (line.isEmpty()) continue; + if (line.contains("enum")) { + found = true; + continue; + } + if (!found) continue; + if (line.contains('}')) + break; + line.remove(',').remove(' ').remove('\t'); + i = line.indexOf("="); + if (i >= 0) { + num = line.right(line.length() - i - 1).trimmed(); + line = line.left(i).trimmed(); + } + name = line; + if (num.isEmpty()) + ++cind; + else + cind = Q2PIString(num).toInt(); + KDesc kd; + kd.index = cind; + kd.name = name; + kd.type = type; + kd.comment = comment; + map[kd.index] = kd; + //qDebug() << name << cind << type << comment; + } + cind++; + return cind; +} + + +void KX_Pult::updateKDesc(bool ask_move) { + int cind = parseHeader(kdesc_file, kdesc); + if (K.size_s() < cind) { + ui->spinSize->setValue(cind); + ui->spinSize->setStyleSheet("background-color: rgb(220, 220, 255);"); + } + bool move = false; + if (ask_move) + move = (QMessageBox::question(this, "KX Pult", "Save values at associated names?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes); + updateTree(move); +} + + +void KX_Pult::updateCDesc() { + parseHeader(cdesc_file, cdesc); + updateCommands(); +} + + +bool stringComp(const QString & s1, const QString & s2) { + if (s1.size() != s2.size()) + return s1.size() > s2.size(); + return s1 > s2; +} + + +void KX_Pult::updateTree(bool move) { + int sp = ui->treeK->verticalScrollBar()->value(); + QApplication::setOverrideCursor(Qt::WaitCursor); + //qDebug() << "fill tree ..."; + QMap prev_val; + if (move) { + for (int i = 0; i < ui->treeK->topLevelItemCount(); ++i) { + QTreeWidgetItem * ti = ui->treeK->topLevelItem(i); + if (!ti->text(1).isEmpty()) + prev_val[ti->text(1)] = ti->text(2); + } + } + ui->treeK->clear(); + ui->treeK->setUpdatesEnabled(false); + eval.clearCustomVariables(); + for (int i = 0; i < K.size_s(); ++i) { + QTreeWidgetItem * ti = new QTreeWidgetItem(); + KDesc kd = kdesc[i]; + QString kn = QString("k%1").arg(i); + knames[kn] = i; + knames_sort << kn; + if (eval.content.findVariable(kn) < 0) + eval.content.addVariable(kn, 0., false); + if (!kd.name.isEmpty()) { + knames[kd.name] = i; + knames_sort << kd.name; + eval.content.addVariable(kd.name, 0., false); + } + if (move && !kd.name.isEmpty()) { + if (prev_val.contains(kd.name)) + coeffs.setFormula(i, Q2PIString(prev_val[kd.name])); + } + ti->setText(0, QString::number(i)); + ti->setText(1, kd.name); + ti->setText(2, PI2QString(coeffs.formula(i))); + ti->setText(3, QString::number(K[i])); + ti->setText(4, typeName(kd.type)); + ti->setText(5, kd.comment); + ui->treeK->addTopLevelItem(ti); + } + eval.content.sortVariables(); + //qDebug() << "fill tree ok"; + //qDebug() << "sort ..."; + qSort(knames_sort.begin(), knames_sort.end(), stringComp); + //qDebug() << "names" << knames_sort; + //qDebug() << "sort ok"; + QApplication::restoreOverrideCursor(); + ui->treeK->setUpdatesEnabled(true); + ui->treeK->verticalScrollBar()->setValue(sp); + calculate(); + filterTree(); +} + + +void KX_Pult::updateCommands() { + while (ui->layoutCommands->count() > 0) + delete ui->layoutCommands->itemAt(0)->widget(); + QMapIterator it(cdesc); + while (it.hasNext()) { + it.next(); + KDesc kd = it.value(); + QPushButton * b = new QPushButton(); + QString text = kd.name; + if (!kd.comment.isEmpty()) + text += QString("\n(%1)").arg(kd.comment); + b->setText(text); + b->setProperty("_command", kd.index); + connect(b, SIGNAL(clicked()), this, SLOT(commandClicked())); + ui->layoutCommands->addWidget(b); + } +} + + +void KX_Pult::filterTree() { + bool he = ui->checkKHideEmpty->isChecked(); + bool hn = ui->checkKHideNormal->isChecked(); + bool hs = ui->checkKHideExpressions->isChecked(); + bool ok = false; + QString fl = ui->lineKSearch->text(); + int lc = ui->treeK->topLevelItemCount(); + for (int i = 0; i < lc; ++i) { + QTreeWidgetItem * ti = ui->treeK->topLevelItem(i); + if (ti->text(1).isEmpty() && he) + ti->setHidden(true); + else + if (fl.isEmpty()) + ti->setHidden(false); + else + ti->setHidden(!ti->text(0).contains(fl, Qt::CaseInsensitive) && + !ti->text(1).contains(fl, Qt::CaseInsensitive) && + !ti->text(2).contains(fl, Qt::CaseInsensitive) && + !ti->text(3).contains(fl, Qt::CaseInsensitive) && + !ti->text(4).contains(fl, Qt::CaseInsensitive)); + if (hn) + if (ti->data(0, Qt::UserRole).toBool()) + ti->setHidden(true); + if (hs) { + ti->data(2, Qt::DisplayRole).toDouble(&ok); + if (!ok) + ti->setHidden(true); + } + } +} + + +void KX_Pult::calculate() { + calculated.clear(); + ui->treeK->setUpdatesEnabled(false); + ui->treeK->blockSignals(true); + QApplication::setOverrideCursor(Qt::WaitCursor); + progress(0, 100); + ui->buttonCalculate->setEnabled(false); + QApplication::processEvents(); + for (int i = 0; i < K.size_s(); ++i) { + ui->treeK->topLevelItem(i)->setToolTip(2, QString()); + ui->treeK->topLevelItem(i)->setToolTip(3, QString()); + } + for (int i = 0; i < K.size_s(); ++i) { + progress(i, K.size_s()); + calculateExpression(i, QVector()); + } + ui->buttonCalculate->setEnabled(true); + ui->progress->setValue(100); + QApplication::restoreOverrideCursor(); + ui->treeK->blockSignals(false); + ui->treeK->setUpdatesEnabled(true); +} + + +bool KX_Pult::calculateExpression(int i, QVector trace) { + if (calculated.contains(i)) return true; + trace << i; + QTreeWidgetItem * ti = ui->treeK->topLevelItem(i); + QString expr = ti->text(2); + if (expr.isEmpty() || expr == "0" || expr == "0,00000000" || expr == "0.00000000") { + markNormal(ti); + calculated << i; + K[i] = 0.; + ti->setText(3, "0"); + return true; + } + //ti->setToolTip(2, QString()); + if (!eval.check(expr)) { + markError(ti, eval.error()); + return false; + } + foreach (const QString & n, knames_sort) { + if (expr.contains(n)) { + int ki = knames.value(n, -1); + if (trace.contains(ki)) { + QString strace; + trace << ki; + for (int j = 0; j < trace.size(); ++j) { + //calculated << trace[j]; + if (j > 0) strace += " -> "; + strace += "k" + QString::number(trace[j]); + } + for (int j = 0; j < trace.size(); ++j) { + QTreeWidgetItem * pti = ui->treeK->topLevelItem(trace[j]); + markError(pti, QString("Circular dependency: %1!").arg(strace)); + } + return false; + } + if (ki < 0) { + markError(ti); + return false; + } + if (calculated.contains(ki)) { + eval.setVariable(n, K[ki]); + } else { + if (calculateExpression(ki, trace)) + eval.setVariable(n, K[ki]); + else { + markError(ti); + return false; + } + } + } + } + markNormal(ti); + calculated << i; + complexd ret = eval.evaluate(); + K[i] = ret.real(); + ti->setText(3, QString::number(K[i])); + return true; +} + + +void KX_Pult::markError(QTreeWidgetItem * item, const QString & tool_tip) { + int cc = item->columnCount(); + for (int i = 0; i < cc; ++i) + item->setBackgroundColor(i, QColor(255, 200, 200)); + if (item->toolTip(2).isEmpty()) + item->setToolTip(2, tool_tip); + if (item->toolTip(3).isEmpty()) + item->setToolTip(3, tool_tip); + item->setData(0, Qt::UserRole, false); + item->setText(3, "Error"); +} + + +void KX_Pult::markNormal(QTreeWidgetItem * item) { + int cc = item->columnCount(); + for (int i = 0; i < cc; ++i) + item->setBackground(i, Qt::NoBrush); + item->setToolTip(2, QString()); + item->setToolTip(3, QString()); + item->setData(0, Qt::UserRole, true); +} + + +void KX_Pult::progress(int val, int max) { + if (ctm.elapsed() < 50) return; + ctm.restart(); + ui->progress->setValue(qRound(val * 100. / max)); + QApplication::processEvents(); +} + + +void KX_Pult::renew(bool write) { + addToList(trUtf8("Update settings from \"%1\"").arg(PI2QString(config_)), Qt::darkMagenta); + dir.setPath(config.getValue("x.output_dir", "./Experiments/").stringValue()); + setWindowTitle(config.getValue("title", "Noname").stringValue() + trUtf8(" - KX Pult")); + //if (write) ui->configWidget->write(); + if (prot_x != 0) { + prot_x->stop(); + delete prot_x; + } + if (prot_c != 0) { + prot_c->stop(); + delete prot_c; + } + prot_x = new __KX_Protocol_X(config_, name_x); + prot_c = new __KX_Protocol_C(config_, name_c); + ui->graphic->setAutoXIncrement(prot_x->expectedFrequency() > 0. ? 1. / prot_x->expectedFrequency() : 1.); + coeffs.renew(); + CONNECT1(void, bool, prot_x, received, this, received); +} + + +void KX_Pult::toggledX(int index, bool on) { + ui->graphic->setGraphicVisible(on, index); +} + + +void KX_Pult::changedX(int index, int num) { + prot_x->to_x.x_num[index] = num; +} + + +void KX_Pult::commandClicked() { + QPushButton * b = qobject_cast(sender()); + if (!b) return; + prot_c->sendCommand(b->property("_command").toInt()); +} diff --git a/test/kx_utils/kx_pult.h b/test/kx_utils/kx_pult.h new file mode 100644 index 0000000..2e2d21b --- /dev/null +++ b/test/kx_utils/kx_pult.h @@ -0,0 +1,170 @@ +#ifndef KX_PULT_H +#define KX_PULT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kx_coeffs.h" +#include "kx_protocol_x.h" +#include "kx_protocol_c.h" +#include "piqt.h" +#include "session_manager.h" +#include "qpievaluator.h" + + +class XCheck: public QWidget { + Q_OBJECT +public: + explicit XCheck(int index); + QCheckBox check; + QSpinBox spin; +private: + bool eventFilter(QObject * o, QEvent * e); + int index_; +private slots: + void spinChanged(int value) {check.setChecked(true); emit valueChanged(index_, value);} + void checkChanged_(bool value) {emit checkChanged(index_, value);} +signals: + void valueChanged(int index, int value); + void checkChanged(int index, bool on); +}; + + +namespace Ui { + class KX_Pult; +}; + +class KX_Pult: public QMainWindow, public PIObject +{ + Q_OBJECT + PIOBJECT(KX_Pult) +public: + KX_Pult(); + ~KX_Pult(); + +private: + struct KDesc { + KDesc() {index = -1;} + int index; + QString name; + QString type; + QString comment; + QString value; + }; + + bool eventFilter(QObject * o, QEvent * e); + void timerEvent(QTimerEvent * ); + void setControlsEnable(bool enable); + void setX(const KX_X_Data & data); + void addToList(const QString & s, const QColor & c); + QString getNewFileName(int inc); + QColor invertColor(QColor col) {return QColor(255 - col.red(), 255 - col.green(), 255 - col.blue());} + void stopWait(); + bool calculateExpression(int i, QVector trace); + void markError(QTreeWidgetItem * item, const QString & tool_tip = QString()); + void markNormal(QTreeWidgetItem * item); + void progress(int val, int max); + void clearSelected(); + QString typeName(const QString & n) const; + int parseHeader(const QString & file, QMap & map); + + EVENT_HANDLER1(void, received, bool, ok); + EVENT_HANDLER(void, pip_sendFailed) {emit q_k_sendFailed();} + EVENT_HANDLER(void, pip_sendSucceed) {emit q_k_sendSucceed();} + EVENT_HANDLER(void, pip_receiveFailed) {emit q_k_receiveFailed();} + EVENT_HANDLER(void, pip_receiveSucceed) {emit q_k_receiveSucceed();} + + QVector checks; + QVector values; + + Ui::KX_Pult * ui; + PIString config_, name_x, name_c; + QDir dir; + QString outdir, kdesc_file, cdesc_file; + QFile file; + QTime tm, ctm; + QIcon icon_record, icon_stop; + QTextStream stream; + QTimer timer_diag; + QMap kdesc, cdesc; + QMap knames; + QSet calculated; + QStringList knames_sort; + QPIEvaluator eval; + SessionManager session; + QPIConfig config; + QMenu log_menu; + //QVector k, x; + KX_Coefficients coeffs; + __KX_Protocol_X * prot_x; + __KX_Protocol_C * prot_c; + int csize, wcnt, timer, timer_update, clear_target; + bool needWrite, isPause, need_update, show_x; + +private slots: + void loading(QPIConfig & conf); + void saving(QPIConfig & conf); + void updateGraph(); + void updateDiag(); + void updateKDesc(bool ask_move = false); + void updateCDesc(); + void updateTree(bool move = false); + void updateCommands(); + void filterTree(); + void calculate(); + void renew(bool write = true); + void toggledX(int index, bool on); + void changedX(int index, int num); + void commandClicked(); + void k_sendFailed(); + void k_sendSucceed(); + void k_receiveFailed(); + void k_receiveSucceed(); + void on_spinBuffer_editingFinished(); + void on_buttonSendK_clicked(); + void on_buttonReceiveK_clicked(); + void on_buttonShowAll_clicked(); + void on_buttonHideAll_clicked(); + void on_buttonRead_clicked(); + void on_buttonWrite_clicked(); + void on_buttonResize_clicked(); + void on_buttonSetKDesc_clicked(); + void on_buttonReparseKDesc_clicked() {updateKDesc(true);} + void on_buttonSetCDesc_clicked(); + void on_buttonReparseCDesc_clicked() {updateCDesc();} + void on_buttonCalculate_clicked() {calculate();} + void on_buttonApply_clicked() {renew();} + void on_spinSize_valueChanged(int); + void on_buttonPause_toggled(bool on) {isPause = on;} + void on_buttonRecord_clicked(); + void on_treeK_itemClicked(QTreeWidgetItem * item, int column); + void on_treeK_itemChanged(QTreeWidgetItem * item, int column); + void on_actionClear_triggered(); + +signals: + void q_k_sendFailed(); + void q_k_sendSucceed(); + void q_k_receiveFailed(); + void q_k_receiveSucceed(); + void recordStarted(const QString & fileName); + void recordStopped(const QString & fileName); + +}; + +#endif // KX_PULT_H + diff --git a/test/kx_utils/kx_pult.qrc b/test/kx_utils/kx_pult.qrc new file mode 100644 index 0000000..99f4688 --- /dev/null +++ b/test/kx_utils/kx_pult.qrc @@ -0,0 +1,40 @@ + + + icons/dialog-close.png + icons/edit-clear-.png + icons/edit-guides.png + icons/view-grid.png + icons/zoom-fit-best.png + icons/configure.png + icons/alpha.png + icons/document-save.png + icons/edit-clear-locationbar-rtl.png + icons/edit-find.png + icons/list-add.png + icons/edit-delete.png + icons/item-add.png + icons/item.png + icons/node-add.png + icons/node.png + icons/edit-copy.png + icons/edit-paste.png + icons/expand_s_x.png + icons/expand_s_y.png + icons/expand_x.png + icons/expand_y.png + icons/media-playback-pause.png + icons/media-playback-stop.png + icons/media-record.png + icons/application-exit.png + icons/document-save-.png + icons/view-refresh.png + icons/document-open.png + icons/archive-extract.png + icons/document-export.png + icons/document-import.png + icons/zoom-fit-height.png + icons/layer-visible-off.png + icons/layer-visible-on.png + icons/accessories-calculator.png + + diff --git a/test/kx_utils/kx_pult.ui b/test/kx_utils/kx_pult.ui new file mode 100644 index 0000000..ab30c52 --- /dev/null +++ b/test/kx_utils/kx_pult.ui @@ -0,0 +1,1311 @@ + + + KX_Pult + + + + 0 + 0 + 1759 + 852 + + + + KX Pult + + + + + + + 0 + + + + Coefficients (K) + + + + + + + + Reparse K desc + + + + :/icons/view-refresh.png:/icons/view-refresh.png + + + + + + + Resize + + + + :/icons/zoom-fit-height.png:/icons/zoom-fit-height.png + + + + + + + Read file K + + + + :/icons/archive-extract.png:/icons/archive-extract.png + + + + + + + Send + + + + :/icons/document-export.png:/icons/document-export.png + + + + + + + Write file K + + + + :/icons/document-save-.png:/icons/document-save-.png + + + + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + Receive + + + + :/icons/document-import.png:/icons/document-import.png + + + + + + + Set K desc file ... + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + 65536 + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + + + + + + + + + + + Hide empty + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Hide expressions + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Hide without errors + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + :/icons/edit-find.png + + + true + + + Qt::AlignCenter + + + + + + + Search: + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + Auto calculate + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Calculate + + + Calculate + + + + :/icons/accessories-calculator.png:/icons/accessories-calculator.png + + + + + + + 100 + + + Calculate %p% + + + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + 6 + + + 200 + + + 20 + + + + Index + + + + + Name + + + + + Expression + + + + + Calculated + + + + + Type + + + + + Comment + + + + + + + + + Commands (C) + + + + + + + + Set C desc file ... + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + Reparse C desc + + + + :/icons/view-refresh.png:/icons/view-refresh.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Commands + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 1370 + 655 + + + + + 0 + + + 0 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 66 + 441 + + + + + + + + + + + + + + + + + + Graphics (X) + + + + + + + 10 + + + + + + + + + + + 0 + 0 + + + + Show all + + + + :/icons/layer-visible-on.png:/icons/layer-visible-on.png + + + + + + + + 0 + 0 + + + + Hide all + + + + :/icons/layer-visible-off.png:/icons/layer-visible-off.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Start record + + + + :/icons/media-record.png:/icons/media-record.png + + + + + + + + + + + Pause + + + + :/icons/media-playback-pause.png:/icons/media-playback-pause.png + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show + + + + + + + + 0 + 0 + + + + с + + + 1 + + + 0.100000000000000 + + + 9999.000000000000000 + + + 10.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + Save + + + + + + + + 0 + 0 + + + + с + + + 1 + + + 0.100000000000000 + + + 9999.000000000000000 + + + 60.000000000000000 + + + + + + + + + Graphic::Autofit|Graphic::BorderInputs|Graphic::Clear|Graphic::Configure|Graphic::CursorAxis|Graphic::Grid|Graphic::OnlyExpandX|Graphic::OnlyExpandY|Graphic::Save + + + false + + + false + + + 60.000000000000000 + + + 10.000000000000000 + + + + + + + + Configuration + + + + + + K + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + receiver: + + + + + + + + + + + + + + sender: + + + + + + + + + + + + + + sended count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + wrong received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + type: + + + + + + + + + + + + + + address K: + + + + + + + + + + + + + + + + + + + + + missed received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + address pult: + + + + + + + + + + X + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + receiver: + + + + + + + + + + + + + + sender: + + + + + + + + + + + + + + type: + + + + + + + + + + + + + + address X: + + + + + + + address pult: + + + + + + + + + + + + + + sended count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + wrong received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + + + + + + + + missed received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + + + + C + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + receiver: + + + + + + + + + + + + + + sender: + + + + + + + + + + + + + + type: + + + + + + + + + + + + + + address C: + + + + + + + address pult: + + + + + + + + + + + + + + sended count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + wrong received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + + + + + + + + missed received count: + + + + + + + true + + + 0 + + + 1999999999 + + + + + + + + + + + + true + + + true + + + true + + + false + + + 20 + + + + + + + Apply + + + + :/icons/document-save-.png:/icons/document-save-.png + + + + + + + + + + + + + + + :/icons/edit-clear-.png:/icons/edit-clear-.png + + + Clear + + + + + + Graphic + QFrame +
graphic.h
+
+ + CLineEdit + QLineEdit +
clineedit.h
+
+ + QPIConfigWidget + QTreeWidget +
qpiconfigwidget.h
+
+
+ + + + + + spinHistory + valueChanged(double) + graphic + setMaxVisibleTime(double) + + + 919 + 107 + + + 932 + 132 + + + + +
diff --git a/test/kx_utils/main_kx_pult.cpp b/test/kx_utils/main_kx_pult.cpp new file mode 100644 index 0000000..e5c1786 --- /dev/null +++ b/test/kx_utils/main_kx_pult.cpp @@ -0,0 +1,10 @@ +#include +#include "kx_pult.h" + + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + KX_Pult w; + w.show(); + return a.exec(); +} diff --git a/test/kx_utils/make_lib.bat b/test/kx_utils/make_lib.bat new file mode 100644 index 0000000..0148a43 --- /dev/null +++ b/test/kx_utils/make_lib.bat @@ -0,0 +1 @@ +cmake -G "MinGW Makefiles" -DLIB=1 && make install . %* diff --git a/test/kx_utils/piprotocol.cpp b/test/kx_utils/piprotocol.cpp new file mode 100644 index 0000000..111654e --- /dev/null +++ b/test/kx_utils/piprotocol.cpp @@ -0,0 +1,718 @@ +/* + PIP - Platform Independent Primitives + Protocol, input/output channel (COM, UDP) + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "piprotocol.h" + +/** \class PIProtocol + * \brief + * \details + * \section PIProtocol_sec0 Synopsis + * + * + * + * */ + + /// DEPRECATED +PIProtocol::PIProtocol(const PIString & config, const PIString & name_, void * recHeaderPtr, int recHeaderSize, void * recDataPtr, int recDataSize, void * sendDataPtr_, int sendDataSize_): PIObject() { + init(); + protName = name_; + PIObject::setName(name_); + PIConfig conf(config, PIIODevice::ReadOnly); + if (!conf.isOpened()) { + piCoutObj << "Can`t open \"" << config << "\"!"; + devReceiverState = devSenderState = "Config error"; + return; + } + PIConfig::Entry & b(conf.getValue(name_)), + & rb(b.getValue("receiver")), + & sb(b.getValue("sender")); + + init_receiver(b, rb, config); + init_sender(b, sb, config); + + headerPtr = (uchar * )recHeaderPtr; + headerSize = recHeaderSize; + dataPtr = (uchar * )recDataPtr; + dataSize = recDataSize; + sendDataPtr = (uchar * )sendDataPtr_; + sendDataSize = sendDataSize_; + packet_ext->setHeader(PIByteArray(recHeaderPtr, recHeaderSize)); + packet_ext->setPayloadSize(recDataSize); + packet_ext->setPacketSize(recDataSize); + packet_ext->setSplitMode(PIPacketExtractor::Header); + bool null_h = (recHeaderPtr == 0 || recHeaderSize == 0), null_d = (recDataPtr == 0 || recDataSize == 0); + if (null_h && null_d) packet_ext->setSplitMode(PIPacketExtractor::None); + else { + if (null_h) packet_ext->setSplitMode(PIPacketExtractor::Size); + } +} + + +PIProtocol::~PIProtocol() { + delete diagTimer; + delete sendTimer; + delete secTimer; + delete packet_ext; + if (eth != 0) delete eth; + if (ser != 0) delete ser; +} + + +void PIProtocol::init() { + packet_ext = new PIPacketExtractor(0, PIPacketExtractor::None); + packet_ext->setThreadedReadData(this); + packet_ext->setThreadedReadSlot(receiveEvent); + packet_ext->setHeaderCheckSlot(headerValidateEvent); + packet_ext->setName("__S__PIProtocol::packet_ext"); + work = new_mp_prot = false; + eth = 0; + ser = 0; + ret_func = 0; + mp_owner = 0; + net_diag = PIProtocol::Unknown; + cur_pckt = 0; + packets[0] = packets[1] = pckt_cnt = pckt_cnt_max = 0; + diagTimer = 0; + timeout_ = 3.f; + sendTimer = new PITimer(sendEvent, this); + diagTimer = new PITimer(diagEvent, this); + secTimer = new PITimer(secEvent, this); + sendTimer->setName("__S__PIProtocol::sendTimer"); + diagTimer->setName("__S__PIProtocol::diagTimer"); + secTimer->setName("__S__PIProtocol::secTimer"); + wrong_count = receive_count = send_count = missed_count = 0; + packets_in_sec = packets_out_sec = bytes_in_sec = bytes_out_sec = 0; + immediate_freq = integral_freq = ifreq = 0.f; + headerPtr = dataPtr = sendDataPtr = 0; + headerSize = dataSize = sendDataSize = 0; + type_rec = type_send = PIProtocol::None; + devSenderState = devReceiverState = "Unknown"; + devSenderName = devReceiverName = "no device"; + secTimer->start(1000.); + /*addEvent("receiver started"); + addEvent("receiver stopped"); + addEvent("sender started"); + addEvent("sender stopped"); + addEvent("received"); + addEvent("quality changed"); + addEventHandler(HANDLER(PIProtocol, startReceive)); + addEventHandler(HANDLER(PIProtocol, startSend)); + addEventHandler(HANDLER(PIProtocol, start)); + addEventHandler(HANDLER(PIProtocol, stopReceive)); + addEventHandler(HANDLER(PIProtocol, stopSend)); + addEventHandler(HANDLER(PIProtocol, stop));*/ +} + + +void PIProtocol::init_sender(PIConfig::Entry & b, PIConfig::Entry & sb, const PIString & config) { + int ps, gps; + bool ok, gok, flag, gflag, has_dev = false; + float freq, gfreq; + PIFlags pp(0); + PIString dev, gdev; + + if (sb.isEntryExists("ip") && sb.isEntryExists("device")) { + piCoutObj << "Ambiguous sender type in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + dev = sb.getValue("ip", "", &ok); + gdev = b.getValue("ip", "", &gok); + has_dev = false; + if (ok || gok) { + if (gok && !ok) dev = gdev; + if (gok && ok && (dev != gdev)) { + piCoutObj << "Ambiguous sender type in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + ps = sb.getValue("port", 0, &ok); + gps = b.getValue("port", 0, &gok); + if (ok || gok) { + if (gok && !ok) ps = gps; + if (gok && ok && (ps != gps)) { + piCoutObj << "Ambiguous send port in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + type_send = PIProtocol::Ethernet; + if (eth == 0) eth = new PIEthernet(); + eth->setName("__S__PIProtocol::eth"); + setSenderAddress(dev, ps); + //setReceiverAddress(dev, ps); + has_dev = true; + flag = sb.getValue("reconnectEnabled", true, &ok); + gflag = b.getValue("reconnectEnabled", true, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + eth->setReopenEnabled(flag); + } + freq = sb.getValue("reconnectTimeout", 1., &ok); + gfreq = b.getValue("reconnectTimeout", 1., &gok); + if (ok || gok) { + if (gok && !ok) freq = gfreq; + if (gok && ok && (freq != gfreq)) { + piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + eth->setReopenTimeout(freq * 1000); + } + /*if (sendDataPtr_ == 0) + piCoutObj << "Warning: null send data pointer!"; + if (sendDataSize_ == 0) + piCoutObj << "Warning: null send data size!";*/ + } else { + piCoutObj << "Can`t find \"" << name() << ".sender.port\" or \"" << name() << ".port\" in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + } + dev = sb.getValue("device", "", &ok); + gdev = b.getValue("device", "", &gok); + if (ok || gok) { + if (gok && !ok) dev = gdev; + if (gok && ok && (dev != gdev)) { + piCoutObj << "Ambiguous sender type in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + ps = sb.getValue("speed", 0, &ok); + gps = b.getValue("speed", 0, &gok); + if (ok || gok) { + if (gok && !ok) ps = gps; + if (gok && ok && (ps != gps)) { + piCoutObj << "Ambiguous send \"speed\" in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + flag = sb.getValue("parity", false, &ok); + gflag = b.getValue("parity", false, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous send \"parity\" in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + pp.setFlag(PISerial::ParityControl, flag); + } + flag = sb.getValue("twoStopBits", false, &ok); + gflag = b.getValue("twoStopBits", false, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous send \"twoStopBits\" parity in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + pp.setFlag(PISerial::TwoStopBits, flag); + } + } else { + piCoutObj << "Can`t find \"" << name() << ".sender.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + type_send = PIProtocol::Serial; + if (ser == 0) ser = new PISerial(dev); + ser->setName("__S__PIProtocol::ser"); + setSenderDevice(dev, (PISerial::Speed)ps); + ser->setOutSpeed((PISerial::Speed)ps); + ser->setParameters(pp); + has_dev = true; + /*if (sendDataPtr_ == 0) + piCoutObj << "Warning: null send data pointer!"; + if (sendDataSize_ == 0) + piCoutObj << "Warning: null send data size!";*/ + } + freq = sb.getValue("frequency", -1.f, &ok); + gfreq = b.getValue("frequency", -1.f, &gok); + if (gok && !ok) freq = gfreq; + if (gok && ok && (freq != gfreq)) { + piCoutObj << "Ambiguous sender frequency in \"" << config << "\"!"; + devSenderState = "Config error"; + return; + } + if (freq > 0.f && !has_dev) + piCoutObj << "Warning: no sender device and not null send frequency!"; + setSenderFrequency(freq); +} + + +void PIProtocol::init_receiver(PIConfig::Entry & b, PIConfig::Entry & rb, const PIString & config) { + int ps, gps; + bool ok, gok, flag, gflag, has_dev = false; + float freq, gfreq; + PIFlags pp(0); + PIString dev, gdev; + + if (rb.isEntryExists("ip") && rb.isEntryExists("device")) { + piCoutObj << "Ambiguous receiver type in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + dev = rb.getValue("ip", "", &ok); + gdev = b.getValue("ip", "", &gok); + if (ok || gok) { + if (gok && !ok) dev = gdev; + if (gok && ok && (dev != gdev)) { + piCoutObj << "Ambiguous receiver type in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + ps = rb.getValue("port", 0, &ok); + gps = b.getValue("port", 0, &gok); + if (ok || gok) { + if (gok && !ok) ps = gps; + if (gok && ok && (ps != gps)) { + piCoutObj << "Ambiguous receive port in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + type_rec = PIProtocol::Ethernet; + eth = new PIEthernet(); + eth->setName("__S__PIProtocol::eth"); + packet_ext->setDevice(eth); + //setSenderAddress(dev, ps); + setReceiverAddress(dev, ps); + has_dev = true; + flag = rb.getValue("reconnectEnabled", true, &ok); + gflag = b.getValue("reconnectEnabled", true, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous \"reconnectEnabled\" flag in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + eth->setReopenEnabled(flag); + } + freq = rb.getValue("reconnectTimeout", 1., &ok); + gfreq = b.getValue("reconnectTimeout", 1., &gok); + if (ok || gok) { + if (gok && !ok) freq = gfreq; + if (gok && ok && (freq != gfreq)) { + piCoutObj << "Ambiguous \"reconnectTimeout\" value in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + eth->setReopenTimeout(freq * 1000); + } + /*if (recDataPtr == 0) + piCoutObj << "Warning: null receive data pointer!"; + if (recDataSize == 0) + piCoutObj << "Warning: null receive data size!";*/ + } else { + piCoutObj << "Can`t find \"" << name() << ".receiver.port\" or \"" << name() << ".port\" in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + } + dev = rb.getValue("device", "", &ok); + gdev = b.getValue("device", "", &gok); + if (ok || gok) { + if (gok && !ok) dev = gdev; + if (gok && ok && (dev != gdev)) { + piCoutObj << "Ambiguous receiver type in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + ps = rb.getValue("speed", 0, &ok); + gps = b.getValue("speed", 0, &gok); + if (ok || gok) { + if (gok && !ok) ps = gps; + if (gok && ok && (ps != gps)) { + piCoutObj << "Ambiguous receive \"speed\" in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + flag = rb.getValue("parity", false, &ok); + gflag = b.getValue("parity", false, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous receive \"parity\" in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + pp.setFlag(PISerial::ParityControl, flag); + } + flag = rb.getValue("twoStopBits", false, &ok); + gflag = b.getValue("twoStopBits", false, &gok); + if (ok || gok) { + if (gok && !ok) flag = gflag; + if (gok && ok && (flag != gflag)) { + piCoutObj << "Ambiguous receive \"twoStopBits\" parity in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + pp.setFlag(PISerial::TwoStopBits, flag); + } + type_rec = PIProtocol::Serial; + type_send = PIProtocol::Serial; + ser = new PISerial(dev); + ser->setName("__S__PIProtocol::ser"); + packet_ext->setDevice(ser); + //setSenderDevice(dev, (PISerial::Speed)ps); + setReceiverDevice(dev, (PISerial::Speed)ps); + ser->setInSpeed((PISerial::Speed)ps); + ser->setParameters(pp); + ps = rb.getValue("vtime", 1, &ok); + gps = b.getValue("vtime", 1, &gok); + if (ok || gok) { + if (gok && !ok) ps = gps; + if (gok && ok && (ps != gps)) { + piCoutObj << "Ambiguous receive \"vtime\" in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + ser->setVTime(ps); + } + has_dev = true; + /*if (recDataPtr == 0) + piCoutObj << "Warning: null receive data pointer!"; + if (recDataSize == 0) + piCoutObj << "Warning: null receive data size!";*/ + } else { + piCoutObj << "Can`t find \"" << name() << ".receiver.speed\" or \"" << name() << ".speed\" in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + } + freq = rb.getValue("frequency", -1.f, &ok); + gfreq = b.getValue("frequency", -1.f, &gok); + if (gok && !ok) freq = gfreq; + if (gok && ok && (freq != gfreq)) { + piCoutObj << "Ambiguous expected frequency in \"" << config << "\"!"; + devReceiverState = "Config error"; + return; + } + if (freq > 0.f && !has_dev) + piCoutObj << "Warning: no receiver device and not null expected frequency!"; + float tm = b.getValue("disconnectTimeout", 3.f); + if (tm <= 0.f) + piCoutObj << "Warning: diconnect timeout <= 0 s!"; + timeout_ = (tm < 0.f) ? 0.f : tm; + setExpectedFrequency(freq); +} + + +void PIProtocol::setReceiverDevice(const PIString & device, PISerial::Speed speed, bool force) { + if (force) { + type_send = type_rec = PIProtocol::Serial; + if (ser == 0) { + ser = new PISerial(); + ser->setName("__S__PIProtocol::ser"); + packet_ext->setDevice(ser); + } + } + if (type_rec == PIProtocol::Serial && ser != 0) { + ser->setDevice(device); + ser->setSpeed(speed); + devReceiverName = device; + devSenderName = device; + } +} + + +void PIProtocol::setReceiverAddress(const PIString & ip, int port, bool force) { + if (force) { + type_rec = PIProtocol::Ethernet; + if (eth == 0) { + eth = new PIEthernet(); + eth->setName("__S__PIProtocol::eth"); + packet_ext->setDevice(eth); + } + } + if (type_rec == PIProtocol::Ethernet && eth != 0) { + eth->setReadAddress(ip, port); + if (ip.trimmed().isEmpty()) devReceiverName = "no ip"; + else devReceiverName = ip + ":" + PIString::fromNumber(port); + } +} + + +void PIProtocol::setSenderDevice(const PIString & device, PISerial::Speed speed, bool force) { + if (force) { + type_send = type_rec = PIProtocol::Serial; + if (ser == 0) ser = new PISerial(); + ser->setName("__S__PIProtocol::ser"); + } + if (type_send == PIProtocol::Serial && ser != 0) { + ser->setDevice(device); + ser->setSpeed(speed); + ser->open(); + devSenderName = device; + } +} + + +void PIProtocol::setSenderAddress(const PIString & ip, int port, bool force) { + if (force) { + type_send = PIProtocol::Ethernet; + if (eth == 0) eth = new PIEthernet(); + eth->setName("__S__PIProtocol::eth"); + } + if (type_send == PIProtocol::Ethernet && eth != 0) { + eth->setSendAddress(ip, port); + if (ip.isEmpty()) devSenderName = "no ip"; + else devSenderName = ip + ":" + PIString::fromNumber(port); + } +} + + +void PIProtocol::setSenderIP(const PIString & ip, bool force) { + if (force) { + type_send = PIProtocol::Ethernet; + if (eth == 0) eth = new PIEthernet(); + } + if (type_send == PIProtocol::Ethernet && eth != 0) { + eth->setSendIP(ip); + if (ip.isEmpty()) devSenderName = "no ip"; + else devSenderName = ip + ":" + PIString::fromNumber(eth->sendPort()); + } +} + + +void PIProtocol::setSenderPort(int port, bool force) { + if (force) { + type_send = PIProtocol::Ethernet; + if (eth == 0) eth = new PIEthernet(); + eth->setName("__S__PIProtocol::eth"); + } + if (type_send == PIProtocol::Ethernet && eth != 0) { + eth->setSendPort(port); + if (eth->sendIP().isEmpty()) devSenderName = "no ip"; + else devSenderName = eth->sendIP() + ":" + PIString::fromNumber(port); + } +} + + +void PIProtocol::setExpectedFrequency(float frequency) { + exp_freq = frequency; + changeDisconnectTimeout(); +} + + +void PIProtocol::changeDisconnectTimeout() { + pckt_cnt_max = int(piRound(timeout_ * exp_freq)); + if (pckt_cnt_max < 3) pckt_cnt_max = 3; + last_packets.resize(pckt_cnt_max); +} + + +void PIProtocol::startReceive(float exp_frequency) { + if (exp_frequency > 0.f) exp_freq = exp_frequency; + //if (type_rec == PIProtocol::Serial) ser->start(); + //if (type_rec == PIProtocol::Ethernet) eth->start(); + packet_ext->startThreadedRead(); + msleep(1); + check_state(); + if (exp_freq <= 0.f) return; + setExpectedFrequency(exp_freq); + diagTimer->start(1000. / exp_freq); + diag_tm.reset(); + receiverStarted(); +} + + +void PIProtocol::startSend(float frequency) { + //cout << "** start send " << send_freq << ", " << frequency << endl; + if (frequency > 0.f) send_freq = frequency; + msleep(1); + check_state(); + if (send_freq <= 0.f) return; + sendTimer->start(1000. / send_freq); + diag_tm.reset(); + senderStarted(); +} + + +void PIProtocol::stopReceive() { + //if (type_rec == PIProtocol::Serial) ser->stop(); + //if (type_rec == PIProtocol::Ethernet) eth->stop(); + packet_ext->stop(); + diagTimer->stop(); + receiverStopped(); +} + + +bool PIProtocol::receiveEvent(void * t, uchar * data, int size) { + PIProtocol * p = (PIProtocol * )t; + if (!p->receive(data, size)) return false; + p->work = true; + //p->lock(); + if (p->validate()) { + p->received(true); + //p->unlock(); + p->ifreq = p->diag_tm.elapsed_m(); + if (p->ifreq > 0.) p->ifreq = 1000. / p->ifreq; + p->diag_tm.reset(); + p->receive_count++; + p->packets_in_sec++; + p->bytes_in_sec += size; + p->cur_pckt = 1; + if (p->ret_func != 0) p->ret_func(p); + if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, true, data, size); + return true; + } + p->received(false); + //p->unlock(); + p->wrong_count++; + if (p->mp_owner != 0) PIMultiProtocolBase::receiveEvent(p->mp_owner, p, false, data, size); + return false; +} + + +void PIProtocol::diagEvent(void * t, int) { + PIProtocol * p = (PIProtocol * )t; + p->calc_freq(); + p->calc_diag(); + p->check_state(); + if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets(); +} + + +void PIProtocol::secEvent(void * t, int ) { + PIProtocol * p = (PIProtocol * )t; + p->speedIn = PIString::readableSize(p->bytes_in_sec) + "/s"; + p->speedOut = PIString::readableSize(p->bytes_out_sec) + "/s"; + p->bytes_in_sec = p->bytes_out_sec = p->packets_in_sec = p->packets_out_sec = 0; + if (p->ser != 0) p->missed_count = p->packet_ext->missedPackets(); +} + + +void PIProtocol::calc_diag() { + PIProtocol::Quality diag; + if (!work) { + diag = PIProtocol::Unknown; + return; + } + if (pckt_cnt < pckt_cnt_max) { + last_packets[pckt_cnt] = cur_pckt; + pckt_cnt++; + } else { + packets[(int)last_packets.back()]--; + if (!last_packets.isEmpty()) last_packets.pop_back(); + last_packets.push_front(cur_pckt); + } + packets[(int)cur_pckt]++; + cur_pckt = 0; + float good_percents; + good_percents = (float)packets[1] / pckt_cnt * 100.f; + if (good_percents == 0.f) diag = PIProtocol::Failure; + else if (good_percents <= 20.f) diag = PIProtocol::Bad; + else if (good_percents > 20.f && good_percents <= 80.f) diag = PIProtocol::Average; + else diag = PIProtocol::Good; + if (diag != net_diag) { + qualityChanged(diag, net_diag); + net_diag = diag; + } +} + + +void PIProtocol::calc_freq() { + float tf;// = float(1000.f / diagTimer->elapsed_m()); + tf = immediate_freq = ifreq; + ifreq = 0.f; + if (last_freq.size_s() >= pckt_cnt_max && last_freq.size_s() > 0) last_freq.pop_front(); + last_freq.push_back(tf); + tf = last_freq[0]; + for (uint i = 1; i < last_freq.size(); ++i) + tf += last_freq[i]; + integral_freq = tf / last_freq.size(); +} + + +void PIProtocol::check_state() { + if (type_rec == PIProtocol::Serial) { + if (ser != 0) { + if (ser->isOpened()) devReceiverState = "Opened"; + else devReceiverState = "Not opened"; + } + else devReceiverState = "Not exists"; + } + if (type_rec == PIProtocol::Ethernet) { + if (eth != 0) { + if (eth->isOpened()) devReceiverState = "Opened"; + else devReceiverState = "Not opened"; + } + else devReceiverState = "Not exists"; + } + if (type_send == PIProtocol::Serial) { + if (ser != 0) { + if (ser->isOpened()) devSenderState = "Opened"; + else devSenderState = "Not opened"; + } + else devSenderState = "Not exists"; + } + if (type_send == PIProtocol::Ethernet) { + if (eth != 0) { + if (eth->isOpened()) devSenderState = "Opened"; + else devSenderState = "Not opened"; + } + else devSenderState = "Not exists"; + } +} + + +void PIProtocol::send(const void * data, int size, bool direct) { + if (!direct) { + if (data == 0 || size == 0) return; + if (!aboutSend()) return; + } + if (type_send == PIProtocol::Serial) + if (ser->send(data, size)) { + send_count++; + packets_out_sec++; + bytes_out_sec += size; + } + if (type_send == PIProtocol::Ethernet) + if (eth->send(data, size)) { + send_count++; + packets_out_sec++; + bytes_out_sec += size; + } +} + + +void PIProtocol::send() { + //lock(); + //memcpy(packet, sendDataPtr, sendDataSize); + //unlock(); + if (!aboutSend()) return; + if (sendDataPtr == 0 || sendDataSize == 0) return; + if (type_send == PIProtocol::Serial) + if (ser->send(sendDataPtr, sendDataSize)) { + send_count++; + packets_out_sec++; + bytes_out_sec += sendDataSize; + } + if (type_send == PIProtocol::Ethernet) + if (eth->send(sendDataPtr, sendDataSize)) { + send_count++; + packets_out_sec++; + bytes_out_sec += sendDataSize; + } +} diff --git a/test/kx_utils/piprotocol.h b/test/kx_utils/piprotocol.h new file mode 100644 index 0000000..2c41fa0 --- /dev/null +++ b/test/kx_utils/piprotocol.h @@ -0,0 +1,238 @@ +/*! \file piprotocol.h + * \brief Highly configurable from file I/O channel +*/ +/* + PIP - Platform Independent Primitives + Protocol, input/output channel (COM, UDP) + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PIPROTOCOL_H +#define PIPROTOCOL_H + +#include "piserial.h" +#include "piethernet.h" +#include "pipacketextractor.h" +#include "pitimer.h" +#include "piconfig.h" +#include "pifile.h" + +class PIProtocol; /// DEPRECATED + +class PIP_EXPORT PIMultiProtocolBase: protected PIObject /// DEPRECATED +{ + PIOBJECT_SUBCLASS(PIMultiProtocolBase, PIObject) + friend class PIProtocol; +public: + PIMultiProtocolBase() {;} /// DEPRECATED + virtual ~PIMultiProtocolBase() {;} + +protected: + virtual void received(PIProtocol * prot, bool corrected, uchar * data, int size) {;} + +private: + static void receiveEvent(PIMultiProtocolBase * p, PIProtocol * prot, bool corrected, uchar * data, int size) {p->mutex_receive.lock(); p->received(prot, corrected, data, size); p->mutex_receive.unlock();} + + PIMutex mutex_receive; + +}; + +typedef void (*ReceiveFunc)(void * ); + +/// events: +/// void receiverStarted() +/// void receiverStopped() +/// void senderStarted() +/// void senderStopped() +/// void received(bool validate_is_ok) +/// void qualityChanged(PIProtocol::Quality old_quality, PIProtocol::Quality new_quality) +/// +/// handlers: +/// void startReceive(float exp_frequency = -1.f) +/// void stopReceive() +/// void startSend(float frequency = -1.f) +/// void stopSend() +/// void start() +/// void stop() +/// void send() +/// void send(const void * data, int size, bool direct = false) +class PIP_EXPORT PIProtocol: public PIObject /// DEPRECATED +{ + PIOBJECT_SUBCLASS(PIProtocol, PIObject) + friend class PIMultiProtocolBase; + friend class PIMultiProtocol; + enum Type {None, Serial, Ethernet}; +public: + + //! Contructs an empty unconfigured protocol + PIProtocol(): PIObject() {init();} /// DEPRECATED + + //! Contructs protocol configured from file "config", config file section "name" + PIProtocol(const PIString & config, const PIString & name, void * recHeaderPtr = 0, int recHeaderSize = 0, + void * recDataPtr = 0, int recDataSize = 0, void * sendDataPtr = 0, int sendDataSize = 0); // from config + + virtual ~PIProtocol(); + + //! Connection quality + enum Quality { + Unknown /** Unknown, no one packet received yet */ = 1, + Failure /** No connection, no one correct packet received for last period */ = 2, + Bad /** Bad connection, correct packets received <= 20% */ = 3, + Average /** Average connection, correct packets received > 20% and <= 80% */ = 4, + Good /** Good connection, correct packets received > 80% */ = 5 + }; + + EVENT_HANDLER0(void, startReceive) {startReceive(-1.f);} + EVENT_HANDLER1(void, startReceive, float, exp_frequency); // if "frequency = -1" used last passed value + EVENT_HANDLER0(void, stopReceive); + void setExpectedFrequency(float frequency); // for connection quality diagnostic + void setReceiverDevice(const PIString & device, PISerial::Speed speed, bool force = false); // for Serial + void setReceiverData(void * dataPtr, int dataSize) {this->dataPtr = (uchar * )dataPtr; this->dataSize = dataSize; packet_ext->setHeader(PIByteArray(headerPtr, headerSize)); packet_ext->setPayloadSize(dataSize); packet_ext->setPacketSize(dataSize);} + void setReceiverDataHeader(void * headerPtr, int headerSize) {this->headerPtr = (uchar * )headerPtr; this->headerSize = headerSize; packet_ext->setHeader(PIByteArray(headerPtr, headerSize)); packet_ext->setPayloadSize(dataSize); packet_ext->setPacketSize(dataSize);} + void setReceiverAddress(const PIString & ip, int port, bool force = false); // for Ethernet + void setReceiverParameters(PIFlags parameters) {if (type_rec == PIProtocol::Serial || type_send == PIProtocol::Serial) ser->setParameters(parameters);} // for Serial + void setReceiveSlot(ReceiveFunc slot) {ret_func = slot;} + float expectedFrequency() const {return exp_freq;} + + EVENT_HANDLER0(void, startSend) {startSend(-1.f);} // if "frequency = -1" used last passed value + EVENT_HANDLER1(void, startSend, float, frequency); // if "frequency = -1" used last passed value + EVENT_HANDLER0(void, stopSend) {sendTimer->stop(); senderStopped();} + void setSenderFrequency(float frequency) {send_freq = frequency;} + void setSenderDevice(const PIString & device, PISerial::Speed speed, bool force = false); // for Serial + void setSenderData(void * dataPtr, int dataSize) {sendDataPtr = (uchar * )dataPtr; sendDataSize = dataSize;} + void setSenderAddress(const PIString & ip, int port, bool force = false); // for Ethernet + void setSenderIP(const PIString & ip, bool force = false); // for Ethernet + void setSenderPort(int port, bool force = false); // for Ethernet + void setSenderParameters(PIFlags parameters) {if (type_send == PIProtocol::Serial) ser->setParameters(parameters);} // for Serial + float senderFrequency() const {return send_freq;} + + EVENT_HANDLER0(void, start) {startReceive(); startSend();} + EVENT_HANDLER0(void, stop) {stopReceive(); stopSend();} + EVENT_HANDLER0(void, send); + EVENT_HANDLER2(void, send, const void *, data, int, size) {send(data, size, false);} + EVENT_HANDLER3(void, send, const void *, data, int, size, bool, direct); + + void setName(const PIString & name) {protName = name; PIObject::setName(name);} + PIString name() const {return protName;} + void setDisconnectTimeout(float timeout) {timeout_ = timeout; changeDisconnectTimeout();} + float disconnectTimeout() const {return timeout_;} + const float * disconnectTimeout_ptr() const {return &timeout_;} + float immediateFrequency() const {return immediate_freq;} + float integralFrequency() const {return integral_freq;} + const float * immediateFrequency_ptr() const {return &immediate_freq;} + const float * integralFrequency_ptr() const {return &integral_freq;} + ullong receiveCountPerSec() const {return packets_in_sec;} + const ullong * receiveCountPerSec_ptr() const {return &packets_in_sec;} + ullong sendCountPerSec() const {return packets_out_sec;} + const ullong * sendCountPerSec_ptr() const {return &packets_out_sec;} + ullong receiveBytesPerSec() const {return bytes_in_sec;} + const ullong * receiveBytesPerSec_ptr() const {return &bytes_in_sec;} + ullong sendBytesPerSec() const {return bytes_out_sec;} + const ullong * sendBytesPerSec_ptr() const {return &bytes_out_sec;} + ullong receiveCount() const {return receive_count;} + const ullong * receiveCount_ptr() const {return &receive_count;} + ullong wrongCount() const {return wrong_count;} + const ullong * wrongCount_ptr() const {return &wrong_count;} + ullong sendCount() const {return send_count;} + const ullong * sendCount_ptr() const {return &send_count;} + ullong missedCount() const {return missed_count;} + const ullong * missedCount_ptr() const {return &missed_count;} + PIProtocol::Quality quality() const {return net_diag;} // receive quality + const int * quality_ptr() const {return (int * )&net_diag;} // receive quality pointer + PIString receiverDeviceName() const {return devReceiverName;} + PIString senderDeviceName() const {return devSenderName;} + PIString receiverDeviceState() const {return devReceiverState;} + const PIString * receiverDeviceState_ptr() const {return &devReceiverState;} + PIString senderDeviceState() const {return devSenderState;} + const PIString * senderDeviceState_ptr() const {return &devSenderState;} + PIString receiveSpeed() const {return speedIn;} + const PIString * receiveSpeed_ptr() const {return &speedIn;} + PIString sendSpeed() const {return speedOut;} + const PIString * sendSpeed_ptr() const {return &speedOut;} + + void * receiveData() {return dataPtr;} + void * sendData() {return sendDataPtr;} + + PIPacketExtractor * packetExtractor() {return packet_ext;} +// PIByteArray lastHeader() {return packet_ext->lastHeader();} + + EVENT0(receiverStarted) + EVENT0(receiverStopped) + EVENT0(senderStarted) + EVENT0(senderStopped) + EVENT1(received, bool, validate_is_ok) + EVENT2(qualityChanged, PIProtocol::Quality, new_quality, PIProtocol::Quality, old_quality) + +protected: + virtual bool receive(uchar * data, int size) {if (dataPtr != 0) memcpy(dataPtr, data, size); return true;} // executed when raw data received, break if 'false' return + virtual bool validate() {return true;} // function for validate algorithm and save data from dataPtr to external struct + virtual bool headerValidate(uchar * src, uchar * rec, int size) {for (int i = 0; i < size; ++i) if (src[i] != rec[i]) return false; return true;} // function for validate header (COM-port and headerSize > 0) + virtual uint checksum_i(void * data, int size) { // function for checksum (uint) + uint c = 0; + for (int i = 0; i < size; ++i) + c += ((uchar*)data)[i]; + return ~(c + 1); + } + virtual uchar checksum_c(void * data, int size) { // function for checksum (uchar) + uchar c = 0; + for (int i = 0; i < size; ++i) + c += ((uchar*)data)[i]; + return ~(c + 1); + } + virtual bool aboutSend() {return true;} // executed before send data, if return 'false' then data is not sending + + void init(); + void init_sender(PIConfig::Entry & b, PIConfig::Entry & sb, const PIString & config); + void init_receiver(PIConfig::Entry & b, PIConfig::Entry & rb, const PIString & config); + void check_state(); + void calc_freq(); + void calc_diag(); + + PISerial * ser; + PIEthernet * eth; + uint dataSize, headerSize, sendDataSize; + uchar * dataPtr, * headerPtr, * sendDataPtr; + +private: + static void sendEvent(void * e, int) {((PIProtocol * )e)->send();} + static bool receiveEvent(void * t, uchar * data, int size); + static bool headerValidateEvent(void * t, uchar * src, uchar * rec, int size) {return ((PIProtocol * )t)->headerValidate(src, rec, size);} + static void diagEvent(void * t, int); + static void secEvent(void * t, int); + + void setMultiProtocolOwner(PIMultiProtocolBase * mp) {mp_owner = mp;} + PIMultiProtocolBase * multiProtocolOwner() const {return mp_owner;} + void changeDisconnectTimeout(); + + ReceiveFunc ret_func; + PIPacketExtractor * packet_ext; + PITimer * diagTimer, * sendTimer, * secTimer; + PITimeMeasurer diag_tm; + PIMultiProtocolBase * mp_owner; + PIProtocol::Type type_send, type_rec; + PIProtocol::Quality net_diag; + PIDeque last_freq; + PIDeque last_packets; + PIString protName, devReceiverName, devReceiverState, devSenderName, devSenderState, speedIn, speedOut; + bool work, new_mp_prot; + float exp_freq, send_freq, ifreq, immediate_freq, integral_freq, timeout_; + int packets[2], pckt_cnt, pckt_cnt_max; + char cur_pckt; + ullong wrong_count, receive_count, send_count, missed_count, packets_in_sec, packets_out_sec, bytes_in_sec, bytes_out_sec; + +}; + +#endif // PIPROTOCOL_H diff --git a/test/logo.png b/test/logo.png new file mode 100644 index 0000000..e08b36a Binary files /dev/null and b/test/logo.png differ diff --git a/test/make_android.bat b/test/make_android.bat new file mode 100644 index 0000000..470a994 --- /dev/null +++ b/test/make_android.bat @@ -0,0 +1,45 @@ +@echo off +rem please add to environment ANDROID_QT5_DIR (path to Qt directory) and ANDROID_NDK_HOME (path to android ndk directory) +rem execute with Android API version and arch e. g. : #make_android.bat 24 arm ../libs +set _usage_=0 +if [%~1]==[] set _usage_=1 +if [%~2]==[] set _usage_=1 +if [%~3]==[] set _usage_=1 +if %_usage_%==1 ( + echo Usage: make_android.bat ^ ^ ^ + echo ^ ^ ^ - number of API ^(e.g. 19^) + echo ^ ^ ^ - architecture ^(arm, arm64, x86, x86_64^) + echo ^ ^ ^ - path to sources + echo. + echo You also have to set next env variables^: + echo ^ ^ ANDROID_QT5_DIR - path to Qt dir, contains subdirs for arch + echo ^ ^ ANDROID_NDK_HOME - path to android ndk directory + exit /b +) +set QARCH= +set AARCH= +if %~2==arm ( + set QARCH=armv7 + set AARCH=armeabi-v7a +) +if %~2==arm64 ( + set QARCH=arm64_v8a + set AARCH=arm64-v8a +) +if %~2==x86 ( + set QARCH=x86 + set AARCH=x86 +) +if %~2==x86_64 ( + echo Qt doesn`t support x86_64, building without Qt + set AARCH=x86_64 +) +set Qt5_ROOT=%ANDROID_QT5_DIR%\android_%QARCH%\lib\cmake +set Qt5_DIR=%Qt5_ROOT%\Qt5 +if NOT [%QARCH%]==[] ( + @echo on + cmake_mgw -Wno-dev -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%\build\cmake\android.toolchain.cmake -DANDROID_STL=c++_shared -DANDROID_PLATFORM=android-%~1 -DANDROID_ABI=%AARCH% -DICU=0 -DQt4=0 -DQt5=1 -DQGLVIEW=0 -DQt5_DIR=%Qt5_ROOT%\Qt5 -DQt5LinguistTools_DIR=%Qt5_ROOT%\Qt5LinguistTools -DQt5UiPlugin_DIR=%Qt5_ROOT%\Qt5UiPlugin -DQt5Widgets_DIR=%Qt5_ROOT%\Qt5Widgets -DQt5Core_DIR=%Qt5_ROOT%\Qt5Core -DQt5Gui_DIR=%Qt5_ROOT%\Qt5Gui -DQt5Sql_DIR=%Qt5_ROOT%\Qt5Sql -DQt5OpenGL_DIR=%Qt5_ROOT%\Qt5OpenGL -DQt5PrintSupport_DIR=%Qt5_ROOT%\Qt5PrintSupport -DQt5Script_DIR=%Qt5_ROOT%\Qt5Script -DQt5Positioning_DIR=%Qt5_ROOT%\Qt5Positioning %~3 %~4 %~5 %~6 +) else ( + @echo on + cmake_mgw -Wno-dev -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%\build\cmake\android.toolchain.cmake -DANDROID_STL=c++_shared -DANDROID_PLATFORM=android-%~1 -DANDROID_ABI=%AARCH% -DICU=0 -DQt4=0 -DQt5=0 -DQGLVIEW=0 %~3 %~4 %~5 %~6 +) diff --git a/test/make_android.sh b/test/make_android.sh new file mode 100755 index 0000000..e4e0fef --- /dev/null +++ b/test/make_android.sh @@ -0,0 +1,11 @@ +export Qt5_ROOT=/mnt/data/Qt_5.9.1_linux/5.9.1/android_armv7/lib/cmake +export Qt5_DIR=${Qt5_ROOT}/Qt5 +export Qt5LinguistTools_DIR=${Qt5_ROOT}/Qt5LinguistTools +export Qt5UiPlugin_DIR=${Qt5_ROOT}/Qt5UiPlugin +export Qt5Widgets_DIR=${Qt5_ROOT}/Qt5Widgets +export Qt5Core_DIR=${Qt5_ROOT}/Qt5Core +export Qt5Gui_DIR=${Qt5_ROOT}/Qt5Gui +export Qt5Sql_DIR=${Qt5_ROOT}/Qt5Sql +export Qt5OpenGL_DIR=${Qt5_ROOT}/Qt5OpenGL +export Qt5Designer_DIR=${Qt5_ROOT}/Qt5Designer +rm -rf * && cmake -Wno-dev -DCMAKE_TOOLCHAIN_FILE=/mnt/data/Distrib/Android/android-sdk-linux/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-24 -DICU=0 -DQt4=0 -DQt5=1 -DQt5_DIR=${Qt5_ROOT}/Qt5 -DQt5LinguistTools_DIR=${Qt5_ROOT}/Qt5LinguistTools -DQt5UiPlugin_DIR=${Qt5_ROOT}/Qt5UiPlugin -DQt5Widgets_DIR=${Qt5_ROOT}/Qt5Widgets -DQt5Core_DIR=${Qt5_ROOT}/Qt5Core -DQt5Gui_DIR=${Qt5_ROOT}/Qt5Gui -DQt5Sql_DIR=${Qt5_ROOT}/Qt5Sql -DQt5OpenGL_DIR=${Qt5_ROOT}/Qt5OpenGL ../libs diff --git a/test/make_libs.bat b/test/make_libs.bat new file mode 100644 index 0000000..dd57e1a --- /dev/null +++ b/test/make_libs.bat @@ -0,0 +1,6 @@ +@echo off +set ARCH=%~1 +set PATH=%SDK_MINGW_DIR%%ARCH%\bin;%SDK_QT4_DIR%%ARCH%\bin;%SDK_CMAKE_DIR%\bin +if defined SDK_QT5_DIR set Qt5_DIR=%SDK_QT5_DIR%%ARCH% +mkdir ..\libs_build_win%ARCH% +cd ../libs_build_win%ARCH% && cmake_mgw -Wno-dev -DQGLVIEW=1 ../libs && make install -j4 && cd ../libs && pause diff --git a/test/make_libs.sh b/test/make_libs.sh new file mode 100755 index 0000000..ec03987 --- /dev/null +++ b/test/make_libs.sh @@ -0,0 +1,6 @@ +#! /bin/bash +cd .. +mkdir libs_build_linux +cd libs_build_linux +cmake ../libs +make install $@ diff --git a/test/make_libs32.bat b/test/make_libs32.bat new file mode 100644 index 0000000..c1103ef --- /dev/null +++ b/test/make_libs32.bat @@ -0,0 +1 @@ +make_libs.bat 32 \ No newline at end of file diff --git a/test/make_libs64.bat b/test/make_libs64.bat new file mode 100644 index 0000000..f12abc1 --- /dev/null +++ b/test/make_libs64.bat @@ -0,0 +1 @@ +make_libs.bat 64 \ No newline at end of file diff --git a/test/make_libs_all.bat b/test/make_libs_all.bat new file mode 100644 index 0000000..b6bd18b --- /dev/null +++ b/test/make_libs_all.bat @@ -0,0 +1,2 @@ +start cmd /C make_libs32.bat +start cmd /C make_libs64.bat diff --git a/test/make_libs_android.bat b/test/make_libs_android.bat new file mode 100644 index 0000000..20513cc --- /dev/null +++ b/test/make_libs_android.bat @@ -0,0 +1,6 @@ +@echo off +mkdir ..\libs_build_android_%~1 +cd ..\libs_build_android_%~1 +call ..\libs\make_android 19 %~1 ..\libs "-DUTILS=0" +call make install -j4 +cd ..\libs diff --git a/test/make_libs_android_all.bat b/test/make_libs_android_all.bat new file mode 100644 index 0000000..54eb321 --- /dev/null +++ b/test/make_libs_android_all.bat @@ -0,0 +1,4 @@ +@echo off +for %%a in (x86,arm) do ( + call .\make_libs_android %%a +) diff --git a/test/piqt/CMakeLists.txt b/test/piqt/CMakeLists.txt new file mode 100644 index 0000000..ec9888b --- /dev/null +++ b/test/piqt/CMakeLists.txt @@ -0,0 +1,46 @@ +project(piqt) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if(NOT LIBPROJECT) + find_package(PIP REQUIRED) + option(LIB "System install" 0) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if(DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +find_package(QAD REQUIRED) +list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME}) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +include_directories(${PIP_INCLUDES} ${QAD_INCLUDES}) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +find_qt(${QtVersions} Core Gui Positioning) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets) +message(STATUS "Building ${PROJECT_NAME}") +if(LIBPROJECT) + sdk_install("pip" "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") +else() + if(LIB) + if(WIN32) + install(FILES ${out_HDR} DESTINATION ${MINGW_INCLUDE}/pip) + if(NOT "x${out_QM}" STREQUAL "x") + qt_install(FILES ${out_QM} DESTINATION QtLang) + endif() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtBin) + else() + install(FILES ${out_HDR} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() diff --git a/test/piqt/clean b/test/piqt/clean new file mode 100644 index 0000000..b687351 --- /dev/null +++ b/test/piqt/clean @@ -0,0 +1,4 @@ +#! /bin/bash +VERBOSE=1 make clean +rm -rvf CMakeFiles +rm -vf CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *.dll *.a *.so *.user* *~ *cxx moc_* ui_* qrc_* *.o *.bak diff --git a/test/piqt/clean.bat b/test/piqt/clean.bat new file mode 100644 index 0000000..f4b0eb0 --- /dev/null +++ b/test/piqt/clean.bat @@ -0,0 +1,4 @@ +#make clean +del /q /f /s CMakeFiles +rmdir /q /s CMakeFiles +del /q /f CMakeCache.txt Makefile cmake_install.cmake install_manifest.txt *.user* *~ *cxx moc_* ui_* qrc_* *.o *.exe *.a *.dll *.lib core *.bak diff --git a/test/piqt/make_lib.bat b/test/piqt/make_lib.bat new file mode 100644 index 0000000..0148a43 --- /dev/null +++ b/test/piqt/make_lib.bat @@ -0,0 +1 @@ +cmake -G "MinGW Makefiles" -DLIB=1 && make install . %* diff --git a/test/piqt/piqt.cpp b/test/piqt/piqt.cpp new file mode 100644 index 0000000..76e14c8 --- /dev/null +++ b/test/piqt/piqt.cpp @@ -0,0 +1,141 @@ +#include "piqt.h" +#include "qvariantedit.h" +#if QT_VERSION >= 0x050200 +# include +#endif + + +const QAD::Enum PI2QADEnum(const PIVariantTypes::Enum & el) { + QAD::Enum ret; + piForeachC (PIVariantTypes::Enumerator & e, el.enum_list) + ret << QAD::Enumerator(e.value, PI2QString(e.name)); + ret.selectValue(el.selectedValue()); + return ret; +} + + +const PIVariantTypes::Enum QAD2PIEnum(const QAD::Enum & el) { + PIVariantTypes::Enum ret; + foreach (const QAD::Enumerator & e, el.enum_list) + ret << PIVariantTypes::Enumerator(e.value, Q2PIString(e.name)); + ret.selectValue(el.selectedValue()); + return ret; +} + + +const QVariant PI2QVariant(const PIVariant & v) { + switch (v.type()) { + case PIVariant::pivBool: return QVariant(v.toBool()); + case PIVariant::pivChar: + case PIVariant::pivUChar: + case PIVariant::pivShort: + case PIVariant::pivUShort: + case PIVariant::pivInt: return QVariant(v.toInt()); + case PIVariant::pivUInt: return QVariant(uint(v.toInt())); + case PIVariant::pivLLong: return QVariant(v.toLLong()); + case PIVariant::pivULLong: return QVariant(ullong(v.toLLong())); + case PIVariant::pivFloat: return QVariant(v.toFloat()); + case PIVariant::pivDouble: return QVariant(v.toDouble()); + case PIVariant::pivLDouble: return QVariant(v.toDouble()); + case PIVariant::pivByteArray: return QVariant(PI2QByteArray(v.toByteArray())); + case PIVariant::pivString: return QVariant(PI2QString(v.toString())); + case PIVariant::pivStringList: return QVariant(PI2QStringList(v.toStringList())); + case PIVariant::pivTime: return QVariant(PI2QTime(v.toTime())); + case PIVariant::pivDate: return QVariant(PI2QDate(v.toDate())); + case PIVariant::pivDateTime: return QVariant(PI2QDateTime(v.toDateTime())); + case PIVariant::pivEnum: return QVariant::fromValue(PI2QADEnum(v.toEnum())); + case PIVariant::pivFile: return QVariant::fromValue(PI2QADFile(v.toFile())); + case PIVariant::pivDir: return QVariant::fromValue(PI2QADDir(v.toDir())); + case PIVariant::pivColor: return QVariant::fromValue(PI2QColor(v.toColor())); + case PIVariant::pivPoint: return QVariant(PI2QPoint(v.toPoint())); + case PIVariant::pivRect: return QVariant(PI2QRect(v.toRect())); + case PIVariant::pivIODevice: return QVariant::fromValue(PI2QADIODevice(v.toIODevice())); + case PIVariant::pivMathVector: return QVariant::fromValue(PI2QMathVector(v.toMathVector())); + case PIVariant::pivMathMatrix: return QVariant::fromValue(PI2QMathMatrix(v.toMathMatrix())); + //case PIVariant::pivSystemTime: return QVariant(v.to()); + default: return QVariant(); + } + return QVariant(); +} + + +const PIVariant Q2PIVariant(const QVariant & v) { + switch (v.type()) { + case QVariant::Bool: return PIVariant(v.toBool()); + case QVariant::Int: return PIVariant(v.toInt()); + case QVariant::UInt: return PIVariant(v.toUInt()); + case QVariant::LongLong: return PIVariant(v.toLongLong()); + case QVariant::ULongLong: return PIVariant(v.toULongLong()); + case QVariant::Double: return PIVariant(v.toDouble()); + case QVariant::ByteArray: return PIVariant(Q2PIByteArray(v.toByteArray())); + case QVariant::String: return PIVariant(Q2PIString(v.toString())); + case QVariant::StringList: return PIVariant(Q2PIStringList(v.toStringList())); + case QVariant::Time: return PIVariant(Q2PITime(v.toTime())); + case QVariant::Date: return PIVariant(Q2PIDate(v.toDate())); + case QVariant::DateTime: return PIVariant(Q2PIDateTime(v.toDateTime())); + case QVariant::Color: return PIVariant(Q2PIColor(v.value())); + case QVariant::Point: + case QVariant::PointF: return PIVariant(Q2PIPoint(v.toPointF())); + case QVariant::Rect: + case QVariant::RectF: return PIVariant(Q2PIRect(v.toRectF())); + default: break; + } + if (v.canConvert()) return PIVariant(QAD2PIEnum(v.value())); + if (v.canConvert()) return PIVariant(QAD2PIFile(v.value())); + if (v.canConvert()) return PIVariant(QAD2PIDir(v.value())); + if (v.canConvert()) return PIVariant(QAD2PIIODevice(v.value())); + if (v.canConvert()) return PIVariant(Q2PIMathVector(v.value())); + if (v.canConvert()) return PIVariant(Q2PIMathMatrix(v.value())); + return PIVariant(); +} + + +const PIVariantTypes::IODevice QAD2PIIODevice(const QAD::IODevice & v) { + PIVariantTypes::IODevice d; + d.set(Q2PIPropertyStorage(v.props)); + d.prefix = Q2PIString(v.prefix); + d.mode = v.mode; + d.options = v.options; + return d; +} + + +const QAD::IODevice PI2QADIODevice(const PIVariantTypes::IODevice & v) { + return QAD::IODevice(PI2QString(v.prefix), PI2QPropertyStorage(v.get()), v.mode, v.options); +} + + +const QAD::File PI2QADFile(const PIVariantTypes::File & v) { + return QAD::File(PI2QString(v.file), PI2QString(v.filter), v.is_abs); +} + + +const QAD::Dir PI2QADDir(const PIVariantTypes::Dir & v) { + return QAD::Dir(PI2QString(v.dir), v.is_abs); +} + + +const PIVariantTypes::File QAD2PIFile(const QAD::File & v) { + return PIVariantTypes::File(Q2PIString(v.file), Q2PIString(v.filter), v.is_abs); +} + + +const PIVariantTypes::Dir QAD2PIDir(const QAD::Dir & v) { + return PIVariantTypes::Dir(Q2PIString(v.dir), v.is_abs); +} + + +#if QT_VERSION >= 0x050200 +const QGeoCoordinate PI2QGeoPosition(const PIGeoPosition & v) { + PIGeoPosition p(v); + p.transformTo(PIGeoPosition::Cartesian); + p.setEllipsoidModel(PIEllipsoidModel::WGS84Ellipsoid()); + p.transformTo(PIGeoPosition::Geodetic); + return QGeoCoordinate(p.latitudeGeodetic(), p.longitude(), p.height()); +} + + +const PIGeoPosition Q2PIGeoPosition(const QGeoCoordinate & v) { + return PIGeoPosition(createVectorT3d(v.latitude(), v.longitude(), v.altitude()), PIGeoPosition::Geodetic); +} +#endif diff --git a/test/piqt/piqt.h b/test/piqt/piqt.h new file mode 100644 index 0000000..1960a9c --- /dev/null +++ b/test/piqt/piqt.h @@ -0,0 +1,149 @@ +#ifndef PIQT_H +#define PIQT_H + +#include "pimathmatrix.h" +#include "pipropertystorage.h" +#include "qad_types.h" +#include +#include +#include +#if QT_VERSION >= 0x050200 +# include "pigeoposition.h" +#endif + + +class QGeoCoordinate; + + +const QVariant PI2QVariant(const PIVariant & v); +const PIVariant Q2PIVariant(const QVariant & v); + +//inline const QString PI2QString(const PIString & v) {return QString::fromLocal8Bit(v.data());} +inline const QString PI2QString(const PIString & v) {return QString::fromUtf8(v.dataUTF8());} + +//inline const PIString Q2PIString(const QString & v) {return PIString(v.toLocal8Bit().data());} +inline const PIString Q2PIString(const QString & v) {return PIString::fromUTF8(v.toUtf8().data());} + + +inline const QStringList PI2QStringList(const PIStringList & v) {QStringList ret; piForeachC (PIString & s, v) ret << PI2QString(s); return ret;} + +inline const PIStringList Q2PIStringList(const QStringList & v) {PIStringList ret; foreach (const QString & s, v) ret << Q2PIString(s); return ret;} + + +inline const QByteArray PI2QByteArray(const PIByteArray & v) {return QByteArray((const char *)(v.data()), v.size_s());} + +inline const PIByteArray Q2PIByteArray(const QByteArray & v) {return PIByteArray(v.constData(), v.size());} + + +inline const QPointF PI2QVector2(const PIMathVectorT2d & v) {return QPointF(v[0], v[1]);} +inline const QVector3D PI2QVector3(const PIMathVectorT3d & v) {return QVector3D(v[0], v[1], v[2]);} + +inline const PIMathVectorT2d Q2PIVector2(const QPointF & v) {return createVectorT2d(double(v.x()), double(v.y()));} +inline const PIMathVectorT3d Q2PIVector3(const QVector3D & v) {return createVectorT3d(double(v.x()), double(v.y()), double(v.z()));} + +inline const QPointF PI2QPoint(const PIPointd & v) {return QPointF(v.x, v.y);} +inline const PIPointd Q2PIPoint(const QPointF & v) {return PIPointd(v.x(), v.y());} + +inline const QRectF PI2QRect(const PIRectd & v) {return QRectF(v.x0, v.y0, v.width(), v.height());} +inline const PIRectd Q2PIRect(const QRectF & v) {return PIRectd(v.x(), v.y(), v.width(), v.height());} + +inline const QAD::MathVector PI2QMathVector(const PIMathVectord & v) { + QVector q = QVector(v.size()); + memcpy(q.data(), v.data(), q.size()*sizeof(double)); + return QAD::MathVector(q); +} +inline const PIMathVectord Q2PIMathVector(const QAD::MathVector & v) { + PIMathVectord p = PIMathVectord(v.v.size()); + memcpy(p.data(), v.v.data(), p.size()*sizeof(double)); + return p; +} + +inline const QAD::MathMatrix PI2QMathMatrix(const PIMathMatrixd & v) { + PIVector > p = v.toVectors(); + QVector > q = QVector >(p.size()); + for (int i = 0; i < q.size(); ++i) { + q[i].resize(p[i].size()); + memcpy(q[i].data(), p[i].data(), q[i].size()*sizeof(double)); + } + return QAD::MathMatrix(q); +} +inline const PIMathMatrixd Q2PIMathMatrix(const QAD::MathMatrix & v) { + PIVector > p = PIVector >(v.m.size()); + for (int i = 0; i < v.m.size(); ++i) { + p[i].resize(v.m[i].size()); + memcpy(p[i].data(), v.m[i].data(), p[i].size()*sizeof(double)); + } + return PIMathMatrixd(p); +} + + +inline const QDate PI2QDate(const PIDate & v) {return QDate(v.year, v.month, v.day);} +inline const QTime PI2QTime(const PITime & v) {return QTime(v.hours, v.minutes, v.seconds, v.milliseconds);} +inline const QDateTime PI2QDateTime(const PIDateTime & v) {return QDateTime(PI2QDate(v.date()), PI2QTime(v.time()));} + +inline const PIDate Q2PIDate(const QDate & v) {return PIDate(v.year(), v.month(), v.day());} +inline const PITime Q2PITime(const QTime & v) {return PITime(v.hour(), v.minute(), v.second(), v.msec());} +inline const PIDateTime Q2PIDateTime(const QDateTime & v) {return PIDateTime(Q2PIDate(v.date()), Q2PITime(v.time()));} + + +inline const QColor PI2QColor(const PIVariantTypes::Color & v) {return QColor::fromRgba(v.rgba);} +inline const PIVariantTypes::Color Q2PIColor(const QColor & v) {return PIVariantTypes::Color(v.rgba());} + + +inline PIPropertyStorage Q2PIPropertyStorage(const PropertyStorage & props) { + PIPropertyStorage ret; + foreach (const PropertyStorage::Property & p, props) + ret.addProperty(Q2PIString(p.name), Q2PIVariant(p.value), Q2PIString(p.comment), p.flags); + return ret; +} + +inline PropertyStorage PI2QPropertyStorage(const PIPropertyStorage & props) { + PropertyStorage ret; + piForeachC (PIPropertyStorage::Property & p, props) + ret.addProperty(PI2QString(p.name), PI2QVariant(p.value), PI2QString(p.comment), p.flags); + return ret; +} + + +const QAD::Enum PI2QADEnum(const PIVariantTypes::Enum & el); +const QAD::File PI2QADFile(const PIVariantTypes::File & v); +const QAD::Dir PI2QADDir(const PIVariantTypes::Dir & v); +const QAD::IODevice PI2QADIODevice(const PIVariantTypes::IODevice & v); +const PIVariantTypes::Enum QAD2PIEnum(const QAD::Enum & el); +const PIVariantTypes::File QAD2PIFile(const QAD::File & v); +const PIVariantTypes::Dir QAD2PIDir(const QAD::Dir & v); +const PIVariantTypes::IODevice QAD2PIIODevice(const QAD::IODevice & v); + +//inline const PIVariant QString2PIVariant(const QString & v) {return PIVariant::readFromString(QString2PIString(v));} + +#if QT_VERSION >= 0x050200 +const QGeoCoordinate PI2QGeoPosition(const PIGeoPosition & v); +const PIGeoPosition Q2PIGeoPosition(const QGeoCoordinate & v); +#endif + +template +class __PIQtConverter { +public: + __PIQtConverter(const From & v): val(v) {} + template operator To() {return To();} + From val; +}; +template inline __PIQtConverter __PIQtConvert(const From & f) {return __PIQtConverter(f);} + +#define _PIQt_CONVERT(ft, tt, ftc, tfc) \ +template<> template<> inline __PIQtConverter::operator tt() {return ftc(val);} \ +template<> template<> inline __PIQtConverter::operator ft() {return tfc(val);} +#define _PIQt_CONVERT_S(t) _PIQt_CONVERT(PI##t, Q##t, PI2Q##t, Q2PI##t) + +_PIQt_CONVERT_S(String) +_PIQt_CONVERT_S(ByteArray) +_PIQt_CONVERT_S(Time) +_PIQt_CONVERT_S(Date) +_PIQt_CONVERT_S(DateTime) +_PIQt_CONVERT(PIMathVectorT2d, QPointF, PI2QVector2, Q2PIVector2) +_PIQt_CONVERT(PIMathVectorT3d, QVector3D, PI2QVector3, Q2PIVector3) + +#define piqt __PIQtConvert +#define qtpi __PIQtConvert + +#endif // PIQT_H diff --git a/test/piqt_utils/CMakeLists.txt b/test/piqt_utils/CMakeLists.txt new file mode 100644 index 0000000..c1a7c3c --- /dev/null +++ b/test/piqt_utils/CMakeLists.txt @@ -0,0 +1,70 @@ +project(piqt_utils) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +set(PIQT_UTILS_UTILS 1) +if(LIBPROJECT) + include(PIPMacros) + include(SDKMacros) + set(PIQT_UTILS_UTILS ${UTILS}) +else() + find_package(PIP REQUIRED) + option(LIB "System install" 0) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if(DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +set(LIBTYPE SHARED) +if(DEFINED ENV{QNX_HOST}) + set(LIBTYPE STATIC) +endif() +find_package(QAD REQUIRED) +list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME}) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +include_directories(${PIP_INCLUDES} ${QAD_INCLUDES}) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +pip_code_model(CCM "../pip/src_main/io_devices/piiodevice.h" "../pip/src_main/io_utils/pipacketextractor.h" OPTIONS "-DPIP_EXPORT" "-Es") +find_qt(${QtVersions} Core Gui) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} ${LIBTYPE} out_CPP CCM) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets qad_blockview piqt) +message(STATUS "Building ${PROJECT_NAME}") + +if (LIBPROJECT) + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + add_dependencies(piqt_utils${_v} pip_cmg) + endif() + endforeach() +endif() + +if (NOT DEFINED ENV{QNX_HOST}) + if (PIQT_UTILS_UTILS) + add_subdirectory(piconnedit) + add_subdirectory(pidumper) + add_subdirectory(piintrospector) + endif() +endif() + +if(LIBPROJECT) + sdk_install("pip" "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") +else() + if(LIB) + if(WIN32) + install(FILES ${out_HDR} DESTINATION ${MINGW_INCLUDE}/pip) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtBin) + else() + install(FILES ${out_HDR} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/pip) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() diff --git a/test/piqt_utils/piconnedit/CMakeLists.txt b/test/piqt_utils/piconnedit/CMakeLists.txt new file mode 100644 index 0000000..a8c7770 --- /dev/null +++ b/test/piqt_utils/piconnedit/CMakeLists.txt @@ -0,0 +1,26 @@ +project(piconnedit) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +find_qt(${QtVersions} Core Gui) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets piqt_utils) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/piqt_utils/piconnedit/icons/Apps-accessories-calculator-icon.png b/test/piqt_utils/piconnedit/icons/Apps-accessories-calculator-icon.png new file mode 100644 index 0000000..37a3035 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/Apps-accessories-calculator-icon.png differ diff --git a/test/piqt_utils/piconnedit/icons/DALI-designer.png b/test/piqt_utils/piconnedit/icons/DALI-designer.png new file mode 100644 index 0000000..23b1946 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/DALI-designer.png differ diff --git a/test/piqt_utils/piconnedit/icons/alpha.png b/test/piqt_utils/piconnedit/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/alpha.png differ diff --git a/test/piqt_utils/piconnedit/icons/application-exit.png b/test/piqt_utils/piconnedit/icons/application-exit.png new file mode 100644 index 0000000..ed5f8b2 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/application-exit.png differ diff --git a/test/piqt_utils/piconnedit/icons/applications-engineering.png b/test/piqt_utils/piconnedit/icons/applications-engineering.png new file mode 100644 index 0000000..4721d8d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/applications-engineering.png differ diff --git a/test/piqt_utils/piconnedit/icons/archive-extract.png b/test/piqt_utils/piconnedit/icons/archive-extract.png new file mode 100644 index 0000000..aa7b1fa Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/archive-extract.png differ diff --git a/test/piqt_utils/piconnedit/icons/archive-insert-directory.png b/test/piqt_utils/piconnedit/icons/archive-insert-directory.png new file mode 100644 index 0000000..0672205 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/archive-insert-directory.png differ diff --git a/test/piqt_utils/piconnedit/icons/archive-insert.png b/test/piqt_utils/piconnedit/icons/archive-insert.png new file mode 100644 index 0000000..193c25c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/archive-insert.png differ diff --git a/test/piqt_utils/piconnedit/icons/arrow-down.png b/test/piqt_utils/piconnedit/icons/arrow-down.png new file mode 100644 index 0000000..03f2014 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/arrow-down.png differ diff --git a/test/piqt_utils/piconnedit/icons/arrow-up.png b/test/piqt_utils/piconnedit/icons/arrow-up.png new file mode 100644 index 0000000..5e42321 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/arrow-up.png differ diff --git a/test/piqt_utils/piconnedit/icons/axis_x.png b/test/piqt_utils/piconnedit/icons/axis_x.png new file mode 100644 index 0000000..321899c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/axis_x.png differ diff --git a/test/piqt_utils/piconnedit/icons/axis_y.png b/test/piqt_utils/piconnedit/icons/axis_y.png new file mode 100644 index 0000000..7d4d9ec Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/axis_y.png differ diff --git a/test/piqt_utils/piconnedit/icons/case-sensitive.png b/test/piqt_utils/piconnedit/icons/case-sensitive.png new file mode 100644 index 0000000..39c5c5a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/case-sensitive.png differ diff --git a/test/piqt_utils/piconnedit/icons/checkbox-unchecked.png b/test/piqt_utils/piconnedit/icons/checkbox-unchecked.png new file mode 100644 index 0000000..01a7307 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/checkbox-unchecked.png differ diff --git a/test/piqt_utils/piconnedit/icons/checkbox.png b/test/piqt_utils/piconnedit/icons/checkbox.png new file mode 100644 index 0000000..1c311e2 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/checkbox.png differ diff --git a/test/piqt_utils/piconnedit/icons/clients.png b/test/piqt_utils/piconnedit/icons/clients.png new file mode 100644 index 0000000..0ae949a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/clients.png differ diff --git a/test/piqt_utils/piconnedit/icons/close.png b/test/piqt_utils/piconnedit/icons/close.png new file mode 100644 index 0000000..5492295 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/close.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-error.png b/test/piqt_utils/piconnedit/icons/code-error.png new file mode 100644 index 0000000..5b4c488 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-error.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-function.png b/test/piqt_utils/piconnedit/icons/code-function.png new file mode 100644 index 0000000..e5cea37 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-function.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-keyword.png b/test/piqt_utils/piconnedit/icons/code-keyword.png new file mode 100644 index 0000000..70dc5ef Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-keyword.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-parents.png b/test/piqt_utils/piconnedit/icons/code-parents.png new file mode 100644 index 0000000..7c14d40 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-parents.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-property.png b/test/piqt_utils/piconnedit/icons/code-property.png new file mode 100644 index 0000000..d2af8a6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-property.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-signal.png b/test/piqt_utils/piconnedit/icons/code-signal.png new file mode 100644 index 0000000..3415e62 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-signal.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-struct.png b/test/piqt_utils/piconnedit/icons/code-struct.png new file mode 100644 index 0000000..61544a8 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-struct.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-union.png b/test/piqt_utils/piconnedit/icons/code-union.png new file mode 100644 index 0000000..2f4567a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-union.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-variable.png b/test/piqt_utils/piconnedit/icons/code-variable.png new file mode 100644 index 0000000..d0ec29e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-variable.png differ diff --git a/test/piqt_utils/piconnedit/icons/code-word.png b/test/piqt_utils/piconnedit/icons/code-word.png new file mode 100644 index 0000000..e61f08f Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/code-word.png differ diff --git a/test/piqt_utils/piconnedit/icons/configure-.png b/test/piqt_utils/piconnedit/icons/configure-.png new file mode 100644 index 0000000..541c14e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/configure-.png differ diff --git a/test/piqt_utils/piconnedit/icons/configure.png b/test/piqt_utils/piconnedit/icons/configure.png new file mode 100644 index 0000000..5ce478b Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/configure.png differ diff --git a/test/piqt_utils/piconnedit/icons/database.png b/test/piqt_utils/piconnedit/icons/database.png new file mode 100644 index 0000000..3041814 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/database.png differ diff --git a/test/piqt_utils/piconnedit/icons/debug-run.png b/test/piqt_utils/piconnedit/icons/debug-run.png new file mode 100644 index 0000000..9bebb44 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/debug-run.png differ diff --git a/test/piqt_utils/piconnedit/icons/designer-qt4.png b/test/piqt_utils/piconnedit/icons/designer-qt4.png new file mode 100644 index 0000000..8f699be Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/designer-qt4.png differ diff --git a/test/piqt_utils/piconnedit/icons/dialog-close.png b/test/piqt_utils/piconnedit/icons/dialog-close.png new file mode 100644 index 0000000..2c2f99e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/dialog-close.png differ diff --git a/test/piqt_utils/piconnedit/icons/dialog-ok-apply.png b/test/piqt_utils/piconnedit/icons/dialog-ok-apply.png new file mode 100644 index 0000000..f5feac0 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/dialog-ok-apply.png differ diff --git a/test/piqt_utils/piconnedit/icons/dialog-warning.png b/test/piqt_utils/piconnedit/icons/dialog-warning.png new file mode 100644 index 0000000..80bd694 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/dialog-warning.png differ diff --git a/test/piqt_utils/piconnedit/icons/disabled.png b/test/piqt_utils/piconnedit/icons/disabled.png new file mode 100644 index 0000000..f20b8e0 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/disabled.png differ diff --git a/test/piqt_utils/piconnedit/icons/distribute-horizontal-margin.png b/test/piqt_utils/piconnedit/icons/distribute-horizontal-margin.png new file mode 100644 index 0000000..3289190 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/distribute-horizontal-margin.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-edit.png b/test/piqt_utils/piconnedit/icons/document-edit.png new file mode 100644 index 0000000..84e345d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-edit.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-new.png b/test/piqt_utils/piconnedit/icons/document-new.png new file mode 100644 index 0000000..3092571 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-new.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-open.png b/test/piqt_utils/piconnedit/icons/document-open.png new file mode 100644 index 0000000..8ba5441 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-open.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-print.png b/test/piqt_utils/piconnedit/icons/document-print.png new file mode 100644 index 0000000..8eb1c67 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-print.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-save-.png b/test/piqt_utils/piconnedit/icons/document-save-.png new file mode 100644 index 0000000..7fa489c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-save-.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-save-all.png b/test/piqt_utils/piconnedit/icons/document-save-all.png new file mode 100644 index 0000000..159857f Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-save-all.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-save-as.png b/test/piqt_utils/piconnedit/icons/document-save-as.png new file mode 100644 index 0000000..9695a56 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-save-as.png differ diff --git a/test/piqt_utils/piconnedit/icons/document-save.png b/test/piqt_utils/piconnedit/icons/document-save.png new file mode 100644 index 0000000..8072aea Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/document-save.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-clear-.png b/test/piqt_utils/piconnedit/icons/edit-clear-.png new file mode 100644 index 0000000..cee7af6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-clear-.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-clear-locationbar-rtl.png b/test/piqt_utils/piconnedit/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..6c4b83b Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-clear-locationbar-rtl.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-clear.png b/test/piqt_utils/piconnedit/icons/edit-clear.png new file mode 100644 index 0000000..631ed44 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-clear.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-copy.png b/test/piqt_utils/piconnedit/icons/edit-copy.png new file mode 100644 index 0000000..477e83a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-copy.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-cut.png b/test/piqt_utils/piconnedit/icons/edit-cut.png new file mode 100644 index 0000000..0732328 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-cut.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-delete.png b/test/piqt_utils/piconnedit/icons/edit-delete.png new file mode 100644 index 0000000..cc6d2af Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-delete.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-find-.png b/test/piqt_utils/piconnedit/icons/edit-find-.png new file mode 100644 index 0000000..64a1e28 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-find-.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-find.png b/test/piqt_utils/piconnedit/icons/edit-find.png new file mode 100644 index 0000000..9a462c0 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-find.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-guides.png b/test/piqt_utils/piconnedit/icons/edit-guides.png new file mode 100644 index 0000000..d264839 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-guides.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-paste.png b/test/piqt_utils/piconnedit/icons/edit-paste.png new file mode 100644 index 0000000..6788b02 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-paste.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-redo.png b/test/piqt_utils/piconnedit/icons/edit-redo.png new file mode 100644 index 0000000..8de333f Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-redo.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-table-insert-row-below.png b/test/piqt_utils/piconnedit/icons/edit-table-insert-row-below.png new file mode 100644 index 0000000..a194a68 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-table-insert-row-below.png differ diff --git a/test/piqt_utils/piconnedit/icons/edit-undo.png b/test/piqt_utils/piconnedit/icons/edit-undo.png new file mode 100644 index 0000000..5071aa1 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/edit-undo.png differ diff --git a/test/piqt_utils/piconnedit/icons/employees.png b/test/piqt_utils/piconnedit/icons/employees.png new file mode 100644 index 0000000..7ae7383 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/employees.png differ diff --git a/test/piqt_utils/piconnedit/icons/expand_s_x.png b/test/piqt_utils/piconnedit/icons/expand_s_x.png new file mode 100644 index 0000000..b66cb40 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/expand_s_x.png differ diff --git a/test/piqt_utils/piconnedit/icons/expand_s_y.png b/test/piqt_utils/piconnedit/icons/expand_s_y.png new file mode 100644 index 0000000..c2e8f78 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/expand_s_y.png differ diff --git a/test/piqt_utils/piconnedit/icons/expand_x.png b/test/piqt_utils/piconnedit/icons/expand_x.png new file mode 100644 index 0000000..217e3ea Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/expand_x.png differ diff --git a/test/piqt_utils/piconnedit/icons/expand_y.png b/test/piqt_utils/piconnedit/icons/expand_y.png new file mode 100644 index 0000000..fedf936 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/expand_y.png differ diff --git a/test/piqt_utils/piconnedit/icons/format-text-subscript.png b/test/piqt_utils/piconnedit/icons/format-text-subscript.png new file mode 100644 index 0000000..206b147 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/format-text-subscript.png differ diff --git a/test/piqt_utils/piconnedit/icons/games-solve.png b/test/piqt_utils/piconnedit/icons/games-solve.png new file mode 100644 index 0000000..47f11f7 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/games-solve.png differ diff --git a/test/piqt_utils/piconnedit/icons/go-jump.png b/test/piqt_utils/piconnedit/icons/go-jump.png new file mode 100644 index 0000000..2cd4627 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/go-jump.png differ diff --git a/test/piqt_utils/piconnedit/icons/go-next.png b/test/piqt_utils/piconnedit/icons/go-next.png new file mode 100644 index 0000000..aa7cbb9 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/go-next.png differ diff --git a/test/piqt_utils/piconnedit/icons/go-previous.png b/test/piqt_utils/piconnedit/icons/go-previous.png new file mode 100644 index 0000000..8230340 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/go-previous.png differ diff --git a/test/piqt_utils/piconnedit/icons/graphics.png b/test/piqt_utils/piconnedit/icons/graphics.png new file mode 100644 index 0000000..64abcb5 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/graphics.png differ diff --git a/test/piqt_utils/piconnedit/icons/groups-edit.png b/test/piqt_utils/piconnedit/icons/groups-edit.png new file mode 100644 index 0000000..278c4d6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/groups-edit.png differ diff --git a/test/piqt_utils/piconnedit/icons/item-add.png b/test/piqt_utils/piconnedit/icons/item-add.png new file mode 100644 index 0000000..8a422c7 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/item-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/item.png b/test/piqt_utils/piconnedit/icons/item.png new file mode 100644 index 0000000..50bfddb Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/item.png differ diff --git a/test/piqt_utils/piconnedit/icons/lastmoves.png b/test/piqt_utils/piconnedit/icons/lastmoves.png new file mode 100644 index 0000000..4454bca Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/lastmoves.png differ diff --git a/test/piqt_utils/piconnedit/icons/led_3.png b/test/piqt_utils/piconnedit/icons/led_3.png new file mode 100644 index 0000000..604c241 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/led_3.png differ diff --git a/test/piqt_utils/piconnedit/icons/led_off.png b/test/piqt_utils/piconnedit/icons/led_off.png new file mode 100644 index 0000000..fe60f36 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/led_off.png differ diff --git a/test/piqt_utils/piconnedit/icons/led_on.png b/test/piqt_utils/piconnedit/icons/led_on.png new file mode 100644 index 0000000..946ba73 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/led_on.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-+.png b/test/piqt_utils/piconnedit/icons/light-+.png new file mode 100644 index 0000000..e7d8d80 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-+.png differ diff --git a/test/piqt_utils/piconnedit/icons/light--.png b/test/piqt_utils/piconnedit/icons/light--.png new file mode 100644 index 0000000..9a4942b Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light--.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-254.png b/test/piqt_utils/piconnedit/icons/light-254.png new file mode 100644 index 0000000..ff6a171 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-254.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-disable-all.png b/test/piqt_utils/piconnedit/icons/light-disable-all.png new file mode 100644 index 0000000..68ee45c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-disable-all.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-disable.png b/test/piqt_utils/piconnedit/icons/light-disable.png new file mode 100644 index 0000000..4755fd8 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-disable.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-down.png b/test/piqt_utils/piconnedit/icons/light-down.png new file mode 100644 index 0000000..52a577a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-down.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-edit.png b/test/piqt_utils/piconnedit/icons/light-edit.png new file mode 100644 index 0000000..581a696 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-edit.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-max.png b/test/piqt_utils/piconnedit/icons/light-max.png new file mode 100644 index 0000000..692ce16 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-max.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-min.png b/test/piqt_utils/piconnedit/icons/light-min.png new file mode 100644 index 0000000..42e76b6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-min.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-off.png b/test/piqt_utils/piconnedit/icons/light-off.png new file mode 100644 index 0000000..3763a5d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-off.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-on.png b/test/piqt_utils/piconnedit/icons/light-on.png new file mode 100644 index 0000000..8db6a2d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-on.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-step-down.png b/test/piqt_utils/piconnedit/icons/light-step-down.png new file mode 100644 index 0000000..64b405d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-step-down.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-step-up.png b/test/piqt_utils/piconnedit/icons/light-step-up.png new file mode 100644 index 0000000..4aaf795 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-step-up.png differ diff --git a/test/piqt_utils/piconnedit/icons/light-up.png b/test/piqt_utils/piconnedit/icons/light-up.png new file mode 100644 index 0000000..ddc5eec Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/light-up.png differ diff --git a/test/piqt_utils/piconnedit/icons/lights.png b/test/piqt_utils/piconnedit/icons/lights.png new file mode 100644 index 0000000..34d2541 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/lights.png differ diff --git a/test/piqt_utils/piconnedit/icons/list-add.png b/test/piqt_utils/piconnedit/icons/list-add.png new file mode 100644 index 0000000..5724694 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/list-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/list-remove.png b/test/piqt_utils/piconnedit/icons/list-remove.png new file mode 100644 index 0000000..53506b0 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/list-remove.png differ diff --git a/test/piqt_utils/piconnedit/icons/log_parser.png b/test/piqt_utils/piconnedit/icons/log_parser.png new file mode 100644 index 0000000..cf0ca98 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/log_parser.png differ diff --git a/test/piqt_utils/piconnedit/icons/mail.png b/test/piqt_utils/piconnedit/icons/mail.png new file mode 100644 index 0000000..81adbf9 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/mail.png differ diff --git a/test/piqt_utils/piconnedit/icons/media-flash.png b/test/piqt_utils/piconnedit/icons/media-flash.png new file mode 100644 index 0000000..d91845b Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/media-flash.png differ diff --git a/test/piqt_utils/piconnedit/icons/media-playback-pause.png b/test/piqt_utils/piconnedit/icons/media-playback-pause.png new file mode 100644 index 0000000..d133bdc Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/media-playback-pause.png differ diff --git a/test/piqt_utils/piconnedit/icons/media-playback-stop.png b/test/piqt_utils/piconnedit/icons/media-playback-stop.png new file mode 100644 index 0000000..4ecbdf6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/media-playback-stop.png differ diff --git a/test/piqt_utils/piconnedit/icons/media-record.png b/test/piqt_utils/piconnedit/icons/media-record.png new file mode 100644 index 0000000..73688e8 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/media-record.png differ diff --git a/test/piqt_utils/piconnedit/icons/node-add.png b/test/piqt_utils/piconnedit/icons/node-add.png new file mode 100644 index 0000000..80b5c61 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/node-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/node.png b/test/piqt_utils/piconnedit/icons/node.png new file mode 100644 index 0000000..fc8194e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/node.png differ diff --git a/test/piqt_utils/piconnedit/icons/object-locked.png b/test/piqt_utils/piconnedit/icons/object-locked.png new file mode 100644 index 0000000..38dff01 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/object-locked.png differ diff --git a/test/piqt_utils/piconnedit/icons/piconnection.png b/test/piqt_utils/piconnedit/icons/piconnection.png new file mode 100644 index 0000000..47d41d7 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/piconnection.png differ diff --git a/test/piqt_utils/piconnedit/icons/piconnection.xcf b/test/piqt_utils/piconnedit/icons/piconnection.xcf new file mode 100644 index 0000000..a5fdbee Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/piconnection.xcf differ diff --git a/test/piqt_utils/piconnedit/icons/quickopen.png b/test/piqt_utils/piconnedit/icons/quickopen.png new file mode 100644 index 0000000..60cb838 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/quickopen.png differ diff --git a/test/piqt_utils/piconnedit/icons/run-build-install-root.png b/test/piqt_utils/piconnedit/icons/run-build-install-root.png new file mode 100644 index 0000000..6c7a236 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/run-build-install-root.png differ diff --git a/test/piqt_utils/piconnedit/icons/run-build.png b/test/piqt_utils/piconnedit/icons/run-build.png new file mode 100644 index 0000000..85a12e8 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/run-build.png differ diff --git a/test/piqt_utils/piconnedit/icons/scenes-edit.png b/test/piqt_utils/piconnedit/icons/scenes-edit.png new file mode 100644 index 0000000..d60c0f6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/scenes-edit.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-add.png b/test/piqt_utils/piconnedit/icons/select-add.png new file mode 100644 index 0000000..084ab28 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-intersect.png b/test/piqt_utils/piconnedit/icons/select-intersect.png new file mode 100644 index 0000000..e1b22cc Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-intersect.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-invert.png b/test/piqt_utils/piconnedit/icons/select-invert.png new file mode 100644 index 0000000..1c82ab4 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-invert.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-none.png b/test/piqt_utils/piconnedit/icons/select-none.png new file mode 100644 index 0000000..4f9bb5a Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-none.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-replace.png b/test/piqt_utils/piconnedit/icons/select-replace.png new file mode 100644 index 0000000..80ccde4 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-replace.png differ diff --git a/test/piqt_utils/piconnedit/icons/select-subtract.png b/test/piqt_utils/piconnedit/icons/select-subtract.png new file mode 100644 index 0000000..26eccee Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/select-subtract.png differ diff --git a/test/piqt_utils/piconnedit/icons/splash.png b/test/piqt_utils/piconnedit/icons/splash.png new file mode 100644 index 0000000..38921fc Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/splash.png differ diff --git a/test/piqt_utils/piconnedit/icons/status-off.png b/test/piqt_utils/piconnedit/icons/status-off.png new file mode 100644 index 0000000..b89267f Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/status-off.png differ diff --git a/test/piqt_utils/piconnedit/icons/status-on.png b/test/piqt_utils/piconnedit/icons/status-on.png new file mode 100644 index 0000000..bb62ff6 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/status-on.png differ diff --git a/test/piqt_utils/piconnedit/icons/status-warn.png b/test/piqt_utils/piconnedit/icons/status-warn.png new file mode 100644 index 0000000..2afcc5e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/status-warn.png differ diff --git a/test/piqt_utils/piconnedit/icons/system-help.png b/test/piqt_utils/piconnedit/icons/system-help.png new file mode 100644 index 0000000..86b6407 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/system-help.png differ diff --git a/test/piqt_utils/piconnedit/icons/tool-light-add.png b/test/piqt_utils/piconnedit/icons/tool-light-add.png new file mode 100644 index 0000000..a16d5fa Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/tool-light-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/tool-selection.png b/test/piqt_utils/piconnedit/icons/tool-selection.png new file mode 100644 index 0000000..8ca8dbd Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/tool-selection.png differ diff --git a/test/piqt_utils/piconnedit/icons/tools-wizard.png b/test/piqt_utils/piconnedit/icons/tools-wizard.png new file mode 100644 index 0000000..f67fb4c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/tools-wizard.png differ diff --git a/test/piqt_utils/piconnedit/icons/transform-move.png b/test/piqt_utils/piconnedit/icons/transform-move.png new file mode 100644 index 0000000..0bd8d04 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/transform-move.png differ diff --git a/test/piqt_utils/piconnedit/icons/variable-add.png b/test/piqt_utils/piconnedit/icons/variable-add.png new file mode 100644 index 0000000..0a551b1 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/variable-add.png differ diff --git a/test/piqt_utils/piconnedit/icons/variable-copy.png b/test/piqt_utils/piconnedit/icons/variable-copy.png new file mode 100644 index 0000000..42d91c3 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/variable-copy.png differ diff --git a/test/piqt_utils/piconnedit/icons/variable-edit.png b/test/piqt_utils/piconnedit/icons/variable-edit.png new file mode 100644 index 0000000..420ace3 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/variable-edit.png differ diff --git a/test/piqt_utils/piconnedit/icons/variable-remove.png b/test/piqt_utils/piconnedit/icons/variable-remove.png new file mode 100644 index 0000000..0686e7c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/variable-remove.png differ diff --git a/test/piqt_utils/piconnedit/icons/view-grid.png b/test/piqt_utils/piconnedit/icons/view-grid.png new file mode 100644 index 0000000..0f1d70c Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/view-grid.png differ diff --git a/test/piqt_utils/piconnedit/icons/view-refresh.png b/test/piqt_utils/piconnedit/icons/view-refresh.png new file mode 100644 index 0000000..afa2a9d Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/view-refresh.png differ diff --git a/test/piqt_utils/piconnedit/icons/warning-a.png b/test/piqt_utils/piconnedit/icons/warning-a.png new file mode 100644 index 0000000..95cb849 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/warning-a.png differ diff --git a/test/piqt_utils/piconnedit/icons/warning-id.png b/test/piqt_utils/piconnedit/icons/warning-id.png new file mode 100644 index 0000000..a8bc774 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/warning-id.png differ diff --git a/test/piqt_utils/piconnedit/icons/window-close.png b/test/piqt_utils/piconnedit/icons/window-close.png new file mode 100644 index 0000000..ea6b607 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/window-close.png differ diff --git a/test/piqt_utils/piconnedit/icons/zoom-fit-best.png b/test/piqt_utils/piconnedit/icons/zoom-fit-best.png new file mode 100644 index 0000000..07cfc98 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/zoom-fit-best.png differ diff --git a/test/piqt_utils/piconnedit/icons/zoom-in.png b/test/piqt_utils/piconnedit/icons/zoom-in.png new file mode 100644 index 0000000..8393e28 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/zoom-in.png differ diff --git a/test/piqt_utils/piconnedit/icons/zoom-out.png b/test/piqt_utils/piconnedit/icons/zoom-out.png new file mode 100644 index 0000000..f66575e Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/zoom-out.png differ diff --git a/test/piqt_utils/piconnedit/icons/zoom-range.png b/test/piqt_utils/piconnedit/icons/zoom-range.png new file mode 100644 index 0000000..c052512 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/zoom-range.png differ diff --git a/test/piqt_utils/piconnedit/icons/zoom-rect.png b/test/piqt_utils/piconnedit/icons/zoom-rect.png new file mode 100644 index 0000000..da44fd5 Binary files /dev/null and b/test/piqt_utils/piconnedit/icons/zoom-rect.png differ diff --git a/test/piqt_utils/piconnedit/piconnedit.qrc b/test/piqt_utils/piconnedit/piconnedit.qrc new file mode 100644 index 0000000..6ea99d5 --- /dev/null +++ b/test/piqt_utils/piconnedit/piconnedit.qrc @@ -0,0 +1,6 @@ + + + icons/edit-clear-.png + icons/document-save-.png + + diff --git a/test/piqt_utils/piconnedit/piconnedit_main.cpp b/test/piqt_utils/piconnedit/piconnedit_main.cpp new file mode 100644 index 0000000..f8d3f70 --- /dev/null +++ b/test/piqt_utils/piconnedit/piconnedit_main.cpp @@ -0,0 +1,34 @@ +#include +#include "piqt_connection_edit.h" +#include +#include +#include +#include + + +int main(int argc, char * argv[]) { + PIINTROSPECTION_START + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + ConnectionEdit w; + if (a.arguments().size() > 1) { + QPIConfig cfg(a.arguments()[1]); + QByteArray model = cfg.getValue("connectionmodel", QByteArray()); + if (!model.isEmpty()) w.setModel(model); + } + if (w.exec() == QDialog::Accepted) { + QString c = QFileDialog::getSaveFileName(&w, "Save config to file", a.applicationDirPath(), "*.conf"); + if (!c.isEmpty()) { + QFile f(c); + if (f.open(QIODevice::WriteOnly)) { + QTextStream ts(&f); + ts << w.configuration(); + ts << "connectionmodel = " << QByteArray2QString(w.model()) << "\n"; + f.close(); + } + } + } + return a.exec(); +} diff --git a/test/piqt_utils/pidumper/CMakeLists.txt b/test/piqt_utils/pidumper/CMakeLists.txt new file mode 100644 index 0000000..2cba56e --- /dev/null +++ b/test/piqt_utils/pidumper/CMakeLists.txt @@ -0,0 +1,26 @@ +project(pidumper) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +find_qt(${QtVersions} Core Gui) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets piqt_utils) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/piqt_utils/pidumper/pidumper.cpp b/test/piqt_utils/pidumper/pidumper.cpp new file mode 100644 index 0000000..4cfb522 --- /dev/null +++ b/test/piqt_utils/pidumper/pidumper.cpp @@ -0,0 +1,146 @@ +#include "pidumper.h" +#include "pifile.h" +#include "pitime.h" +#include "pidir.h" +//#include "ccm.h" +#include +#ifdef CC_GCC +# include +#endif + +PIDumper::PIDumper(QWidget * parent): QMainWindow(parent) { + setupUi(this); +#if QT_VERSION >= 0x050000 + treeDump->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#else + treeDump->header()->setResizeMode(QHeaderView::ResizeToContents); +#endif +} + + +void PIDumper::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void PIDumper::buildDump() { + treeDump->clear(); + if (!dump.contains('{')) return; + PIString app = dump.takeWord(); + if (app != "application") return; + dump.takeLine(); + QTreeWidgetItem * ti; + while (!dump.isEmpty()) { + PIString line = dump.takeLine(); + if (line.contains('{')) { + ti = new QTreeWidgetItem(); + dump.prepend(line + "\n"); + buildDumpSection(ti, dump); + treeDump->addTopLevelItem(ti); + continue; + } + int colon = line.find(":"); + if (colon < 0) continue; + PIString name = line.takeLeft(colon).trim(); + PIString value = line.cutLeft(1).trim(); + if (value.startsWith("\"")) value.cutLeft(1); + if (value.endsWith("\"")) value.cutRight(1); + ti = new QTreeWidgetItem(QStringList() << PI2QString(name) << PI2QString(value)); + treeDump->addTopLevelItem(ti); + } + on_checkDumpHideService_toggled(checkDumpHideService->isChecked()); +} + + +void PIDumper::buildDumpSection(QTreeWidgetItem * pi, PIString & str) { + PIString section = str.takeLeft(str.find("{")).trim(); + PIString value; + if (section.contains('(')) { + value = section; + section = value.takeLeft(value.find('(')); + value = value.takeRange("(", ")"); + } + pi->setText(0, piqt(section)); + pi->setText(1, piqt(value)); + //if (section == "PIObjects") + PIString range = str.takeRange("{", "}"); + QTreeWidgetItem * ti; + PIString fs; + if (section == "PIObjects" || section == "properties") fs = ":"; + if (section == "methodsEH") fs = " "; + if (section == "connections") fs = "->"; + while (!range.isEmpty()) { + PIString line = range.takeLine().trim(); + if (line.contains('{')) { + ti = new QTreeWidgetItem(pi); + range.prepend(line + "\n"); + buildDumpSection(ti, range); + //treeDump->addTopLevelItem(ti); + continue; + } + if (line.left(line.find(":")).trim() == "count") { + pi->setText(1, "[" + PI2QString(line.right(line.length() - line.find(":") - 1).trim()) + "]"); + continue; + } + int colon = line.find(fs); + if (colon < 0) continue; + PIString name = line.takeLeft(colon).trim(); + PIString value = line.cutLeft(fs.size_s()).trim(); + if (value.startsWith("\"")) value.cutLeft(1); + if (value.endsWith("\"")) value.cutRight(1); + if (name.endsWith(":")) name.cutRight(1); + if (name.isEmpty() && value.isEmpty()) continue; + ti = new QTreeWidgetItem(pi, QStringList() << PI2QString(name) << PI2QString(value)); + //treeDump->addTopLevelItem(ti); + } +} + + +void PIDumper::on_buttonDumpMake_clicked() { + if (radioDumpCurrent->isChecked()) { + PICout::setBufferActive(true, true); + dumpApplication(); + dump = PICout::buffer(); + PICout::setBufferActive(false); + } else { + int pid = lineDumpCustom->text().toInt(); + if (pid == 0) return; + if (system(QString("kill -USR1 %1").arg(pid).toLatin1().constData()) < 0) return; + PIString dp = PIDir::home().path() + "/_PIP_DUMP_" + PIString::fromNumber(pid); + PITimeMeasurer tm; + while (tm.elapsed_s() < 5. && !PIFile::isExists(dp)) { + piMSleep(10); + } + //piSleep(2.); + PIFile f(dp, PIIODevice::ReadOnly); + if (!f.isOpened()) return; + dump = PIString(f.readAll()); + f.remove(); + } + buildDump(); +} + + +void PIDumper::on_buttonDumpClipboard_clicked() { + dump = Q2PIString(QApplication::clipboard()->text()); + buildDump(); +} + + +void PIDumper::on_checkDumpHideService_toggled(bool on) { + QList il = treeDump->findItems("", Qt::MatchContains | Qt::MatchRecursive); + foreach (QTreeWidgetItem * i, il) { + if (on) { + if (i->text(0).startsWith("class")) + i->setHidden(i->text(1).contains("__S__")); + } else + i->setHidden(false); + } +} diff --git a/test/piqt_utils/pidumper/pidumper.h b/test/piqt_utils/pidumper/pidumper.h new file mode 100644 index 0000000..677fe1f --- /dev/null +++ b/test/piqt_utils/pidumper/pidumper.h @@ -0,0 +1,36 @@ +#ifndef PIDUMPER_H +#define PIDUMPER_H + +#include "ui_pidumper.h" +#include +#include +#include +#include +#include +#include "piqt.h" +#include "piconnection.h" + +class PIDumper: public QMainWindow, private Ui::PIDumper +{ + Q_OBJECT +public: + PIDumper(QWidget * parent = 0); + +protected: + void changeEvent(QEvent * e); + + void buildDump(); + void buildDumpSection(QTreeWidgetItem * pi, PIString & str); + + PIString dump; + +private slots: + void on_buttonDumpMake_clicked(); + void on_buttonDumpClipboard_clicked(); + void on_checkDumpHideService_toggled(bool on); + +public slots: + +}; + +#endif // PIDUMPER_H diff --git a/test/piqt_utils/pidumper/pidumper.ui b/test/piqt_utils/pidumper/pidumper.ui new file mode 100644 index 0000000..fdce675 --- /dev/null +++ b/test/piqt_utils/pidumper/pidumper.ui @@ -0,0 +1,207 @@ + + + PIDumper + + + + 0 + 0 + 1134 + 854 + + + + PIP dump viewer + + + + + + + Hide service objects + + + + + + + + + + + Make PIP dump + + + + + + + Take PIP dump from clipboard + + + + + + + + + Process + + + + + + + 0 + 0 + + + + Current + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + Custom: + + + + + + + false + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + 2 + + + false + + + 20 + + + + 1 + + + + + 2 + + + + + + + + + + + radioDumpCustom + toggled(bool) + lineDumpCustom + setEnabled(bool) + + + 439 + 91 + + + 480 + 90 + + + + + radioDumpCustom + clicked() + lineDumpCustom + setFocus() + + + 453 + 91 + + + 480 + 86 + + + + + + configChanged() + updateMap() + + diff --git a/test/piqt_utils/pidumper/pidumper_main.cpp b/test/piqt_utils/pidumper/pidumper_main.cpp new file mode 100644 index 0000000..5f2a368 --- /dev/null +++ b/test/piqt_utils/pidumper/pidumper_main.cpp @@ -0,0 +1,13 @@ +#include +#include "pidumper.h" + + +int main(int argc, char * argv[]) { + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + PIDumper w; + w.show(); + return a.exec(); +} diff --git a/test/piqt_utils/piintrospector/CMakeLists.txt b/test/piqt_utils/piintrospector/CMakeLists.txt new file mode 100644 index 0000000..db74bc3 --- /dev/null +++ b/test/piqt_utils/piintrospector/CMakeLists.txt @@ -0,0 +1,28 @@ +project(piintrospector) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +set(PII_ROOT "../../pip/src_main/introspection") +pip_code_model(PII_CCM "${PII_ROOT}/piintrospection_server_p.h" "${PII_ROOT}/piintrospection_threads_p.h" OPTIONS "-DPIP_EXPORT" "-Es") +find_qt(${QtVersions} Core Gui) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP PII_CCM) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets qad_application piqt_utils) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/piqt_utils/piintrospector/containers_view.cpp b/test/piqt_utils/piintrospector/containers_view.cpp new file mode 100644 index 0000000..328b9b8 --- /dev/null +++ b/test/piqt_utils/piintrospector/containers_view.cpp @@ -0,0 +1,264 @@ +#include "containers_view.h" +#include +#include +#include +#include + + +enum ColumnContainers { + ccType, + ccItemSize, + ccCount, + ccBytesAllocated, + ccBytesUsed, + ccColumnCount, +}; + + + + +ContainersModel::ContainersModel() { + mode_changes = false; + ls_column = 0; + ls_order = Qt::AscendingOrder; + all.resize(columnCount(), 0L); + prev_all.resize(all.size(), 0L); +} + + +void ContainersModel::update(const PIVector & t) { + prev_data.clear(); + all.fill(0U); + piForeachC (PIIntrospectionContainers::TypeInfo & i, cur_data) { + prev_data[i.id] = i; + } + piForeachC (PIIntrospectionContainers::TypeInfo & i, t) { + all[ccCount] += i.count; + all[ccBytesAllocated] += i.allocated * i.item_size; + all[ccBytesUsed] += i.used * i.item_size; + } + int pts = cur_data.size_s(); + cur_data = t; + if (t.size_s() > pts) { + beginInsertRows(QModelIndex(), pts, t.size_s() - 1); + endInsertRows(); + } + if (t.size_s() < pts) { + beginRemoveRows(QModelIndex(), t.size_s(), pts - 1); + endRemoveRows(); + } + sort(ls_column, ls_order); + emit headerDataChanged(Qt::Horizontal, ccCount, columnCount()); +} + + +void ContainersModel::clear() { + beginRemoveRows(QModelIndex(), 0, cur_data.size_s() - 1); + cur_data.clear(); + prev_data.clear(); + all.fill(0L); + endRemoveRows(); +} + + +int ContainersModel::rowCount(const QModelIndex & parent) const { + return cur_data.size_s(); +} + + +int ContainersModel::columnCount(const QModelIndex & parent) const { + return ccColumnCount; +} + + +QModelIndex ContainersModel::index(int row, int column, const QModelIndex & parent) const { + if (row >= cur_data.size_s() || row >= cur_data.size_s()) return QModelIndex(); + return createIndex(row, column, cur_data[row].id); +} + + +bool ContainersModel::hasChildren(const QModelIndex & parent) const { + if (!parent.isValid()) return true; + return false; +} + + +QVariant ContainersModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant(); + PIVector ret = all; + if (mode_changes) { + for (int i = 0; i < all.size_s(); ++i) + ret[i] -= prev_all[i]; + } + switch (section) { + case ccType : return tr("Type"); + case ccItemSize : return tr("Item size"); + case ccCount : return tr("Count (%1)").arg(ret[ccCount]); + case ccBytesAllocated: return tr("Allocated (%1)").arg(PI2QString(PIString::readableSize(ret[ccBytesAllocated]))); + case ccBytesUsed : return tr("Used (%1)").arg(PI2QString(PIString::readableSize(ret[ccBytesUsed]))); + default: break; + } + return QVariant(); +} + + +QVariant ContainersModel::data(const QModelIndex & index, int role) const { + if (role != Qt::DisplayRole && role != Qt::UserRole && role != (Qt::UserRole+1)) return QVariant(); + uint id = uint(index.internalId()); + const PIIntrospectionContainers::TypeInfo & t(cur_data[index.row()]); + llong v = 0L; + if (role == Qt::DisplayRole || role == Qt::UserRole) { + switch (index.column()) { + case ccType: return PI2QString(t.name); + case ccItemSize: + if (role == Qt::UserRole) return t.item_size; + return PI2QString(PIString::readableSize(t.item_size)); + default: break; + } + if (mode_changes) { + switch (index.column()) { + case ccCount: return int(t.count) - int(prev_data.value(id).count); + case ccBytesAllocated: + v = t.allocated; + v -= prev_data.value(id).allocated; + v *= t.item_size; + if (role == Qt::UserRole) return piAbs(v); + return PI2QString(PIString::readableSize(v)); + case ccBytesUsed: + v = t.used; + v -= prev_data.value(id).used; + v *= t.item_size; + if (role == Qt::UserRole) return piAbs(v); + return PI2QString(PIString::readableSize(v)); + } + } else { + switch (index.column()) { + case ccCount: return t.count; + case ccBytesAllocated: + v = t.allocated * t.item_size; + if (role == Qt::UserRole) return v; + return PI2QString(PIString::readableSize(v)); + case ccBytesUsed: + v = t.used * t.item_size; + if (role == Qt::UserRole) return v; + return PI2QString(PIString::readableSize(v)); + } + } + } + if (role == (Qt::UserRole+1) && (index.column() == ccCount)) { + return t.count; + } + return QVariant(); +} + + +Qt::ItemFlags ContainersModel::flags(const QModelIndex & index) const { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + + +//bool cmp(const PIIntrospectionContainers::TypeInfo & a, const PIIntrospectionContainers::TypeInfo & b); +int cmp_func_name_a(const PIIntrospectionContainers::TypeInfo * t0, const PIIntrospectionContainers::TypeInfo * t1) { + return QString::localeAwareCompare(PI2QString(t0->name), PI2QString(t1->name)); +} +int cmp_func_name_d(const PIIntrospectionContainers::TypeInfo * t0, const PIIntrospectionContainers::TypeInfo * t1) { + return -cmp_func_name_a(t0, t1); +} +#define CMP_FUNC(field) \ + int cmp_func_##field##_a(const PIIntrospectionContainers::TypeInfo * t0, const PIIntrospectionContainers::TypeInfo * t1) { \ + return (t0->field) <= (t1->field) ? -1 : ((t0->field) == (t1->field) ? 0 : 1); \ + } \ + int cmp_func_##field##_d(const PIIntrospectionContainers::TypeInfo * t0, const PIIntrospectionContainers::TypeInfo * t1) { \ + return -cmp_func_##field##_a(t0, t1); \ + } +CMP_FUNC(item_size) +CMP_FUNC(count) +CMP_FUNC(allocated) +CMP_FUNC(used) +#undef CMP_FUNC +void ContainersModel::sort(int column, Qt::SortOrder order) { + ls_column = column; + ls_order = order; + if (cur_data.isEmpty()) return; + PIVector::CompareFunc cf = 0; + switch (column) { + case ccType : cf = order == Qt::AscendingOrder ? cmp_func_name_a : cmp_func_name_d; break; + case ccItemSize : cf = order == Qt::AscendingOrder ? cmp_func_item_size_a : cmp_func_item_size_d; break; + case ccCount : cf = order == Qt::AscendingOrder ? cmp_func_count_a : cmp_func_count_d; break; + case ccBytesAllocated: cf = order == Qt::AscendingOrder ? cmp_func_allocated_a : cmp_func_allocated_d; break; + case ccBytesUsed : cf = order == Qt::AscendingOrder ? cmp_func_used_a : cmp_func_used_d; break; + default : break; + } + if (cf) + cur_data.sort(cf); + //qDebug() << "sort" << column << order; + dataChanged(index(0, 0), index(cur_data.size_s() - 1, columnCount())); +} + + +void ContainersModel::setChangesMode(bool yes) { + mode_changes = yes; + if (cur_data.isEmpty()) return; + sort(ls_column, ls_order); + emit headerDataChanged(Qt::Horizontal, ccCount, columnCount()); +} + + + + +void ContainersDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QStyledItemDelegate::paint(painter, option, index); + if (model->index(index.row(), ccCount).data(Qt::UserRole+1).toInt() == 0) { + QColor col; + if (option.state == QStyle::State_Enabled || + option.state == QStyle::State_HasFocus) + col = option.palette.color(QPalette::Active, QPalette::WindowText); + else + col = option.palette.color(QPalette::Disabled, QPalette::WindowText); + col.setAlphaF(0.5); + int y = option.rect.center().y() + (option.fontMetrics.height() / 2.5) - option.fontMetrics.strikeOutPos(); + painter->setPen(QPen(col, lineThickness())); + painter->drawLine(QPoint(option.rect.left(), y), QPoint(option.rect.right(), y)); + } +} + + + + +ContainersView::ContainersView(QWidget * parent): QWidget(parent) { + setupUi(this); + model = new ContainersModel(); + connect(radioChanges, SIGNAL(toggled(bool)), model, SLOT(setChangesMode(bool))); + QSortFilterProxyModel * proxy = new QSortFilterProxyModel(); + proxy->setSourceModel(model); + proxy->setSortRole(Qt::UserRole); + proxy->setDynamicSortFilter(false); + treeContainers->setModel(model); + treeContainers->setItemDelegate(new ContainersDelegate(model)); +} + + +ContainersView::~ContainersView() { +} + + +void ContainersView::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void ContainersView::showContainers(const PIVector & t) { + model->update(t); +} + + +void ContainersView::clear() { + model->clear(); +} diff --git a/test/piqt_utils/piintrospector/containers_view.h b/test/piqt_utils/piintrospector/containers_view.h new file mode 100644 index 0000000..13961a0 --- /dev/null +++ b/test/piqt_utils/piintrospector/containers_view.h @@ -0,0 +1,76 @@ +#ifndef CONTAINERS_VIEW_H +#define CONTAINERS_VIEW_H + +#include "ui_containers_view.h" +#include +#include +#include +#include +#include "piqt.h" +#include "piintrospection_containers_p.h" + +class ContainersModel: public QAbstractItemModel { + Q_OBJECT +public: + ContainersModel(); + + void update(const PIVector & t); + void clear(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + int columnCount(const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex & child) const {return QModelIndex();} + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + bool hasChildren(const QModelIndex & parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex & index, int role) const; + Qt::ItemFlags flags(const QModelIndex & index) const; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + +protected: + PIVector cur_data; + PIMap prev_data; + PIVector all, prev_all; + Qt::SortOrder ls_order; + int ls_column; + bool mode_changes; + +public slots: + void setChangesMode(bool yes); + +}; + + +class ContainersDelegate: public QStyledItemDelegate { + Q_OBJECT +public: + ContainersDelegate(QAbstractItemModel * m) {model = m;} + void paint(QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex &index) const; + QAbstractItemModel * model; +}; + + + +class ContainersView: public QWidget, private Ui::ContainersView +{ + Q_OBJECT +public: + ContainersView(QWidget * parent = 0); + ~ContainersView(); + void showContainers(const PIVector & t); + void clear(); +protected: + void changeEvent(QEvent * e); + + ContainersModel * model; + +private slots: + void sessionSave(QByteArray * data) {*data = treeContainers->header()->saveState();} + void sessionLoad(QByteArray * data) {treeContainers->header()->restoreState(*data);} + +public slots: + +}; + + +#endif // CONTAINERS_VIEW_H diff --git a/test/piqt_utils/piintrospector/containers_view.ui b/test/piqt_utils/piintrospector/containers_view.ui new file mode 100644 index 0000000..b4f73c7 --- /dev/null +++ b/test/piqt_utils/piintrospector/containers_view.ui @@ -0,0 +1,93 @@ + + + ContainersView + + + + 0 + 0 + 610 + 406 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Overall + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Changes + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + true + + + + + + + + diff --git a/test/piqt_utils/piintrospector/objects_view.cpp b/test/piqt_utils/piintrospector/objects_view.cpp new file mode 100644 index 0000000..7f2743d --- /dev/null +++ b/test/piqt_utils/piintrospector/objects_view.cpp @@ -0,0 +1,60 @@ +#include "objects_view.h" +#include +#include + + +enum ColumnObjects { + coClassName, + coName, + coParents, + coQueuedEvents, +}; + + +ObjectsView::ObjectsView(QWidget * parent): QWidget(parent) { + setupUi(this); +} + + +ObjectsView::~ObjectsView() { +} + + +void ObjectsView::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void ObjectsView::showObjects(const PIVector & objects) { + QHash stat; + + int vpos = treeObjects->verticalScrollBar()->value(); + treeObjects->clear(); + piForeachC (PIIntrospection::ObjectInfo & i, objects) { + stat[PI2QString(i.classname)]++; + QTreeWidgetItem * ti = new QTreeWidgetItem(); + ti->setText(coClassName, PI2QString(i.classname)); + ti->setText(coName, PI2QString(i.name)); + ti->setText(coParents, PI2QString(i.parents.join(":"))); + ti->setText(coQueuedEvents, QString::number(i.queued_events)); + treeObjects->addTopLevelItem(ti); + } + treeObjects->verticalScrollBar()->setValue(vpos); + + vpos = treeObjectsStat->verticalScrollBar()->value(); + treeObjectsStat->clear(); + for (QHash::const_iterator i = stat.constBegin(); i != stat.constEnd(); ++i) { + QTreeWidgetItem * ti = new QTreeWidgetItem(); + ti->setText(0, i.key()); + ti->setText(1, QString::number(i.value())); + treeObjectsStat->addTopLevelItem(ti); + } + treeObjectsStat->verticalScrollBar()->setValue(vpos); +} diff --git a/test/piqt_utils/piintrospector/objects_view.h b/test/piqt_utils/piintrospector/objects_view.h new file mode 100644 index 0000000..7143291 --- /dev/null +++ b/test/piqt_utils/piintrospector/objects_view.h @@ -0,0 +1,28 @@ +#ifndef OBJECTS_VIEW_H +#define OBJECTS_VIEW_H + +#include "ui_objects_view.h" +#include +#include +#include "piqt.h" +#include "piintrospection_server_p.h" + +class ObjectsView: public QWidget, private Ui::ObjectsView +{ + Q_OBJECT +public: + ObjectsView(QWidget * parent = 0); + ~ObjectsView(); + void showObjects(const PIVector & objects); +protected: + void changeEvent(QEvent * e); + +private slots: + void sessionSave(QByteArray * data) {*data = treeObjects->header()->saveState();} + void sessionLoad(QByteArray * data) {treeObjects->header()->restoreState(*data);} + +public slots: + +}; + +#endif // OBJECTS_VIEW_H diff --git a/test/piqt_utils/piintrospector/objects_view.ui b/test/piqt_utils/piintrospector/objects_view.ui new file mode 100644 index 0000000..082ce46 --- /dev/null +++ b/test/piqt_utils/piintrospector/objects_view.ui @@ -0,0 +1,101 @@ + + + ObjectsView + + + + 0 + 0 + 614 + 413 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + List + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + + class + + + + + name + + + + + parents + + + + + queued_events + + + + + + + + + Statictic + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + + class + + + + + count + + + + + + + + + + + + + diff --git a/test/piqt_utils/piintrospector/piintrospector.cpp b/test/piqt_utils/piintrospector/piintrospector.cpp new file mode 100644 index 0000000..d28ad1a --- /dev/null +++ b/test/piqt_utils/piintrospector/piintrospector.cpp @@ -0,0 +1,242 @@ +#include "piintrospector.h" +#include +#include +#include "pifile.h" +#include "pitime.h" +#include "pidir.h" +#include "pichunkstream.h" +#include "ccm_piintrospector.h" + + +QPIIntrospector::QPIIntrospector(QWidget * parent): EMainWindow(parent), peer("__introspection_client__") { + setupUi(this); + request_timer = 0; + session.setFile("qpiintrospector_session.conf"); +#if QT_VERSION >= 0x050000 + //treeContainers->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#else + //treeContainers->header()->setResizeMode(QHeaderView::ResizeToContents); +#endif + CONNECTU_QUEUED(&peer, peerConnectedEvent, this, peersChanged, this); + CONNECTU_QUEUED(&peer, peerDisconnectedEvent, this, peersChanged, this); + CONNECTU_QUEUED(&peer, dataReceivedEvent, this, peerReceived, this); + CONNECTU(&peer, peerConnectedEvent, this, reqProcPIEvents); + CONNECTU(&peer, peerDisconnectedEvent, this, reqProcPIEvents); + CONNECTU(&peer, dataReceivedEvent, this, reqProcPIEvents); + session.addEntry(this); + session.addEntry(tabWidgetMain); + PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIIntrospection::InfoTypes"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) { + QCheckBox * cb = new QCheckBox(PI2QString(e.name.mid(2))); + cb->setObjectName(QString("checkRequest%1").arg(e.value)); + cb->setProperty("__value__", e.value); + layoutRequestFlags->addWidget(cb); + session.addEntry(cb); + } + } + //startTimer(100); + session.load(); + peer.start(); +} + + +QPIIntrospector::~QPIIntrospector() { + session.save(); + peer.stop(); +} + + +void QPIIntrospector::changeEvent(QEvent * e) { + EMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: break; + } +} + + +void QPIIntrospector::timerEvent(QTimerEvent * e) { + if (e->timerId() == request_timer) + buttonRequest->click(); +} + + +void QPIIntrospector::savingSession(QPIConfig & conf) { + conf.setValue("treeStat_header", treeStat->header()->saveState()); +} + + +void QPIIntrospector::loadingSession(QPIConfig & conf) { + treeStat->header()->restoreState(conf.getValue("treeStat_header", QByteArray())); +} + + +void QPIIntrospector::buildTree(QByteArray d) { + /*PIVector threads; + PIByteArray pd = Q2PIByteArray(d); + pd >> threads; + treeContainers->clear(); + piForeachC (PIIntrospectionThreads::ThreadInfo & t, threads) { + QTreeWidgetItem * ti = new QTreeWidgetItem(); + ti->setText(0, QString(PI2QString(t.name) + " (%1)").arg(t.id)); + treeContainers->addTopLevelItem(ti); + }*/ +} + + +void QPIIntrospector::procRequestTimer() { + if (request_timer != 0) killTimer(request_timer); + request_timer = 0; + if (!checkRequestTimer->isChecked()) return; + request_timer = startTimer(1000 / spinRequestTimerHz->value()); +} + + +void QPIIntrospector::buildDumpSection(QTreeWidgetItem * pi, PIString & str) { +} + + +void QPIIntrospector::showInfo() { + PIString s; + s << info.execCommand << "\n"; + s << info.execDateTime.toString() << "\n"; + s << info.OS_name << "(" << info.OS_version << ", " << info.architecture << ")\n"; + s << info.user << "\n"; + s << info.build_options.join(", ") << "\n"; + s << "\n"; + s << "load k: " << PIString::fromNumber(stat.proc.cpu_load_system, 'f', 2) << " %\n"; + s << "load u: " << PIString::fromNumber(stat.proc.cpu_load_user, 'f', 2) << " %\n"; + s << "threads: " << stat.proc.threads << "\n"; + s << "memory usage: " << stat.proc.physical_memsize_readable; + labelInfo->setText(PI2QString(s)); + int tlic = treeStat->topLevelItemCount(); + for (int i = tlic; i < stat.threads.size_s(); ++i) + treeStat->addTopLevelItem(new QTreeWidgetItem()); + for (int i = tlic - 1; i >= stat.threads.size_s(); --i) + delete treeStat->topLevelItem(i); + tlic = piMini(treeStat->topLevelItemCount(), stat.threads.size_s()); + for (int i = 0; i < tlic; ++i) { + QTreeWidgetItem * ti = treeStat->topLevelItem(i); + PISystemMonitor::ThreadStats & ts(stat.threads[i]); + ti->setText(0, QString::number(i + 1)); + ti->setText(1, QString::number(ts.id)); + ti->setText(2, PI2QString(ts.name)); + ti->setText(3, durationStr(ts.work_time)); + ti->setText(4, QString::number(ts.cpu_load_kernel, 'f', 2)); + ti->setText(5, QString::number(ts.cpu_load_user, 'f', 2)); + } +} + + +QString QPIIntrospector::durationStr(PISystemTime t) { + QString ret; + double s = t.toSeconds(); + int d, h, m; + d = piFloor(s / (24*60*60)); + if (d > 0) { + ret += QString::number(d) + " d "; + s -= d * (24*60*60); + } + h = piFloor(s / (60*60)); + if (h > 0) { + ret += QString::number(h) + " h "; + s -= h * (60*60); + } + m = piFloor(s / 60); + if (m > 0) { + ret += QString::number(m).rightJustified(2, '0') + " m "; + s -= m * 60; + } + ret += QString::number(piFloor(s)).rightJustified(2, '0') + " s"; + return ret; +} + + +void QPIIntrospector::on_listApp_currentRowChanged(int r) { + PIString cs; + if (r < 0) cs.clear(); + else cs = Q2PIString(listApp->item(r)->text()); + if (cur_server != cs) + widgetContainers->clear(); + cur_server = cs; +} + + +void QPIIntrospector::peerReceived(const PIString & from, const PIByteArray & data) { + if (from != cur_server) return; + //piCout << "rec" << data.size(); + PIByteArray ba(data); + if (ba.size_s() < 4) return; + uint sign(0); ba >> sign; + if (sign != PIIntrospection::sign) return; + PIChunkStream cs(ba); + PIByteArray pba; + while (!cs.atEnd()) { + switch (cs.read()) { + case PIIntrospection::itInfo: { + cs.get(pba); + PIIntrospection::unpackInfo(pba, info); + } break; + case PIIntrospection::itProcStat: { + cs.get(pba); + PIIntrospection::unpackProcStat(pba, stat); + widgetThreads->setStat(stat.threads); + //showInfo(info); + } break; + case PIIntrospection::itContainers: { + cs.get(pba); + PIVector data; + PIIntrospection::unpackContainers(pba, data); + widgetContainers->showContainers(data); + } break; + case PIIntrospection::itObjects: { + cs.get(pba); + PIVector objects; + PIIntrospection::unpackObjects(pba, objects); + widgetObjects->showObjects(objects); + } break; + case PIIntrospection::itThreads: { + cs.get(pba); + PIVector threads; + PIIntrospection::unpackThreads(pba, threads); + widgetThreads->showThreads(threads); + } break; + default: break; + } + } + showInfo(); +} + + +void QPIIntrospector::peersChanged(const PIString & name) { + listApp->blockSignals(true); + QString cs = listApp->currentItem() ? listApp->currentItem()->text() : ""; + listApp->clear(); + peer.lock(); + piForeachC (PIPeer::PeerInfo & p, peer.allPeers()) { + QString pn = PI2QString(p.name); + listApp->addItem(pn); + if (pn == cs) + listApp->setCurrentRow(listApp->count() - 1); + } + peer.unlock(); + listApp->blockSignals(false); +} + + +void QPIIntrospector::on_buttonRequest_clicked() { + if (cur_server.isEmpty()) return; + PIIntrospection::RequiredInfo info; + for (int i = 0; i < layoutRequestFlags->count(); ++i) { + QCheckBox * cb = qobject_cast(layoutRequestFlags->itemAt(i)->widget()); + if (!cb) continue; + if (!cb->isChecked()) continue; + info.types |= cb->property("__value__").toInt(); + } + PIByteArray ba; + ba << PIIntrospection::sign << info; + peer.send(cur_server, ba); +} diff --git a/test/piqt_utils/piintrospector/piintrospector.h b/test/piqt_utils/piintrospector/piintrospector.h new file mode 100644 index 0000000..7b4d7cd --- /dev/null +++ b/test/piqt_utils/piintrospector/piintrospector.h @@ -0,0 +1,53 @@ +#ifndef PIINTROSPECTOR_H +#define PIINTROSPECTOR_H + +#include "ui_piintrospector.h" +#include +#include +#include +#include +#include +#include "piqt.h" +#include "pipeer.h" +#include "piintrospection_server_p.h" + +class QPIIntrospector: public EMainWindow, private Ui::QPIIntrospector, public PIObject +{ + Q_OBJECT + PIOBJECT(QPIIntrospector) +public: + QPIIntrospector(QWidget * parent = 0); + ~QPIIntrospector(); + +protected: + void changeEvent(QEvent * e); + void timerEvent(QTimerEvent * ); + void savingSession(QPIConfig & conf); + void loadingSession(QPIConfig & conf); + + void buildDumpSection(QTreeWidgetItem * pi, PIString & str); + void showInfo(); + EVENT_HANDLER(void, reqProcPIEvents) {QMetaObject::invokeMethod(this, "procPIEvents", Qt::QueuedConnection);} + QString durationStr(PISystemTime t); + + EVENT_HANDLER2(void, peerReceived, const PIString &, from, const PIByteArray &, data); + EVENT_HANDLER1(void, peersChanged, const PIString &, name); + + PIString cur_server; + PIIntrospection::ProcessInfo info; + PIIntrospection::ProcessStat stat; + PIPeer peer; + int request_timer; + +private slots: + void procPIEvents() {callQueuedEvents();} + void buildTree(QByteArray d); + void procRequestTimer(); + void on_listApp_currentRowChanged(int r); + void on_buttonRequest_clicked(); + +public slots: + +}; + +#endif // PIINTROSPECTOR_H diff --git a/test/piqt_utils/piintrospector/piintrospector.ui b/test/piqt_utils/piintrospector/piintrospector.ui new file mode 100644 index 0000000..9230309 --- /dev/null +++ b/test/piqt_utils/piintrospector/piintrospector.ui @@ -0,0 +1,284 @@ + + + QPIIntrospector + + + + 0 + 0 + 824 + 597 + + + + PIP introspector + + + + + + + Qt::Horizontal + + + + Select application + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + Request + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Request + + + + + + + + + + 0 + 0 + + + + Timer + + + + + + + + 0 + 0 + + + + Hz + + + 2 + + + 0.010000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 1.000000000000000 + + + + + + + + + + + + + + + + 0 + + + + Info + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Threads: + + + + + + + + # + + + + + id + + + + + name + + + + + uptime + + + + + kernel, % + + + + + user, % + + + + + + + + + Containers + + + + + + + + + + Objects + + + + + + + + + + Threads + + + + + + + + + + + + + + + + + + + ObjectsView + QWidget +
objects_view.h
+ 1 +
+ + ContainersView + QWidget +
containers_view.h
+ 1 +
+ + ThreadsView + QWidget +
threads_view.h
+ 1 +
+
+ + + + checkRequestTimer + toggled(bool) + QPIIntrospector + procRequestTimer() + + + 65 + 552 + + + 6 + 546 + + + + + spinRequestTimerHz + valueChanged(double) + QPIIntrospector + procRequestTimer() + + + 150 + 552 + + + 140 + 605 + + + + + + procRequestTimer() + +
diff --git a/test/piqt_utils/piintrospector/piintrospector_main.cpp b/test/piqt_utils/piintrospector/piintrospector_main.cpp new file mode 100644 index 0000000..3d25393 --- /dev/null +++ b/test/piqt_utils/piintrospector/piintrospector_main.cpp @@ -0,0 +1,13 @@ +#include +#include "piintrospector.h" + + +int main(int argc, char * argv[]) { + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + QPIIntrospector w; + w.show(); + return a.exec(); +} diff --git a/test/piqt_utils/piintrospector/threads_view.cpp b/test/piqt_utils/piintrospector/threads_view.cpp new file mode 100644 index 0000000..0fcd09b --- /dev/null +++ b/test/piqt_utils/piintrospector/threads_view.cpp @@ -0,0 +1,230 @@ +#include "threads_view.h" +#include +#include +#include +#include +#include "ccm_piintrospector.h" + + +enum ColumnThreads { + ctClassname, + ctName, + ctID, + ctDelay, + ctState, + ctLoad, + ctRunCost, + ctRunCount, + ctColumnCount, +}; + + + + +ThreadsModel::ThreadsModel() { + PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIIntrospectionThreads::ThreadState"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) { + state_names[e.value] = PI2QString(e.name.mid(1)); + } + } + state_colors[PIIntrospectionThreads::sStopped] = QColor(Qt::red).lighter(150); + state_colors[PIIntrospectionThreads::sStarting] = QColor(Qt::blue).lighter(150); + state_colors[PIIntrospectionThreads::sRunning] = QColor(Qt::green).lighter(120); + state_colors[PIIntrospectionThreads::sWaiting] = QColor(Qt::yellow).lighter(110); +} + + +void ThreadsModel::update(const PIVector & t) { + int pts = threads.size_s(); + threads = t; + if (t.size_s() > pts) { + beginInsertRows(QModelIndex(), pts, t.size_s() - 1); + endInsertRows(); + } + if (t.size_s() < pts) { + beginRemoveRows(QModelIndex(), t.size_s(), pts - 1); + endRemoveRows(); + } + dataChanged(index(0, 0), index(threads.size_s() - 1, columnCount())); + //emit headerDataChanged(Qt::Horizontal, ccCount, columnCount()); +} + + +void ThreadsModel::setStat(const PIVector & s) { + stat = s; + dataChanged(index(0, ctLoad), index(threads.size_s() - 1, ctLoad)); +} + + +void ThreadsModel::clear() { + beginRemoveRows(QModelIndex(), 0, threads.size_s() - 1); + threads.clear(); + endRemoveRows(); +} + + +int ThreadsModel::rowCount(const QModelIndex & parent) const { + return threads.size_s(); +} + + +int ThreadsModel::columnCount(const QModelIndex & parent) const { + return ctColumnCount; +} + + +QModelIndex ThreadsModel::index(int row, int column, const QModelIndex & parent) const { + if (row >= threads.size_s() || row >= threads.size_s()) return QModelIndex(); + return createIndex(row, column, quintptr(0)); +} + + +bool ThreadsModel::hasChildren(const QModelIndex & parent) const { + if (!parent.isValid()) return true; + return false; +} + + +QVariant ThreadsModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return QVariant(); + switch (section) { + case ctClassname: return tr("Classname"); + case ctName : return tr("Name"); + case ctID : return tr("pID (pri)"); + case ctDelay : return tr("Delay, ms"); + case ctState : return tr("State"); + case ctLoad : return tr("CPU, %"); + case ctRunCost : return tr("Run cost, avg"); + case ctRunCount : return tr("Run counts"); + default: break; + } + return QVariant(); +} + + +QVariant ThreadsModel::data(const QModelIndex & index, int role) const { + if (role != Qt::DisplayRole && role != Qt::DecorationRole && role != Qt::UserRole) return QVariant(); + const PIIntrospectionThreads::ThreadInfo & ti(threads[index.row()]); + if (role == Qt::DisplayRole) { + switch (index.column()) { + case ctClassname: return PI2QString(ti.classname); + case ctName : return PI2QString(ti.name); + case ctID : return QString("%1 (%2)").arg(ti.id).arg(ti.priority); + case ctDelay : return QString::number(ti.delay); + case ctState : return state_names.value(ti.state); + case ctLoad : { + piForeachC (PISystemMonitor::ThreadStats & s, stat) { + if (s.id == llong(ti.id)) { + return QString::number(s.cpu_load_kernel + s.cpu_load_user, 'f', 2) + " %"; + } + } + return "-"; + } + case ctRunCost : { + double v = ti.run_us; + QByteArray suff = " us"; + if (v > 1000.) {v /= 1000.; suff = " ms";} + if (v > 1000.) {v /= 1000.; suff = " s";} + return QString::number(v, 'f', 1) + suff; + } + case ctRunCount : return QString::number(ti.run_count); + } + } + if (index.column() == ctState) { + if (role == Qt::DecorationRole) { + return state_colors.value(ti.state); + } + if (role == Qt::UserRole) { + return int(ti.state); + } + } + return QVariant(); +} + + +Qt::ItemFlags ThreadsModel::flags(const QModelIndex & index) const { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + + + +/* +void ContainersDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QStyledItemDelegate::paint(painter, option, index); + if (model->index(index.row(), ccCount).data(Qt::UserRole+1).toInt() == 0) { + QColor col; + if (option.state == QStyle::State_Enabled || + option.state == QStyle::State_HasFocus) + col = option.palette.color(QPalette::Active, QPalette::WindowText); + else + col = option.palette.color(QPalette::Disabled, QPalette::WindowText); + col.setAlphaF(0.5); + int y = option.rect.center().y() + (option.fontMetrics.height() / 2.5) - option.fontMetrics.strikeOutPos(); + painter->setPen(QPen(col, lineThickness())); + painter->drawLine(QPoint(option.rect.left(), y), QPoint(option.rect.right(), y)); + } +} +*/ + + + + +ThreadsView::ThreadsView(QWidget * parent): QWidget(parent) { + setupUi(this); + model = new ThreadsModel(); + QSortFilterProxyModel * proxy = new QSortFilterProxyModel(); + proxy->setSourceModel(model); + proxy->setSortRole(Qt::UserRole); + proxy->setDynamicSortFilter(false); + treeThreads->setModel(model); + connect(checkHideStopped, SIGNAL(toggled(bool)), this, SLOT(updateHidden())); + //treeContainers->setItemDelegate(new ContainersDelegate(model)); +} + + +ThreadsView::~ThreadsView() { +} + + +void ThreadsView::setStat(const PIVector & stat) { + model->setStat(stat); +} + + +void ThreadsView::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void ThreadsView::updateHidden() { + bool hide_stopped = checkHideStopped->isChecked(); + QModelIndex root = treeThreads->rootIndex(); + for (int i = 0; i < model->rowCount(); ++i) { + if (!hide_stopped) treeThreads->setRowHidden(i, root, false); + else { + if (model->index(i, ctState).data(Qt::UserRole).toInt() == PIIntrospectionThreads::sStopped) + treeThreads->setRowHidden(i, root, true); + else + treeThreads->setRowHidden(i, root, false); + } + } +} + + +void ThreadsView::showThreads(const PIVector & threads) { + model->update(threads); + updateHidden(); +} + + +void ThreadsView::clear() { + model->clear(); +} diff --git a/test/piqt_utils/piintrospector/threads_view.h b/test/piqt_utils/piintrospector/threads_view.h new file mode 100644 index 0000000..bd643b6 --- /dev/null +++ b/test/piqt_utils/piintrospector/threads_view.h @@ -0,0 +1,81 @@ +#ifndef THREADS_VIEW_H +#define THREADS_VIEW_H + +#include "ui_threads_view.h" +#include +#include +#include +#include +#include +#include +#include "piqt.h" +#include "pisystemmonitor.h" +#include "piintrospection_threads_p.h" + + +class ThreadsModel: public QAbstractItemModel { + Q_OBJECT +public: + ThreadsModel(); + + void update(const PIVector & t); + void setStat(const PIVector & s); + void clear(); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + int columnCount(const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex & child) const {return QModelIndex();} + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + bool hasChildren(const QModelIndex & parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QVariant data(const QModelIndex & index, int role) const; + Qt::ItemFlags flags(const QModelIndex & index) const; + +protected: + PIVector threads; + PIVector stat; + QHash state_names; + QHash state_colors; + +}; + + + +/* +class ContainersDelegate: public QStyledItemDelegate { + Q_OBJECT +public: + ContainersDelegate(QAbstractItemModel * m) {model = m;} + void paint(QPainter *painter, const QStyleOptionViewItem & option, const QModelIndex &index) const override; + QAbstractItemModel * model; +}; +*/ + + +class ThreadsView: public QWidget, private Ui::ThreadsView +{ + Q_OBJECT +public: + ThreadsView(QWidget * parent = 0); + ~ThreadsView(); + + void setStat(const PIVector & stat); + void showThreads(const PIVector & threads); + void clear(); + +protected: + void changeEvent(QEvent * e); + + ThreadsModel * model; + +private slots: + void sessionSave(QByteArray * data) {*data = treeThreads->header()->saveState();} + void sessionLoad(QByteArray * data) {treeThreads->header()->restoreState(*data);} + void updateHidden(); + +public slots: + +}; + + +#endif // CONTAINERS_VIEW_H diff --git a/test/piqt_utils/piintrospector/threads_view.ui b/test/piqt_utils/piintrospector/threads_view.ui new file mode 100644 index 0000000..10210d0 --- /dev/null +++ b/test/piqt_utils/piintrospector/threads_view.ui @@ -0,0 +1,51 @@ + + + ThreadsView + + + + 0 + 0 + 513 + 365 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Hide stopped + + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + + + + + + diff --git a/test/piqt_utils/piqt_connection_edit.cpp b/test/piqt_utils/piqt_connection_edit.cpp new file mode 100644 index 0000000..b505e7a --- /dev/null +++ b/test/piqt_utils/piqt_connection_edit.cpp @@ -0,0 +1,493 @@ +#include "ui_piqt_connection_edit.h" +#include "piqt_connection_edit.h" +#include "piqt_connection_view.h" +#include "piqt_highlighter.h" +#include "piqt.h" +#include "picodeinfo.h" +#include +#include + + +ConnectionEdit::ConnectionEdit(QWidget * parent): QDialog(parent) { + ui = new Ui::ConnectionEdit(); + ui->setupUi(this); + new ConfigHighlighter(ui->codeEdit->document()); + loading = false; + connect(ui->blockView, SIGNAL(schemeAction(BlockItemBase::Action,QList)), this, SLOT(recreateRequest())); + connect(ui->blockView->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + conn = 0; + PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIIODevice::DeviceMode"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) + ui->comboMode->addItem(PI2QString(e.name + " (" + PIString::fromNumber(e.value) + ")"), QVariant::fromValue(e.value)); + } + ui->comboMode->setCurrentIndex(ui->comboMode->count() - 1); + ei = PICodeInfo::enumsInfo->value("PIIODevice::DeviceOption"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) { + QCheckBox * cb = new QCheckBox(); + cb->setText(PI2QString(e.name + " (" + PIString::fromNumber(e.value) + ")")); + cb->setProperty("__value", e.value); + ui->layoutOptions->addWidget(cb); + } + } + ei = PICodeInfo::enumsInfo->value("PIPacketExtractor::SplitMode"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) + ui->comboSplit->addItem(PI2QString(e.name + " (" + PIString::fromNumber(e.value) + ")"), QVariant::fromValue(e.value)); + } + udevicenum = 0; +} + + +ConnectionEdit::~ConnectionEdit() { + if (conn) { + conn->stop(); + delete conn; + } +} + + +void ConnectionEdit::accept() { + //bool ok = false; + QList bl = ui->blockView->allDevices(); + foreach (BlockItem * i, bl) + foreach (BlockItem * j, bl) + if (i != j) + if (((DeviceItem*)i)->name() == ((DeviceItem*)j)->name()) { + QMessageBox::critical(this, windowTitle() + " - " + tr("error") + "!", tr("Equal devices names: \"%1\"!").arg(((DeviceItem*)i)->name())); + return; + } + QDialog::accept(); +} + + +QString ConnectionEdit::configuration() const { + if (!conn) return QString(); + return PI2QString(conn->makeConfig()); +} + + +QByteArray ConnectionEdit::model() const { + QByteArray ret; + QDataStream s(&ret, QIODevice::ReadWrite); + QString cn = PI2QString(conn ? conn->name() : PIString()); + s << cn; + QList busl = ui->blockView->buses(); + s << busl.size(); + foreach (BlockBusItem * b, busl) + s << b->save(); + QList blockl = ui->blockView->blocks(); + s << blockl.size(); + foreach (BlockItem * b, blockl) { + int type = b->propertyByName("__type").value.toInt(); + s << type; + switch (type) { + case __CV_Device: + case __CV_Filter: + case __CV_Sender: + s << b->save(); + break; + } + } + return ret; +} + + +QString ConnectionEdit::name() const { + if (!conn) return QString(); + return PI2QString(conn->name()); +} + + +void ConnectionEdit::setName(const QString & name) { + ui->lineName->setText(name); + recreateConnection(); +} + + +void ConnectionEdit::addDevice(const QString & name, const QString & path) { + ui->blockView->addDevice(name, path); +} + + +void ConnectionEdit::setModel(const QByteArray & m) { + loading = true; + ui->blockView->removeAll(); + QDataStream s(m); + QString cn; + s >> cn; + if (conn) delete conn; + conn = new PIConnection(Q2PIString(cn)); + ui->lineName->setText(cn); + int sz; + s >> sz; + for (int i = 0; i < sz; ++i) { + BlockBusItem * b = new BlockBusItem(); + QByteArray ba; s >> ba; b->load(ba); + ui->blockView->addItem(b); + } + s >> sz; + for (int i = 0; i < sz; ++i) { + int type(0); + BlockItem * b(0); + QByteArray ba; + s >> type; + switch (type) { + case __CV_Device: + b = new DeviceItem(); + s >> ba; b->load(ba); + if (!b->isPropertyExists("bufferSize")) + ((DeviceItem*)b)->setBufferSize(4096); + ((DeviceItem*)b)->rename(); + break; + case __CV_Filter: + b = new FilterItem(); + s >> ba; b->load(ba); + if (!b->isPropertyExists("bufferSize")) + ((FilterItem*)b)->setBufferSize(65536); + ((FilterItem*)b)->rename(); + break; + case __CV_Sender: + b = new SenderItem(); + s >> ba; b->load(ba); + ((SenderItem*)b)->rename(); + break; + } + if (b) + ui->blockView->addItem(b); + } + ui->blockView->reconnectAll(); + loading = false; + recreateConnection(); +} + + +void ConnectionEdit::selectionChanged() { + QList si = ui->blockView->scene()->selectedItems(); + ui->buttonRemove->setEnabled(!si.isEmpty()); + ui->buttonDeviceModify->setEnabled(false); + ui->buttonFilterModify->setEnabled(false); + ui->buttonSenderModify->setEnabled(false); + if (si.size() != 1) return; + BlockItem * b = qgraphicsitem_cast(si[0]); + if (!b) return; + int type = b->propertyByName("__type").value.toInt(); + if (type == __CV_Device) { + ui->tabWidget->setCurrentIndex(0); + DeviceItem * di = (DeviceItem*)b; + ui->buttonDeviceModify->setEnabled(true); + for (int i = 0; i < ui->comboMode->count(); ++i) + if (ui->comboMode->itemData(i).toInt() == di->mode()) { + ui->comboMode->setCurrentIndex(i); + break; + } + ui->lineDevice->setText(di->name()); + ui->linePath->setEditText(di->path()); + ui->spinDeviceDT->setValue(di->disconnectTimeout()); + ui->spinDeviceBS->setValue(di->bufferSize()); + setOptions(di->options()); + } + if (type == __CV_Filter) { + ui->tabWidget->setCurrentIndex(1); + FilterItem * fi = (FilterItem*)b; + ui->buttonFilterModify->setEnabled(true); + for (int i = 0; i < ui->comboSplit->count(); ++i) + if (ui->comboSplit->itemData(i).toInt() == fi->mode()) { + ui->comboSplit->setCurrentIndex(i); + break; + } + ui->lineFilter->setText(fi->name()); + ui->lineHeader->setText(fi->header()); + ui->lineFooter->setText(fi->footer()); + ui->spinTimeout->setValue(fi->timeout()); + ui->spinSize->setValue(fi->packetSize()); + ui->spinFilterDT->setValue(fi->disconnectTimeout()); + ui->spinFilterBS->setValue(fi->bufferSize()); + } + if (type == __CV_Sender) { + ui->tabWidget->setCurrentIndex(2); + SenderItem * si = (SenderItem*)b; + ui->buttonSenderModify->setEnabled(true); + ui->lineSender->setText(si->name()); + ui->lineData->setText(si->data()); + ui->spinFrequency->setValue(si->frequency()); + } + +} + + +void ConnectionEdit::applyFilter(FilterItem * b) { + b->setName(ui->lineFilter->text()); + b->setMode(PIPacketExtractor::SplitMode(ui->comboSplit->itemData(ui->comboSplit->currentIndex()).toInt())); + b->setHeader(ui->lineHeader->text()); + b->setFooter(ui->lineFooter->text()); + b->setTimeout(ui->spinTimeout->value()); + b->setPacketSize(ui->spinSize->value()); + b->setDisconnectTimeout(ui->spinFilterDT->value()); + b->setBufferSize(ui->spinFilterBS->value()); + recreateConnection(); +} + + +void ConnectionEdit::applyDevice(DeviceItem * b) { + QString n = ui->lineDevice->text(); + if (n.isEmpty()) { + n = "device" + QString::number(udevicenum); + } + b->setName(n); + b->setMode(PIIODevice::DeviceMode(ui->comboMode->itemData(ui->comboMode->currentIndex()).toInt())); + b->setOptions(PIIODevice::DeviceOptions(getOptions())); + b->setPath(ui->linePath->currentText()); + b->setDisconnectTimeout(ui->spinDeviceDT->value()); + b->setBufferSize(ui->spinDeviceBS->value()); + recreateConnection(); +} + + +void ConnectionEdit::applySender(SenderItem * b) { + b->setName(ui->lineSender->text()); + b->setData(ui->lineData->text()); + b->setFrequency(ui->spinFrequency->value()); + recreateConnection(); +} + + +int ConnectionEdit::getOptions() const { + int ret(0); + for (int i = 0; i < ui->layoutOptions->count(); ++i) { + QCheckBox * cb = qobject_cast(ui->layoutOptions->itemAt(i)->widget()); + if (!cb) continue; + if (cb->isChecked()) + ret |= cb->property("__value").toInt(); + } + return ret; +} + + +void ConnectionEdit::setOptions(int o) { + for (int i = 0; i < ui->layoutOptions->count(); ++i) { + QCheckBox * cb = qobject_cast(ui->layoutOptions->itemAt(i)->widget()); + if (!cb) continue; + int cbf = cb->property("__value").toInt(); + //qDebug() << cbf; + cb->setChecked((o & cbf) == cbf); + } +} + + +void ConnectionEdit::recreateConnection() { + //qDebug() << "recreate"; + if (loading) return; + ui->blockView->reconnectAll(); + if (conn) delete conn; + PIString cn = Q2PIString(ui->lineName->text()); + if (cn.isEmpty()) conn = new PIConnection(); + else conn = new PIConnection(cn); + QList devs = ui->blockView->allDevices(); + foreach (BlockItem * b, devs) { + DeviceItem * di = (DeviceItem*)b; + //qDebug() << di->path(); + PIIODevice * dev = conn->addDevice(Q2PIString(di->path()), di->mode()); + if (!dev) continue; + dev->setOptions(di->options()); + dev->setThreadedReadBufferSize(di->bufferSize()); + conn->setDeviceName(dev, Q2PIString(di->name())); + PIDiagnostics * diag = conn->diagnostic(dev); + if (diag) diag->setDisconnectTimeout(di->disconnectTimeout()); + } + foreach (BlockItem * b, devs) { + DeviceItem * di = (DeviceItem*)b; + PIIODevice * dev = conn->deviceByName(Q2PIString(di->name())); + if (!dev) continue; + BlockItemPin * p = b->pinByText("read"); + if (!p) continue; + QList buses = p->connectedBuses(), nbuses; + QSet pbuses; + while (!buses.isEmpty()) { + nbuses.clear(); + foreach (BlockBusItem * bus, buses) { + QList cb = bus->connectedBlocks(); + if (cb.size() != 2) continue; + FilterItem * fi_t(0); + BlockItem * bi_f(0); + if (cb[0]->pinAtBus(bus)->text() == "in") { + fi_t = (FilterItem*)(cb[0]); + bi_f = (cb[1]); + } else if (cb[1]->pinAtBus(bus)->text() == "in") { + fi_t = (FilterItem*)(cb[1]); + bi_f = (cb[0]); + } + if (!fi_t || !bi_f) continue; + PIString name_from; + int type = bi_f->propertyByName("__type").value.toInt(); + if (type == __CV_Device) name_from = Q2PIString(((DeviceItem*)bi_f)->name()); + if (type == __CV_Filter) name_from = Q2PIString(((FilterItem*)bi_f)->name()); + if (name_from.isEmpty()) continue; + PIPacketExtractor * pe = conn->addFilter(Q2PIString(fi_t->name()),conn->deviceByName(name_from), fi_t->mode()); + if (!pe) continue; + pe->setBufferSize(fi_t->bufferSize()); + pe->setHeader(PIByteArray::fromUserInput(Q2PIString(fi_t->header()))); + pe->setFooter(PIByteArray::fromUserInput(Q2PIString(fi_t->footer()))); + pe->setTimeout(fi_t->timeout()); + pe->setPacketSize(fi_t->packetSize()); + pe->setPayloadSize(fi_t->packetSize()); + PIDiagnostics * diag = conn->diagnostic(pe); + if (diag) diag->setDisconnectTimeout(fi_t->disconnectTimeout()); + QList nb = fi_t->pinByText("out")->connectedBuses(); + foreach (BlockBusItem * b_, nb) + if (!pbuses.contains(b_)) { + pbuses << b_; + nbuses << b_; + } + } + buses = nbuses; + } + } + foreach (BlockItem * b, devs) { + BlockItemPin * p = b->pinByText("write"); + if (!p) continue; + QList buses = p->connectedBuses(); + foreach (BlockBusItem * bus, buses) { + QList cb = bus->connectedBlocks(); + if (cb.size() != 2) continue; + BlockItem * bi_f(0); + DeviceItem * di_t(0); + if (cb[0]->pinAtBus(bus)->text() == "write") { + di_t = (DeviceItem*)(cb[0]); + bi_f = (cb[1]); + } else if (cb[1]->pinAtBus(bus)->text() == "write") { + di_t = (DeviceItem*)(cb[1]); + bi_f = (cb[0]); + } + if (!bi_f || !di_t) continue; + QString name_from; + int type = bi_f->propertyByName("__type").value.toInt(); + if (type == __CV_Sender) { + SenderItem * si = ((SenderItem*)bi_f); + si->name(); + conn->addSender(Q2PIString(si->name()), Q2PIString(di_t->path()), si->frequency()); + if (!si->data().isEmpty()) + conn->setSenderFixedData(Q2PIString(si->name()), PIByteArray::fromUserInput(Q2PIString(si->data()))); + } else { + if (type == __CV_Device) name_from = ((DeviceItem*)bi_f)->name(); + if (type == __CV_Filter) name_from = ((FilterItem*)bi_f)->name(); + if (name_from.isEmpty()) continue; + conn->addChannel(Q2PIString(name_from), Q2PIString(di_t->name())); + } + } + } + ui->codeEdit->setText(PI2QString(conn->makeConfig())); +} + + +void ConnectionEdit::on_buttonRemove_clicked() { + ui->blockView->removeSelected(); + recreateConnection(); +} + + +void ConnectionEdit::on_buttonClear_clicked() { + ui->blockView->removeAll(); + recreateConnection(); +} + + +void ConnectionEdit::on_buttonFilterAdd_clicked() { + if (!conn) return; + applyFilter(ui->blockView->addFilter(ui->lineFilter->text())); +} + + +void ConnectionEdit::on_buttonFilterModify_clicked() { + QList si = ui->blockView->scene()->selectedItems(); + if (si.isEmpty()) return; + if (!qgraphicsitem_cast(si[0])) return; + if (qgraphicsitem_cast(si[0])->propertyByName("__type").value.toInt() != __CV_Filter) + return; + applyFilter(qgraphicsitem_cast(si[0])); +} + + +void ConnectionEdit::on_buttonDeviceAdd_clicked() { + if (!conn) return; + QString n = ui->lineDevice->text(); + if (n.isEmpty()) { + udevicenum++; + n = "device" + QString::number(udevicenum); + } + QString p = ui->linePath->currentText(); + if (ui->linePath->findText(p) < 0) ui->linePath->addItem(p); + qDebug() << "add:" << n; + applyDevice(ui->blockView->addDevice(n, ui->linePath->currentText())); +} + + +void ConnectionEdit::on_buttonDeviceModify_clicked() { + QList si = ui->blockView->scene()->selectedItems(); + if (si.isEmpty()) return; + if (!qgraphicsitem_cast(si[0])) return; + if (qgraphicsitem_cast(si[0])->propertyByName("__type").value.toInt() != __CV_Device) + return; + applyDevice(qgraphicsitem_cast(si[0])); +} + + +void ConnectionEdit::on_buttonSenderAdd_clicked() { + if (!conn) return; + applySender(ui->blockView->addSender(ui->lineSender->text())); +} + + +void ConnectionEdit::on_buttonSenderModify_clicked() { + QList si = ui->blockView->scene()->selectedItems(); + if (si.isEmpty()) return; + if (!qgraphicsitem_cast(si[0])) return; + if (qgraphicsitem_cast(si[0])->propertyByName("__type").value.toInt() != __CV_Sender) + return; + applySender(qgraphicsitem_cast(si[0])); +} + + +void ConnectionEdit::on_comboSplit_currentIndexChanged(int index) { + int mode = ui->comboSplit->itemData(index).toInt(); + switch (mode) { + case PIPacketExtractor::None: + ui->widgetHeader->setEnabled(false); + ui->widgetFooter->setEnabled(false); + ui->widgetTimeout->setEnabled(false); + ui->widgetSize->setEnabled(false); + break; + case PIPacketExtractor::Header: + ui->widgetHeader->setEnabled(true); + ui->widgetFooter->setEnabled(false); + ui->widgetTimeout->setEnabled(false); + ui->widgetSize->setEnabled(true); + break; + case PIPacketExtractor::Footer: + ui->widgetHeader->setEnabled(false); + ui->widgetFooter->setEnabled(true); + ui->widgetTimeout->setEnabled(false); + ui->widgetSize->setEnabled(true); + break; + case PIPacketExtractor::HeaderAndFooter: + ui->widgetHeader->setEnabled(true); + ui->widgetFooter->setEnabled(true); + ui->widgetTimeout->setEnabled(false); + ui->widgetSize->setEnabled(false); + break; + case PIPacketExtractor::Timeout: + ui->widgetHeader->setEnabled(false); + ui->widgetFooter->setEnabled(false); + ui->widgetTimeout->setEnabled(true); + ui->widgetSize->setEnabled(false); + break; + case PIPacketExtractor::Size: + ui->widgetHeader->setEnabled(false); + ui->widgetFooter->setEnabled(false); + ui->widgetTimeout->setEnabled(false); + ui->widgetSize->setEnabled(true); + break; + default: break; + } +} diff --git a/test/piqt_utils/piqt_connection_edit.h b/test/piqt_utils/piqt_connection_edit.h new file mode 100644 index 0000000..8dafe84 --- /dev/null +++ b/test/piqt_utils/piqt_connection_edit.h @@ -0,0 +1,61 @@ +#ifndef CONNECTION_EDIT_H +#define CONNECTION_EDIT_H + +#include +#include "piconnection.h" + +namespace Ui { + class ConnectionEdit; +} + +class FilterItem; +class DeviceItem; +class SenderItem; + +class ConnectionEdit: public QDialog { + Q_OBJECT +public: + explicit ConnectionEdit(QWidget * parent = 0); + ~ConnectionEdit(); + + QString configuration() const; + QByteArray model() const; + QString name() const; + + void setName(const QString & name); + void addDevice(const QString & name, const QString & path); + void setModel(const QByteArray & m); + +private: + void accept(); + void keyPressEvent(QKeyEvent *) {} + void applyFilter(FilterItem * b); + void applyDevice(DeviceItem * b); + void applySender(SenderItem * b); + int getOptions() const; + void setOptions(int o); + + Ui::ConnectionEdit * ui; + PIConnection * conn; + bool loading; + int udevicenum; + +private slots: + void recreateRequest() {if (!loading) QMetaObject::invokeMethod(this, "recreateConnection", Qt::QueuedConnection);} + void on_buttonRemove_clicked(); + void on_buttonClear_clicked(); + void on_buttonFilterAdd_clicked(); + void on_buttonFilterModify_clicked(); + void on_buttonDeviceAdd_clicked(); + void on_buttonDeviceModify_clicked(); + void on_buttonSenderAdd_clicked(); + void on_buttonSenderModify_clicked(); + void on_comboSplit_currentIndexChanged(int index); + void selectionChanged(); + +public slots: + void recreateConnection(); + +}; + +#endif // CONNECTION_EDIT_H diff --git a/test/piqt_utils/piqt_connection_edit.ui b/test/piqt_utils/piqt_connection_edit.ui new file mode 100644 index 0000000..eb99380 --- /dev/null +++ b/test/piqt_utils/piqt_connection_edit.ui @@ -0,0 +1,898 @@ + + + ConnectionEdit + + + + 0 + 0 + 794 + 799 + + + + Connection editor + + + + + + Qt::Horizontal + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Name: + + + + + + + + + + + + Qt::Horizontal + + + + 0 + + + + Device + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Path: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + -1 + + + + eth://UDP:127.0.0.1:12345:127.0.0.1:12346 + + + + + eth://TCP:127.0.0.1:16666 + + + + + eth://UDP:192.168.0.5:16666:192.168.0.6:16667:mcast:234.0.2.1:mcast:234.0.2.2 + + + + + file://./text.txt + + + + + binlog://./logs/:mylog_:1 + + + + + ser:///dev/ttyUSB0:9600:8:N:1 + + + + + ser://COM32:115200:8:N:1 + + + + + usb://0bb4:0c86:1:1:2 + + + + + + + + Mode: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Options: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 2 + + + + + + + + + Disconnect timeout: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + ms + + + 9999.000000000000000 + + + 3.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Buffer size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + b + + + 999999999 + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + false + + + Modify + + + + :/icons/document-save-.png:/icons/document-save-.png + + + + + + + + + + Filter + + + + + + + + Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + Parameters + + + + + + + + false + + + + 0 + + + + + Header: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + false + + + + 0 + + + + + Footer: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + false + + + + 0 + + + + + Timeout: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + ms + + + 99999.000000000000000 + + + 10.000000000000000 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + + + + false + + + + 0 + + + + + Size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 999999999 + + + 8 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 1 + 20 + + + + + + + + + + + + + 0 + + + + + Disconnect timeout: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + ms + + + 9999.000000000000000 + + + 3.000000000000000 + + + + + + + + + + false + + + + 0 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 17 + 17 + + + + + + + + Buffer size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + b + + + 999999999 + + + 65536 + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 1 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + false + + + Modify + + + + :/icons/document-save-.png:/icons/document-save-.png + + + + + + + + + + Sender + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Frequency: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Hz + + + 9999.000000000000000 + + + 10.000000000000000 + + + + + + + Data: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + Qt::Vertical + + + + 20 + 77 + + + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + false + + + Modify + + + + :/icons/document-save-.png:/icons/document-save-.png + + + + + + + + + + + + + + + + false + + + Remove + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Clear + + + + :/icons/edit-clear-.png:/icons/edit-clear-.png + + + + + + + + + + + + false + + + true + + + false + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + BlockView + QGraphicsView +
blockview.h
+
+ + CLineEdit + QLineEdit +
clineedit.h
+
+ + EComboBox + QComboBox +
ecombobox.h
+
+ + QCodeEdit + QWidget +
qcodeedit.h
+
+ + ConnectionView + BlockView +
piqt_connection_view.h
+
+
+ + + + + + buttonBox + accepted() + ConnectionEdit + accept() + + + 442 + 738 + + + 413 + 426 + + + + + buttonBox + rejected() + ConnectionEdit + reject() + + + 669 + 738 + + + 643 + 428 + + + + + lineName + editingFinished() + ConnectionEdit + recreateConnection() + + + 644 + 29 + + + 662 + -5 + + + + + + recreateConnection() + +
diff --git a/test/piqt_utils/piqt_connection_view.cpp b/test/piqt_utils/piqt_connection_view.cpp new file mode 100644 index 0000000..3f5437c --- /dev/null +++ b/test/piqt_utils/piqt_connection_view.cpp @@ -0,0 +1,204 @@ +#include "piqt_connection_view.h" +#include "picodeinfo.h" +#include "piqt.h" +#include + + +DeviceItem::DeviceItem(): BlockItem() { + addProperty(BlockItem::Property("__type", "", __CV_Device)); + addProperty(BlockItem::Property("bufferSize", "", 4096)); + setSize(200, 80); + setColor(QColor(192, 192, 255)); + text_name = new AlignedTextItem(); + text_path = new AlignedTextItem(); + QFont fnt(text_name->font()); fnt.setBold(true); + text_name->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_name->setFont(fnt); + text_name->setPos(0., -size().height() / 2.); + addDecor(text_name); + fnt.setBold(false); + fnt.setPointSizeF(fnt.pointSizeF() - 2.); + text_path->setFont(fnt); + text_path->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_path->setPos(0., -size().height() / 2. + 20); + addDecor(text_path); + addPin(Qt::AlignLeft, 0, "write", ""); + addPin(Qt::AlignRight, 0, "read", ""); +} + + +void DeviceItem::rename() { + QString ms; + BlockItemPin * pr = pinByText("read"), * pw = pinByText("write"); + switch (mode()) { + case PIIODevice::ReadOnly: ms = "ro"; pr->show(); pw->hide(); break; + case PIIODevice::WriteOnly: ms = "wo"; pr->hide(); pw->show(); break; + case PIIODevice::ReadWrite: ms = "rw"; pr->show(); pw->show(); break; + } + text_name->setText(name() + " (" + ms + ")"); + text_path->setText(path()); +} + + + + +FilterItem::FilterItem(): BlockItem() { + addProperty(BlockItem::Property("__type", "", __CV_Filter)); + addProperty(BlockItem::Property("bufferSize", "", 65536)); + setSize(140, 80); + setPos(200, 0); + setColor(QColor(192, 255, 192)); + text_name = new AlignedTextItem(); + text_mode = new AlignedTextItem(); + QFont fnt(text_name->font()); fnt.setBold(true); + text_name->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_name->setFont(fnt); + text_name->setPos(0., -size().height() / 2.); + addDecor(text_name); + fnt.setBold(false); + fnt.setPointSizeF(fnt.pointSizeF() - 2.); + text_mode->setFont(fnt); + text_mode->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_mode->setPos(0., -size().height() / 2. + 20); + addDecor(text_mode); + addPin(Qt::AlignLeft, 0, "in", ""); + addPin(Qt::AlignRight, 0, "out", ""); +} + + +void FilterItem::rename() { + text_name->setText(name()); + QString ms; + PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIPacketExtractor::SplitMode", 0); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & i, ei->members) + if (i.value == mode()) { + ms = PI2QString(i.name); + piBreak; + } + } + text_mode->setText(ms); + +} + + + + +SenderItem::SenderItem(): BlockItem() { + addProperty(BlockItem::Property("__type", "", __CV_Sender)); + setSize(140, 80); + setPos(-200, 0); + setColor(QColor(255, 192, 192)); + text_name = new AlignedTextItem(); + text_frequency = new AlignedTextItem(); + QFont fnt(text_name->font()); fnt.setBold(true); + text_name->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_name->setFont(fnt); + text_name->setPos(0., -size().height() / 2.); + addDecor(text_name); + fnt.setBold(false); + fnt.setPointSizeF(fnt.pointSizeF() - 2.); + text_frequency->setFont(fnt); + text_frequency->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + text_frequency->setPos(0., -size().height() / 2. + 20); + addDecor(text_frequency); + addPin(Qt::AlignRight, 0, "out", ""); +} + + +void SenderItem::rename() { + text_name->setText(name()); + text_frequency->setText(QString::number(frequency()) + " Hz"); +} + + + + +ConnectionView::ConnectionView(QWidget * parent): BlockView(parent) { +} + + +ConnectionView::~ConnectionView() { +} + + +DeviceItem * ConnectionView::addDevice(const QString & name, const QString & path) { + DeviceItem * ret = findDevice(name); + if (ret) return ret; + ret = new DeviceItem(); + PIString dp; + PIIODevice::DeviceMode dm; + PIIODevice::splitFullPath(Q2PIString(path), &dp, &dm); + ret->setPos(0, 0); + ret->setName(name); + ret->setPath(PI2QString(dp)); + ret->setMode(dm); + ret->setDisconnectTimeout(3.); + ret->setBufferSize(4096); + placeBlock(ret, allDevices()); + addItem(ret); + return ret; +} + + +FilterItem * ConnectionView::addFilter(const QString & name) { + FilterItem * ret = new FilterItem(); + ret->setDisconnectTimeout(3.); + ret->setBufferSize(65536); + ret->setName(name); + placeBlock(ret, allFilters()); + addItem(ret); + return ret; +} + + +SenderItem * ConnectionView::addSender(const QString & name) { + SenderItem * ret = new SenderItem(); + ret->setName(name); + placeBlock(ret, allSenders()); + addItem(ret); + return ret; +} + + +DeviceItem * ConnectionView::findDevice(const QString & name) const { + QList blockl = blocks(); + foreach (BlockItem * b, blockl) + if ((b->propertyByName("name").value == name) && (b->propertyByName("__type").value.toInt() == __CV_Device)) + return (DeviceItem*)b; + return 0; +} + + +void ConnectionView::loadBus(BlockBusItem * bus) { + bus->setEndpointsNumber(2); +} + + +void ConnectionView::placeBlock(BlockItem * b, QList coll) { + if (coll.isEmpty()) return; + QList collr; + foreach (BlockItem * i, coll) + collr << i->sceneBoundingRect(); + while (true) { + QRectF br = b->sceneBoundingRect(); + bool ok = true; + for (int i = 0; i < collr.size(); ++i) + if (br.intersects(collr[i])) { + ok = false; + break; + } + if (ok) break; + b->moveBy(0., grid_step); + } + b->moveBy(0., grid_step * 2.); +} + + +QList ConnectionView::allByType(int type_) const { + QList blockl = blocks(), ret; + foreach (BlockItem * b, blockl) + if (b->propertyByName("__type").value.toInt() == type_) + ret << b; + return ret; +} diff --git a/test/piqt_utils/piqt_connection_view.h b/test/piqt_utils/piqt_connection_view.h new file mode 100644 index 0000000..09a5fce --- /dev/null +++ b/test/piqt_utils/piqt_connection_view.h @@ -0,0 +1,113 @@ +#ifndef CONNECTION_VIEW_H +#define CONNECTION_VIEW_H + +#include "blockview.h" +#include "piconnection.h" + +const int __CV_Device = 1; +const int __CV_Filter = 2; +const int __CV_Sender = 3; + + +class DeviceItem: public BlockItem { +public: + DeviceItem(); + + void setName(const QString & n) {addProperty(BlockItem::Property("name", "", n)); rename();} + void setPath(const QString & p) {addProperty(BlockItem::Property("device", "", p)); rename();} + void setMode(PIIODevice::DeviceMode m) {addProperty(BlockItem::Property("mode", "", int(m))); rename();} + void setOptions(PIIODevice::DeviceOptions o) {addProperty(BlockItem::Property("options", "", int(o))); rename();} + void setDisconnectTimeout(double v) {addProperty(BlockItem::Property("disconnectTimeout", "", v)); rename();} + void setBufferSize(int v) {addProperty(BlockItem::Property("bufferSize", "", v)); rename();} + + QString name() const {return propertyByName("name").value.toString();} + QString path() const {return propertyByName("device").value.toString();} + PIIODevice::DeviceMode mode() const {return PIIODevice::DeviceMode(propertyByName("mode").value.toInt());} + PIIODevice::DeviceOptions options() const {return PIIODevice::DeviceOptions(propertyByName("options").value.toInt());} + double disconnectTimeout() const {return propertyByName("disconnectTimeout").value.toDouble();} + int bufferSize() const {return propertyByName("bufferSize").value.toInt();} + + void rename(); + +protected: + AlignedTextItem * text_name, * text_path; + +}; + + +class FilterItem: public BlockItem { +public: + FilterItem(); + + void setName(const QString & n) {addProperty(BlockItem::Property("name", "", n)); rename();} + void setMode(PIPacketExtractor::SplitMode m) {addProperty(BlockItem::Property("mode", "", int(m))); rename();} + void setHeader(const QString & v) {addProperty(BlockItem::Property("header", "", v)); rename();} + void setFooter(const QString & v) {addProperty(BlockItem::Property("footer", "", v)); rename();} + void setTimeout(double v) {addProperty(BlockItem::Property("timeout", "", v)); rename();} + void setPacketSize(int v) {addProperty(BlockItem::Property("size", "", v)); rename();} + void setDisconnectTimeout(double v) {addProperty(BlockItem::Property("disconnectTimeout", "", v)); rename();} + void setBufferSize(int v) {addProperty(BlockItem::Property("bufferSize", "", v)); rename();} + + QString name() const {return propertyByName("name").value.toString();} + PIPacketExtractor::SplitMode mode() const {return PIPacketExtractor::SplitMode(propertyByName("mode").value.toInt());} + QString header() const {return propertyByName("header").value.toString();} + QString footer() const {return propertyByName("footer").value.toString();} + double timeout() const {return propertyByName("timeout").value.toDouble();} + int packetSize() const {return propertyByName("size").value.toInt();} + double disconnectTimeout() const {return propertyByName("disconnectTimeout").value.toDouble();} + int bufferSize() const {return propertyByName("bufferSize").value.toInt();} + + void rename(); + +protected: + AlignedTextItem * text_name, * text_mode; + +}; + + +class SenderItem: public BlockItem { +public: + SenderItem(); + + void setName(const QString & n) {addProperty(BlockItem::Property("name", "", n)); rename();} + void setData(const QString & p) {addProperty(BlockItem::Property("data", "", p)); rename();} + void setFrequency(double m) {addProperty(BlockItem::Property("frequency", "", m)); rename();} + + QString name() const {return propertyByName("name").value.toString();} + QString data() const {return propertyByName("data").value.toString();} + double frequency() const {return propertyByName("frequency").value.toDouble();} + + void rename(); + +protected: + AlignedTextItem * text_name, * text_frequency; + +}; + + +class ConnectionView: public BlockView { + Q_OBJECT +public: + explicit ConnectionView(QWidget * parent = 0); + ~ConnectionView(); + + DeviceItem * addDevice(const QString & name, const QString & path); + FilterItem * addFilter(const QString & name); + SenderItem * addSender(const QString & name); + DeviceItem * findDevice(const QString & name) const; + QList allDevices() const {return allByType(__CV_Device);} + QList allFilters() const {return allByType(__CV_Filter);} + QList allSenders() const {return allByType(__CV_Sender);} + +private: + QSize sizeHint() const {return QSize(800, 600);} + void loadBus(BlockBusItem * bus); + void placeBlock(BlockItem * b, QList coll); + + QList allByType(int type_) const; + +private slots: + +}; + +#endif // CONNECTION_VIEW_H diff --git a/test/piqt_utils/piqt_highlighter.cpp b/test/piqt_utils/piqt_highlighter.cpp new file mode 100644 index 0000000..8e6ae64 --- /dev/null +++ b/test/piqt_utils/piqt_highlighter.cpp @@ -0,0 +1,70 @@ +#include "piqt_highlighter.h" + + +ConfigHighlighter::ConfigHighlighter(QTextDocument * parent): QSyntaxHighlighter(parent) { + HighlightingRule rule; + + valueNameFormat.setForeground(QColor(0, 64, 154)); + rule.pattern = QRegExp("[^=]"); //"\\b[A-Za-z0-9_]+(?=\\()"); + rule.format = valueNameFormat; + highlightingRules.append(rule); + + valueFormat.setForeground(QColor(192, 0, 0)); + rule.pattern = QRegExp("=[^\n]*"); + rule.format = valueFormat; + highlightingRules.append(rule); + + equalFormat.setFontWeight(QFont::Bold); + equalFormat.setForeground(QColor(96, 126, 0)); + rule.pattern = QRegExp("="); + rule.format = equalFormat; + highlightingRules.append(rule); + + sectionFormat.setFontWeight(QFont::Bold); + sectionFormat.setForeground(QColor(0, 32, 64)); + rule.pattern = QRegExp("\\[.*\\]"); + rule.format = sectionFormat; + highlightingRules.append(rule); + + //substFormat.setFontWeight(QFont::Bold); + substFormat.setForeground(QColor(192, 0, 192)); + rule.pattern = QRegExp("\\$\\{.*\\}+"); + rule.pattern.setMinimal(true); + rule.format = substFormat; + highlightingRules.append(rule); + rule.pattern = QRegExp("\\$\\{[^\\{]*\\}+"); + highlightingRules.append(rule); + + singleLineCommentFormat.setFontItalic(true); + singleLineCommentFormat.setForeground(QColor(128, 128, 128)); + rule.pattern = QRegExp("#[^\n]*"); + rule.format = singleLineCommentFormat; + highlightingRules.append(rule); + + spaceFormat.setForeground(QColor(210, 210, 210)); + + //commentStartExpression = QRegExp("/\\*"); + //commentEndExpression = QRegExp("\\*/"); +} + + +void ConfigHighlighter::highlightBlock(const QString & text) { + foreach (const HighlightingRule &rule, highlightingRules) { + QRegExp expression(rule.pattern); + int index = expression.indexIn(text); + while (index >= 0) { + int length = expression.matchedLength(); + setFormat(index, length, rule.format); + index = expression.indexIn(text, index + length); + } + } + setCurrentBlockState(0); + + QRegExp expression = QRegExp("[ |\t]"); + int index = expression.indexIn(text); + while (index >= 0) { + int length = expression.matchedLength(); + setFormat(index, length, spaceFormat); + index = expression.indexIn(text, index + length); + } +} diff --git a/test/piqt_utils/piqt_highlighter.h b/test/piqt_utils/piqt_highlighter.h new file mode 100644 index 0000000..1d481bb --- /dev/null +++ b/test/piqt_utils/piqt_highlighter.h @@ -0,0 +1,31 @@ +#ifndef CONF_HIGHLIGHTER_H +#define CONF_HIGHLIGHTER_H + +#include +#include +#include + +class QTextDocument; + +class ConfigHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + ConfigHighlighter(QTextDocument *parent = 0); + + QTextCursor cursor; + +private: + void highlightBlock(const QString &text); + + struct HighlightingRule { + QRegExp pattern; + QTextCharFormat format; + }; + + QVector highlightingRules; + QRegExp commentStartExpression, commentEndExpression; + QTextCharFormat singleLineCommentFormat, valueNameFormat, valueFormat, equalFormat, sectionFormat, spaceFormat, substFormat; +}; + +#endif // CONF_HIGHTLIGHTER_H diff --git a/test/piqt_utils/piqt_iodevice_edit.cpp b/test/piqt_utils/piqt_iodevice_edit.cpp new file mode 100644 index 0000000..1eb8355 --- /dev/null +++ b/test/piqt_utils/piqt_iodevice_edit.cpp @@ -0,0 +1,92 @@ +#include "piqt_iodevice_edit.h" +#include "piqt_iodevice_edit_dialog.h" +#include "qvariantedit_custom.h" +#include +#include +#include +#include +#include + + +IODeviceEdit::IODeviceEdit(QWidget * parent): QWidget(parent) { + dlg = new IODeviceEditDialog(); + line = new QLineEdit(); + btn = new QToolButton(); + setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); + layout()->setContentsMargins(0, 0, 0, 0); + layout()->addWidget(line); + layout()->addWidget(btn); + connect(btn, SIGNAL(clicked(bool)), this, SLOT(buttonDlg_clicked())); + line->setReadOnly(true); + btn->setText(QString()); + btn->setIcon(QIcon(":/icons/configure.png")); + btn->setToolTip(tr("Edit ...")); +} + + +IODeviceEdit::~IODeviceEdit() { + delete dlg; +} + + +QVariant IODeviceEdit::value() const { + return QVariant::fromValue(dev); +} + + +bool IODeviceEdit::isReadOnly() const { + return btn->isHidden(); +} + + +void IODeviceEdit::setDevice(const QAD::IODevice & d) { + if (dev.toString() == d.toString()) return; + dev = d; + line->setText(dev.toString()); + line->setCursorPosition(0); + emit valueChanged(); +} + + +void IODeviceEdit::setValue(const QVariant & v) { + setDevice(v.value()); +} + + +void IODeviceEdit::setReadOnly(bool yes) { + btn->setHidden(yes); +} + + +void IODeviceEdit::buttonDlg_clicked() { + QAD::IODevice d = dlg->getIODevice(dev); + if (!d.isValid()) return; + setDevice(d); +} + + + +class Factory: public QVariantEditorFactoryBase { +public: + Factory() {} + virtual QWidget * createEditor() {return new IODeviceEdit();} +}; + + +__IODeviceEditRegistrator__::__IODeviceEditRegistrator__() { + QVariantEditorFactories::registerEditorFactory(qMetaTypeId(), new Factory()); + __QADTypesRegistrator__::instance()->toString_funcs.insert(qMetaTypeId(), &QAD_IODevice_toString); +} + + +void QAD_IODevice_toString(const QVariant & v, QString & r) { + PIVariantTypes::IODevice sd = Q2PIVariant(v).toIODevice(); +// piCout << sd; + PIIODevice * rd = PIIODevice::createFromVariant(sd); + if (rd) { + PIString ps = rd->constructFullPath(); + r = PI2QString(ps); + } else { + piCout << "error in " << sd; + } +} diff --git a/test/piqt_utils/piqt_iodevice_edit.h b/test/piqt_utils/piqt_iodevice_edit.h new file mode 100644 index 0000000..8a64e36 --- /dev/null +++ b/test/piqt_utils/piqt_iodevice_edit.h @@ -0,0 +1,52 @@ +#ifndef PIQT_IODEVICE_EDIT_H +#define PIQT_IODEVICE_EDIT_H + +#include +#include "qad_types.h" + +class QLineEdit; +class QToolButton; +class IODeviceEditDialog; + + +class IODeviceEdit: public QWidget { + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) +public: + explicit IODeviceEdit(QWidget * parent = 0); + ~IODeviceEdit(); + + QVariant value() const; + bool isReadOnly() const; + +private: + void setDevice(const QAD::IODevice & d); + + QLineEdit * line; + QToolButton * btn; + IODeviceEditDialog * dlg; + QAD::IODevice dev; + +public slots: + void setValue(const QVariant & v); + void setReadOnly(bool yes); + +private slots: + void buttonDlg_clicked(); + +signals: + void valueChanged(); + +}; + + +class __IODeviceEditRegistrator__ { +public: + __IODeviceEditRegistrator__(); +}; + +static __IODeviceEditRegistrator__ __iodeviceeditregistrator__; + +void QAD_IODevice_toString(const QVariant & v, QString & r); +#endif // PIQT_IODEVICE_EDIT_H diff --git a/test/piqt_utils/piqt_iodevice_edit_dialog.cpp b/test/piqt_utils/piqt_iodevice_edit_dialog.cpp new file mode 100644 index 0000000..f7d3e4c --- /dev/null +++ b/test/piqt_utils/piqt_iodevice_edit_dialog.cpp @@ -0,0 +1,112 @@ +#include "ui_piqt_iodevice_edit_dialog.h" +#include "piqt_iodevice_edit_dialog.h" +#include "piqt.h" +#include "picodeinfo.h" +#include "piiodevice.h" +#include + + +IODeviceEditDialog::IODeviceEditDialog(QWidget * parent): QDialog(parent) { + ui = new Ui::IODeviceEditDialog(); + ui->setupUi(this); + PICodeInfo::EnumInfo * ei = PICodeInfo::enumsInfo->value("PIIODevice::DeviceMode"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) + ui->comboMode->addItem(PI2QString(e.name + " (" + PIString::fromNumber(e.value) + ")"), QVariant::fromValue(e.value)); + } + ui->comboMode->setCurrentIndex(ui->comboMode->count() - 1); + ei = PICodeInfo::enumsInfo->value("PIIODevice::DeviceOption"); + if (ei) { + piForeachC (PICodeInfo::EnumeratorInfo & e, ei->members) { + QCheckBox * cb = new QCheckBox(); + cb->setText(PI2QString(e.name + " (" + PIString::fromNumber(e.value) + ")")); + cb->setProperty("__value", e.value); + ui->layoutOptions->addWidget(cb); + } + } + PIStringList pl = PIIODevice::availablePrefixes(); + piForeachC (PIString & p, pl) { + ui->comboType->addItem(PI2QString(p)); + } +} + + +IODeviceEditDialog::~IODeviceEditDialog() { +} + + +int IODeviceEditDialog::getOptions() const { + int ret = 0; + for (int i = 0; i < ui->layoutOptions->count(); ++i) { + QCheckBox * cb = qobject_cast(ui->layoutOptions->itemAt(i)->widget()); + if (!cb) continue; + if (cb->isChecked()) + ret |= cb->property("__value").toInt(); + } + return ret; +} + + +void IODeviceEditDialog::setOptions(int o) { + for (int i = 0; i < ui->layoutOptions->count(); ++i) { + QCheckBox * cb = qobject_cast(ui->layoutOptions->itemAt(i)->widget()); + if (!cb) continue; + int cbf = cb->property("__value").toInt(); + cb->setChecked((o & cbf) == cbf); + } +} + + +void IODeviceEditDialog::on_comboType_currentIndexChanged(int index) { + PIString prefix = Q2PIString(ui->comboType->currentText()); + PIVector rd(PICollection::groupElements("__PIIODevices__")); + piForeachC (PIObject * d, rd) { + const PIIODevice * dev = ((const PIIODevice * )d); + if (prefix != dev->fullPathPrefix()) + continue; + ps = PI2QPropertyStorage(dev->constructVariant().get()); + ui->widgetProperties->setStorage(&ps); + return; + } +} + + +QAD::IODevice IODeviceEditDialog::getIODevice(const QAD::IODevice & d) { + setValue(d); + if (QDialog::exec() != QDialog::Accepted) + return QAD::IODevice(); + return value(); +} + + +QAD::IODevice IODeviceEditDialog::value() const { + QAD::IODevice d; + ui->widgetProperties->applyProperties(); + d.prefix = ui->comboType->currentText(); + d.mode = ui->comboMode->itemData(ui->comboMode->currentIndex()).toInt(); + d.options = getOptions(); + d.props = ps; + return d; +} + + +void IODeviceEditDialog::setValue(const QAD::IODevice & d) { +#if QT_VERSION >= 0x050000 + ui->comboType->setCurrentText(d.prefix); +#else + for (int i = 0; i < ui->comboType->count(); ++i) { + if (ui->comboType->itemText(i) == d.prefix) { + ui->comboType->setCurrentIndex(i); + break; + } + } +#endif + for (int i = 0; i < ui->comboMode->count(); ++i) + if (ui->comboMode->itemData(i).toInt() == d.mode) { + ui->comboMode->setCurrentIndex(i); + break; + } + setOptions(d.options); + ps = d.props; + ui->widgetProperties->setStorage(&ps); +} diff --git a/test/piqt_utils/piqt_iodevice_edit_dialog.h b/test/piqt_utils/piqt_iodevice_edit_dialog.h new file mode 100644 index 0000000..056826f --- /dev/null +++ b/test/piqt_utils/piqt_iodevice_edit_dialog.h @@ -0,0 +1,34 @@ +#ifndef PIQT_IODEVICE_EDIT_DIALOG_H +#define PIQT_IODEVICE_EDIT_DIALOG_H + +#include +#include "qad_types.h" +#include "propertystorage.h" + +namespace Ui { + class IODeviceEditDialog; +} + +class IODeviceEditDialog: public QDialog { + Q_OBJECT +public: + explicit IODeviceEditDialog(QWidget * parent = 0); + ~IODeviceEditDialog(); + + QAD::IODevice getIODevice(const QAD::IODevice & d); + +private: + QAD::IODevice value() const; + void setValue(const QAD::IODevice & d); + int getOptions() const; + void setOptions(int o); + + PropertyStorage ps; + Ui::IODeviceEditDialog * ui; + +private slots: + void on_comboType_currentIndexChanged(int index); + +}; + +#endif // PIQT_IODEVICE_EDIT_DIALOG_H diff --git a/test/piqt_utils/piqt_iodevice_edit_dialog.ui b/test/piqt_utils/piqt_iodevice_edit_dialog.ui new file mode 100644 index 0000000..f69db58 --- /dev/null +++ b/test/piqt_utils/piqt_iodevice_edit_dialog.ui @@ -0,0 +1,138 @@ + + + IODeviceEditDialog + + + + 0 + 0 + 387 + 345 + + + + IODevice + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Type: + + + + + + + + + + Mode: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Options: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 2 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + PropertyStorageEditor + QWidget +
propertystorage_editor.h
+ 1 +
+
+ + + + buttonBox + accepted() + IODeviceEditDialog + accept() + + + 227 + 326 + + + 144 + 302 + + + + + buttonBox + rejected() + IODeviceEditDialog + reject() + + + 325 + 315 + + + 304 + 306 + + + + + + recreateConnection() + +
diff --git a/test/piqt_utils/qpiconnection.cpp b/test/piqt_utils/qpiconnection.cpp new file mode 100644 index 0000000..2a6c3a5 --- /dev/null +++ b/test/piqt_utils/qpiconnection.cpp @@ -0,0 +1,31 @@ +#include "qpiconnection.h" + + +QPIConnection::QPIConnection(const QString & name): QObject(), PIConnection(Q2PIString(name)) { + setObjectName(name); + CONNECTU(this, dataReceivedEvent, this, piDataRec); + CONNECTU(this, packetReceivedEvent, this, piPacketRec); +} + + +bool QPIConnection::loadFromCMFile(const QString & file) { + QPIConfig f(file, QIODevice::ReadOnly); + if (!f.isOpen()) return false; + QByteArray cd = f.getValue("config", QByteArray()); + PIString cs = Q2PIString(QString::fromLatin1(cd)); + configureFromString(&cs, Q2PIString(f.getValue("name", ""))); + return true; +} + + +void QPIConnection::piDataRec(const PIString & from, const PIByteArray & data) { + //piCout << from << PICoutManipulators::Hex << data; + QMetaObject::invokeMethod(this, "qDataReceivedEvent", Qt::QueuedConnection, + Q_ARG(QString, PI2QString(from)), Q_ARG(QByteArray, PI2QByteArray(data))); +} + + +void QPIConnection::piPacketRec(const PIString & from, const PIByteArray & data) { + QMetaObject::invokeMethod(this, "qPacketReceivedEvent", Qt::QueuedConnection, + Q_ARG(QString, PI2QString(from)), Q_ARG(QByteArray, PI2QByteArray(data))); +} diff --git a/test/piqt_utils/qpiconnection.h b/test/piqt_utils/qpiconnection.h new file mode 100644 index 0000000..e9ff617 --- /dev/null +++ b/test/piqt_utils/qpiconnection.h @@ -0,0 +1,31 @@ +#ifndef QPICONNECTION_H +#define QPICONNECTION_H + +#include +#include +#include "piconnection.h" +#include "qpiconfig.h" +#include "piqt.h" + +class QPIConnection: public QObject, public PIConnection { + Q_OBJECT + PIOBJECT_SUBCLASS(QPIConnection, PIConnection) +public: + QPIConnection(const QString & name = QString()); + + bool loadFromCMFile(const QString & file); + +protected: + void propertyChanged(const PIString & ) {setObjectName(PI2QString(name()));} + EVENT_HANDLER2(void, piDataRec, const PIString &, from, const PIByteArray &, data); + EVENT_HANDLER2(void, piPacketRec, const PIString &, from, const PIByteArray &, data); + +public slots: + +signals: + void qDataReceivedEvent(QString from, QByteArray data); + void qPacketReceivedEvent(QString from, QByteArray data); + +}; + +#endif // QPICONNECTION_H diff --git a/test/project_fs/ProjectFilesystemPlugin.json.in b/test/project_fs/ProjectFilesystemPlugin.json.in new file mode 100644 index 0000000..0d8ab6d --- /dev/null +++ b/test/project_fs/ProjectFilesystemPlugin.json.in @@ -0,0 +1,11 @@ +{ + \"Name\" : \"ProjectFilesystemPlugin\", + \"Version\" : \"1.0.0\", + \"CompatVersion\" : \"1.0.0\", + \"Vendor\" : \"Peri4\", + \"Copyright\" : \"(C) Peri4\", + \"License\" : \"LGPLv3\", + \"Description\" : \"LGPLv3\", + \"Url\" : \"\", + $$dependencyList +} diff --git a/test/project_fs/filterdialog.cpp b/test/project_fs/filterdialog.cpp new file mode 100644 index 0000000..2137cd1 --- /dev/null +++ b/test/project_fs/filterdialog.cpp @@ -0,0 +1,91 @@ +#include "filterdialog.h" +#include +#include + + +FilterDialog::FilterDialog(QWidget * parent): QDialog(parent) { + setupUi(this); + toolButton->setIcon(Utils::Icons::CLEAN.icon()); + toolButton_2->setIcon(Utils::Icons::CLEAN.icon()); + toolButton_3->setIcon(Utils::Icons::CLEAN.icon()); + toolButton_4->setIcon(Utils::Icons::CLEAN.icon()); +} + + +void FilterDialog::changeEvent(QEvent *e) { + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +FilterDialog::Filter FilterDialog::filter() const { + FilterDialog::Filter ret; + ret.files_show = getFilters(lineFilesShow); + ret.files_hide = getFilters(lineFilesHide); + ret.dirs_show = getFilters(lineDirsShow); + ret.dirs_hide = getFilters(lineDirsHide); + return ret; +} + +void FilterDialog::setFilter(const FilterDialog::Filter & f) { + setFilters(lineFilesShow, f.files_show); + setFilters(lineFilesHide, f.files_hide); + setFilters(lineDirsShow, f.dirs_show); + setFilters(lineDirsHide, f.dirs_hide); +} + + +QStringList FilterDialog::getFilters(QLineEdit * le) const { + if (!le) return QStringList(); + QStringList ret = le->text().split(","); + for (int i = 0; i < ret.size(); ++i) + ret[i] = ret[i].trimmed(); + ret.removeAll(""); + return ret; +} + + +void FilterDialog::setFilters(QLineEdit * le, QStringList f) { + if (!le) return; + le->setText(f.join(",")); +} + + +FilterDialog::Filter::Filter(const QVariant & v) { + QByteArray ba = v.toByteArray(); + if (ba.isEmpty()) return; + QDataStream s(ba); + s >> files_show >> files_hide >> dirs_show >> dirs_hide; +} + + +QVariant FilterDialog::Filter::toVariant() const { + QByteArray ba; + QDataStream s(&ba, QIODevice::ReadWrite); + s << files_show << files_hide << dirs_show << dirs_hide; + return QVariant(ba); +} + + +bool FilterDialog::Filter::filterLogic(const QStringList & fshow, const QStringList & fhide, const QString & path) const { + if (fshow.isEmpty() && fhide.isEmpty()) return true; + if (!fhide.isEmpty()) { + if (QDir::match(fhide, path)) + return false; + else { + if (fshow.isEmpty()) + return true; + else + return QDir::match(fshow, path); + } + } + if (!fshow.isEmpty()) + return QDir::match(fshow, path); + return true; +} diff --git a/test/project_fs/filterdialog.h b/test/project_fs/filterdialog.h new file mode 100644 index 0000000..0d1e2a3 --- /dev/null +++ b/test/project_fs/filterdialog.h @@ -0,0 +1,37 @@ +#ifndef FILTERDIALOG_H +#define FILTERDIALOG_H + +#include "ui_filterdialog.h" + +class FilterDialog: public QDialog, private Ui::FilterDialog +{ + Q_OBJECT +public: + explicit FilterDialog(QWidget *parent = 0); + + struct Filter { + Filter() {} + Filter(const QVariant & v); + QVariant toVariant() const; + bool filterFile(const QString & path) const {return filterLogic(files_show, files_hide, path);} + bool filterDir(const QString & path) const {return filterLogic(dirs_show, dirs_hide, path);} + QStringList files_show; + QStringList files_hide; + QStringList dirs_show; + QStringList dirs_hide; + private: + bool filterLogic(const QStringList & fshow, const QStringList & fhide, const QString & path) const; + }; + + Filter filter() const; + void setFilter(const Filter & f); + +protected: + void changeEvent(QEvent *e); + + QStringList getFilters(QLineEdit * le) const; + void setFilters(QLineEdit * le, QStringList f); + +}; + +#endif // FILTERDIALOG_H diff --git a/test/project_fs/filterdialog.ui b/test/project_fs/filterdialog.ui new file mode 100644 index 0000000..df38a3f --- /dev/null +++ b/test/project_fs/filterdialog.ui @@ -0,0 +1,226 @@ + + + FilterDialog + + + + 0 + 0 + 342 + 250 + + + + Project filesystem filters + + + + + + Input filters, separated by ",": + + + + + + + + + Show dirs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + Hide files: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Show files: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + Hide dirs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + lineFilesShow + lineFilesHide + lineDirsShow + lineDirsHide + toolButton + toolButton_2 + toolButton_3 + toolButton_4 + + + + + buttonBox + accepted() + FilterDialog + accept() + + + 259 + 238 + + + 157 + 274 + + + + + buttonBox + rejected() + FilterDialog + reject() + + + 327 + 238 + + + 286 + 274 + + + + + toolButton + clicked() + lineFilesShow + clear() + + + 329 + 60 + + + 269 + 58 + + + + + toolButton_2 + clicked() + lineFilesHide + clear() + + + 329 + 93 + + + 295 + 91 + + + + + toolButton_3 + clicked() + lineDirsShow + clear() + + + 329 + 126 + + + 295 + 124 + + + + + toolButton_4 + clicked() + lineDirsHide + clear() + + + 329 + 159 + + + 295 + 157 + + + + + diff --git a/test/project_fs/icons/edit-find.png b/test/project_fs/icons/edit-find.png new file mode 100644 index 0000000..140e581 Binary files /dev/null and b/test/project_fs/icons/edit-find.png differ diff --git a/test/project_fs/icons/utilities-terminal.png b/test/project_fs/icons/utilities-terminal.png new file mode 100644 index 0000000..9c8effe Binary files /dev/null and b/test/project_fs/icons/utilities-terminal.png differ diff --git a/test/project_fs/projectfilesystem.qrc b/test/project_fs/projectfilesystem.qrc new file mode 100644 index 0000000..2b5712f --- /dev/null +++ b/test/project_fs/projectfilesystem.qrc @@ -0,0 +1,6 @@ + + + icons/edit-find.png + icons/utilities-terminal.png + + diff --git a/test/project_fs/projectfilesystemplugin.cpp b/test/project_fs/projectfilesystemplugin.cpp new file mode 100644 index 0000000..37b3c58 --- /dev/null +++ b/test/project_fs/projectfilesystemplugin.cpp @@ -0,0 +1,89 @@ +#include "projectfilesystemplugin.h" +#include "projectfilesystempluginconstants.h" +#include "projectfilesystemwidget.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace ProjectFilesystemPlugin { + namespace Internal { + + ProjectFilesystemPluginPlugin::ProjectFilesystemPluginPlugin() + { + // Create your members + } + + ProjectFilesystemPluginPlugin::~ProjectFilesystemPluginPlugin() + { + // Unregister objects from the plugin manager's object pool + // Delete members + } + + bool ProjectFilesystemPluginPlugin::initialize(const QStringList &arguments, QString *errorString) + { + // Register objects in the plugin manager's object pool + // Load settings + // Add actions to menus + // Connect to other plugins' signals + // In the initialize function, a plugin can be sure that the plugins it + // depends on have initialized their members. + + Q_UNUSED(arguments) + Q_UNUSED(errorString) + + /*auto action = new QAction(tr("ProjectFilesystemPlugin Action"), this); + Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID, + Core::Context(Core::Constants::C_GLOBAL)); + cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A"))); + connect(action, &QAction::triggered, this, &ProjectFilesystemPluginPlugin::triggerAction); + + Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::MENU_ID); + menu->menu()->setTitle(tr("ProjectFilesystemPlugin")); + menu->addAction(cmd); + Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu); + + qDebug() << Core::IContext::widget();*/ + //addAutoReleasedObject(new ProjectFSWidgetFactory()); + (new ProjectFSWidgetFactory())->setParent(this); + + return true; + } + + void ProjectFilesystemPluginPlugin::extensionsInitialized() + { + Utils::globalMacroExpander()->registerPrefix("ProjectFilesystem", "Project filesystem plugin", [](QString v) -> QString {return v;} ); + Utils::globalMacroExpander()->registerFileVariables("ProjectFilesystem", "Selected item", + []() -> QString { return projectfs_menu_target.absoluteFilePath();}); + // Retrieve objects from the plugin manager's object pool + // In the extensionsInitialized function, a plugin can be sure that all + // plugins that depend on it are completely initialized. + } + + ExtensionSystem::IPlugin::ShutdownFlag ProjectFilesystemPluginPlugin::aboutToShutdown() + { + // Save settings + // Disconnect from signals that are not needed during shutdown + // Hide UI (if you add UI that is not in the main window directly) + return SynchronousShutdown; + } + + void ProjectFilesystemPluginPlugin::triggerAction() + { + QMessageBox::information(Core::ICore::mainWindow(), + tr("Action Triggered"), + tr("This is an action from ProjectFilesystemPlugin.")); + } + + } // namespace Internal +} // namespace ProjectFilesystemPlugin diff --git a/test/project_fs/projectfilesystemplugin.h b/test/project_fs/projectfilesystemplugin.h new file mode 100644 index 0000000..4262865 --- /dev/null +++ b/test/project_fs/projectfilesystemplugin.h @@ -0,0 +1,29 @@ +#pragma once + +#include "projectfilesystemplugin_global.h" +#include "projectfilesystemwidgetplugin.h" + +#include + +namespace ProjectFilesystemPlugin { + namespace Internal { + + class ProjectFilesystemPluginPlugin : public ExtensionSystem::IPlugin + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ProjectFilesystemPlugin.json") + + public: + ProjectFilesystemPluginPlugin(); + ~ProjectFilesystemPluginPlugin(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + ShutdownFlag aboutToShutdown(); + + private: + void triggerAction(); + }; + + } // namespace Internal +} // namespace ProjectFilesystemPlugin diff --git a/test/project_fs/projectfilesystemplugin.pro b/test/project_fs/projectfilesystemplugin.pro new file mode 100644 index 0000000..ec797d3 --- /dev/null +++ b/test/project_fs/projectfilesystemplugin.pro @@ -0,0 +1,61 @@ +DEFINES += PROJECTFILESYSTEMPLUGIN_LIBRARY + +# ProjectFilesystemPlugin files + +SOURCES += projectfilesystemplugin.cpp \ + projectfilesystemwidget.cpp \ + projectfilesystemwidgetplugin.cpp \ + filterdialog.cpp + +HEADERS += projectfilesystemplugin.h \ + projectfilesystemplugin_global.h \ + projectfilesystempluginconstants.h \ + projectfilesystemwidget.h \ + projectfilesystemwidgetplugin.h \ + filterdialog.h + +FORMS += \ + projectfilesystemwidget.ui \ + filterdialog.ui + +RESOURCES += \ + projectfilesystem.qrc + +#QT += network + +# Qt Creator linking + +## Either set the IDE_SOURCE_TREE when running qmake, +## or set the QTC_SOURCE environment variable, to override the default setting +isEmpty(IDE_SOURCE_TREE): IDE_SOURCE_TREE = $$(SDK_QTCREATOR_SRC) + +## Either set the IDE_BUILD_TREE when running qmake, +## or set the QTC_BUILD environment variable, to override the default setting +isEmpty(IDE_BUILD_TREE): IDE_BUILD_TREE = $$(SDK_QTCREATOR_BUILD) + +## uncomment to build plugin into user config directory +## /plugins/ +## where is e.g. +## "%LOCALAPPDATA%\QtProject\qtcreator" on Windows Vista and later +## "$XDG_DATA_HOME/data/QtProject/qtcreator" or "~/.local/share/data/QtProject/qtcreator" on Linux +## "~/Library/Application Support/QtProject/Qt Creator" on OS X +#USE_USER_DESTDIR = yes + +###### If the plugin can be depended upon by other plugins, this code needs to be outsourced to +###### _dependencies.pri, where is the name of the directory containing the +###### plugin's sources. + +QTC_PLUGIN_NAME = ProjectFilesystemPlugin +QTC_LIB_DEPENDS += \ + # nothing here at this time + +QTC_PLUGIN_DEPENDS += \ + coreplugin \ + projectexplorer + +QTC_PLUGIN_RECOMMENDS += \ + # optional plugin dependencies. nothing here at this time + +###### End _dependencies.pri contents ###### + +include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri) diff --git a/test/project_fs/projectfilesystemplugin_global.h b/test/project_fs/projectfilesystemplugin_global.h new file mode 100644 index 0000000..f2e22e8 --- /dev/null +++ b/test/project_fs/projectfilesystemplugin_global.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#if defined(PROJECTFILESYSTEMPLUGIN_LIBRARY) +# define PROJECTFILESYSTEMPLUGINSHARED_EXPORT Q_DECL_EXPORT +#else +# define PROJECTFILESYSTEMPLUGINSHARED_EXPORT Q_DECL_IMPORT +#endif diff --git a/test/project_fs/projectfilesystempluginconstants.h b/test/project_fs/projectfilesystempluginconstants.h new file mode 100644 index 0000000..2fb2893 --- /dev/null +++ b/test/project_fs/projectfilesystempluginconstants.h @@ -0,0 +1,10 @@ +#pragma once + +namespace ProjectFilesystemPlugin { + namespace Constants { + + const char ACTION_ID[] = "ProjectFilesystemPlugin.Action"; + const char MENU_ID[] = "ProjectFilesystemPlugin.Menu"; + + } // namespace ProjectFilesystemPlugin +} // namespace Constants diff --git a/test/project_fs/projectfilesystemwidget.cpp b/test/project_fs/projectfilesystemwidget.cpp new file mode 100644 index 0000000..764fcf5 --- /dev/null +++ b/test/project_fs/projectfilesystemwidget.cpp @@ -0,0 +1,308 @@ +#include "projectfilesystemwidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QFileInfo projectfs_menu_target; + + +ProjectFilesystemWidget::ProjectFilesystemWidget(QWidget * parent): QWidget(parent) { + setupUi(this); + int is = style()->pixelMetric(QStyle::PM_ButtonIconSize, 0, this); + label->setFixedSize(is, is); + buttonClear->setIcon(Utils::Icons::CLEAN.icon()); + buttonExpand->setIcon(Utils::Icons::EXPAND.icon()); + buttonCollapse->setIcon(Utils::Icons::COLLAPSE.icon()); + actionOpen_here->setIcon(Utils::Icons::OPENFILE.icon()); + actionOpen_external->setIcon(Utils::Icons::OPENFILE.icon()); + actionShow_external->setIcon(Core::FileIconProvider::icon(QFileIconProvider::Folder)); + actionCopy_name->setIcon(Utils::Icons::COPY.icon()); + actionCopy_path->setIcon(Utils::Icons::COPY.icon()); + popup_menu.addActions(QList() << actionOpen_here << actionOpen_external << actionShow_external << actionOpen_terminal); + popup_menu.addSeparator(); + popup_menu.addActions(QList() << actionCopy_name << actionCopy_path); + proj_plug = 0; + //connect(ProjectExplorer::ProjectTree::instance(), SIGNAL(subtreeChanged(ProjectExplorer::FolderNode*)), this, SLOT(projectsChanged())); + connect(ProjectExplorer::SessionManager::instance(), SIGNAL(startupProjectChanged(ProjectExplorer::Project *)), this, SLOT(startupProjectChanged())); + connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectAdded(ProjectExplorer::Project*)), this, SLOT(projectsChanged())); + connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectDisplayNameChanged(ProjectExplorer::Project*)), this, SLOT(projectsChanged())); + connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectRemoved(ProjectExplorer::Project*)), this, SLOT(projectsChanged())); + //connect(ProjectExplorer::SessionManager::instance(), SIGNAL(), this, SLOT(startupProjectChanged())); + connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), this, SLOT(fileChanged())); + projectsChanged(); +} + + +void ProjectFilesystemWidget::setCurrentFilter(const FilterDialog::Filter & v) { + cur_filter = v; + projectsChanged(); +} + + +void ProjectFilesystemWidget::changeEvent(QEvent *e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void ProjectFilesystemWidget::createTree(QTreeWidgetItem * ti, const QString & dir) { + QFileInfoList fl = QDir(dir).entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot, QDir::LocaleAware | QDir::DirsFirst); + for (QFileInfo i: fl) { + QString nit = i.fileName(); + if (i.isDir()) { + if (!cur_filter.filterDir(nit)) + continue; + } else { + if (!cur_filter.filterFile(nit)) + continue; + } + QTreeWidgetItem * ni = new QTreeWidgetItem(); + ni->setText(0, nit); + ni->setIcon(0, Core::FileIconProvider::icon(i)); + ni->setData(0, Qt::UserRole, i.absoluteFilePath()); + ni->setData(0, Qt::UserRole + 1, i.isDir()); + item_map[i.absoluteFilePath()] = ni; + if (i.isDir()) { + createTree(ni, dir + QDir::separator() + i.fileName()); + } + ti->addChild(ni); + } + //if (ti->childCount() == 0) delete ti; +} + + +bool ProjectFilesystemWidget::filterTree(QTreeWidgetItem * ti, const QString & filter) { + bool ret = false; + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + QString cit = ci->text(0); + if (ci->data(0, Qt::UserRole + 1).toBool()) { + if (!filterTree(ci, filter)) { + ci->setHidden(true); + continue; + } + ci->setHidden(false); + ret = true; + } else { + bool f = false; + if (filter.isEmpty()) { + f = true; + } else { + f = f || cit.contains(filter); + } + ci->setHidden(!f); + if (f) ret = true; + } + } + return ret; +} + + +void ProjectFilesystemWidget::filter() { + QString f = lineFilter->text(); + for (int i = 0; i < tree->topLevelItemCount(); ++i) { + QTreeWidgetItem * ti = tree->topLevelItem(i); + filterTree(ti, f); + } +} + + +void ProjectFilesystemWidget::rememberExpanded(QTreeWidgetItem * ti) { + //QMessageBox::information(0, ti->data(0, Qt::UserRole).toString(), QString::number(ti->childCount())); + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + if (ci->data(0, Qt::UserRole + 1).toBool()) { + if (ci->isExpanded()) + last_expanded << ci->data(0, Qt::UserRole).toString(); + rememberExpanded(ci); + } + } +} + + +void ProjectFilesystemWidget::restoreExpanded(QTreeWidgetItem * ti) { + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + if (ci->data(0, Qt::UserRole + 1).toBool()) { + if (last_expanded.contains(ci->data(0, Qt::UserRole).toString())) + ci->setExpanded(true); + restoreExpanded(ci); + } + } +} + + +void ProjectFilesystemWidget::setExtVariable() { +} + + +void ProjectFilesystemWidget::projectsChanged() { + last_expanded.clear(); + item_map.clear(); + int spos = tree->verticalScrollBar()->value(); + rememberExpanded(tree->invisibleRootItem()); + tree->clear(); + QApplication::setOverrideCursor(Qt::WaitCursor); + QList pl = ProjectExplorer::SessionManager::projects(); + for (ProjectExplorer::Project * p: pl) { + QTreeWidgetItem * ri = new QTreeWidgetItem(); + QString dir = p->projectDirectory().toString(); + ri->setText(0, p->displayName()); + QFile logo(dir + "/logo.png"); + if (logo.exists()) + ri->setIcon(0, QIcon(logo.fileName())); + else + ri->setIcon(0, Core::FileIconProvider::icon(QFileIconProvider::Folder)); + ri->setData(0, Qt::UserRole, dir); + ri->setData(0, Qt::UserRole + 1, true); + createTree(ri, dir); + tree->addTopLevelItem(ri); + } + startupProjectChanged(); + fileChanged(); + filter(); + restoreExpanded(tree->invisibleRootItem()); + QApplication::restoreOverrideCursor(); + qApp->processEvents(); + tree->verticalScrollBar()->setValue(spos); +} + + +void ProjectFilesystemWidget::filterClicked() { + filter_dialog.setFilter(cur_filter); + if (filter_dialog.exec() == QDialog::Rejected) return; + setCurrentFilter(filter_dialog.filter()); +} + + +void ProjectFilesystemWidget::fileChanged() { + Core::IDocument * cd = Core::EditorManager::instance()->currentDocument(); + if (!cd) return; + QString np = cd->filePath().toString(); + QTreeWidgetItem * ti = item_map.value(np); + if (!ti) return; + tree->setCurrentItem(ti); + tree->expandItem(ti); +} + + +void ProjectFilesystemWidget::startupProjectChanged() { + ProjectExplorer::Project * sp = ProjectExplorer::SessionManager::startupProject(); + QFont f(tree->font()), bf(f); + bf.setBold(true); + for (int i = 0; i < tree->topLevelItemCount(); ++i) { + QTreeWidgetItem * ti = tree->topLevelItem(i); + ti->setFont(0, f); + if (!sp) continue; + if (sp->projectDirectory().toString() == ti->data(0, Qt::UserRole).toString()) + ti->setFont(0, bf); + } +} + + +void ProjectFilesystemWidget::on_tree_itemDoubleClicked(QTreeWidgetItem * item, int) { + if (!item) return; + QString afp = item->data(0, Qt::UserRole).toString(); + bool dir = item->data(0, Qt::UserRole + 1).toBool(); + if (dir) return; + if (afp.isEmpty()) return; + Core::EditorManager::openEditor(afp); +} + + +void ProjectFilesystemWidget::on_lineFilter_textChanged(const QString & ) { + filter(); +} + + +void ProjectFilesystemWidget::on_tree_itemClicked(QTreeWidgetItem * item, int column) { + projectfs_menu_target = QFileInfo(); + if (!item) { + setExtVariable(); + return; + } + projectfs_menu_target = QFileInfo(item->data(0, Qt::UserRole).toString()); + setExtVariable(); +} + + +void ProjectFilesystemWidget::on_tree_customContextMenuRequested(const QPoint & pos) { + projectfs_menu_target = QFileInfo(); + QTreeWidgetItem * item = tree->itemAt(pos); + //QMessageBox::information(this, "", QString::number(index.row())); + if (!item) { + setExtVariable(); + return; + } + projectfs_menu_target = QFileInfo(item->data(0, Qt::UserRole).toString()); + setExtVariable(); + actionOpen_here->setEnabled(!projectfs_menu_target.isDir()); + actionOpen_external->setEnabled(!projectfs_menu_target.isDir()); + popup_menu.popup(tree->mapToGlobal(pos)); +} + + +void ProjectFilesystemWidget::on_actionOpen_here_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + Core::EditorManager::openEditor(projectfs_menu_target.absoluteFilePath(), Core::Constants::K_DEFAULT_TEXT_EDITOR_ID); +} + + +void ProjectFilesystemWidget::on_actionOpen_external_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + QString wd = QDir::current().absolutePath(); + QDir::setCurrent(projectfs_menu_target.absoluteDir().path()); + QDesktopServices::openUrl(QUrl::fromLocalFile(projectfs_menu_target.absoluteFilePath())); + QDir::setCurrent(wd); +} + + +void ProjectFilesystemWidget::on_actionShow_external_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), projectfs_menu_target.absoluteFilePath()); +} + + +void ProjectFilesystemWidget::on_actionOpen_terminal_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + Core::FileUtils::openTerminal(QDir(projectfs_menu_target.absoluteFilePath()).path()); +} + + +void ProjectFilesystemWidget::on_actionCopy_name_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + QApplication::clipboard()->setText(projectfs_menu_target.fileName()); +} + + +void ProjectFilesystemWidget::on_actionCopy_path_triggered() { + if (projectfs_menu_target.path().isEmpty()) return; + QApplication::clipboard()->setText(projectfs_menu_target.absoluteFilePath()); +} diff --git a/test/project_fs/projectfilesystemwidget.h b/test/project_fs/projectfilesystemwidget.h new file mode 100644 index 0000000..a017d13 --- /dev/null +++ b/test/project_fs/projectfilesystemwidget.h @@ -0,0 +1,64 @@ +#ifndef PROJECTFILESYSTEMWIDGET_H +#define PROJECTFILESYSTEMWIDGET_H + +#include "ui_projectfilesystemwidget.h" +#include "filterdialog.h" +#include +#include +#include +#include + +extern QFileInfo projectfs_menu_target; + +class ProjectsModel; + +class ProjectFilesystemWidget: public QWidget, private Ui::ProjectFilesystemWidget +{ + Q_OBJECT +public: + explicit ProjectFilesystemWidget(QWidget * parent = 0); + + FilterDialog::Filter currentFilters() const {return cur_filter;} + QString currentSearch() const {return lineFilter->text();} + + void setCurrentFilter(const FilterDialog::Filter & v); + void setCurrentSearch(QString v) {lineFilter->setText(v);} + +protected: + void changeEvent(QEvent * e); + void createTree(QTreeWidgetItem * ti, const QString & dir); + bool filterTree(QTreeWidgetItem * ti, const QString & filter); + void filter(); + void rememberExpanded(QTreeWidgetItem * ti); + void restoreExpanded(QTreeWidgetItem * ti); + void setExtVariable(); + + ExtensionSystem::IPlugin * proj_plug; + QMenu popup_menu; + QMap item_map; + QSet last_expanded; + FilterDialog filter_dialog; + FilterDialog::Filter cur_filter; + +public slots: + void projectsChanged(); + void filterClicked(); + +private slots: + void fileChanged(); + void startupProjectChanged(); + + void on_tree_itemDoubleClicked(QTreeWidgetItem * item, int ); + void on_lineFilter_textChanged(const QString &); + void on_tree_itemClicked(QTreeWidgetItem *item, int column); + void on_tree_customContextMenuRequested(const QPoint & pos); + void on_actionOpen_here_triggered(); + void on_actionOpen_external_triggered(); + void on_actionShow_external_triggered(); + void on_actionOpen_terminal_triggered(); + void on_actionCopy_name_triggered(); + void on_actionCopy_path_triggered(); + +}; + +#endif // PROJECTFILESYSTEMWIDGET_H diff --git a/test/project_fs/projectfilesystemwidget.ui b/test/project_fs/projectfilesystemwidget.ui new file mode 100644 index 0000000..c05af68 --- /dev/null +++ b/test/project_fs/projectfilesystemwidget.ui @@ -0,0 +1,183 @@ + + + ProjectFilesystemWidget + + + + 0 + 0 + 386 + 390 + + + + + + + + + :/icons/edit-find.png + + + true + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Expand tree + + + + + + + Collapse tree + + + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + Qt::ElideMiddle + + + QAbstractItemView::ScrollPerPixel + + + true + + + true + + + false + + + + Path + + + + + + + + Open as plain + + + + + Open external ... + + + + + Show external ... + + + + + Copy name + + + + + Copy path + + + + + + :/icons/utilities-terminal.png:/icons/utilities-terminal.png + + + Open terminal ... + + + + + + + + + buttonClear + clicked() + lineFilter + clear() + + + 246 + 37 + + + 212 + 35 + + + + + buttonExpand + clicked() + tree + expandAll() + + + 263 + 28 + + + 251 + 58 + + + + + buttonCollapse + clicked() + tree + collapseAll() + + + 307 + 27 + + + 297 + 71 + + + + + diff --git a/test/project_fs/projectfilesystemwidgetplugin.cpp b/test/project_fs/projectfilesystemwidgetplugin.cpp new file mode 100644 index 0000000..48f619c --- /dev/null +++ b/test/project_fs/projectfilesystemwidgetplugin.cpp @@ -0,0 +1,57 @@ +#include "projectfilesystemwidgetplugin.h" +#include "projectfilesystemwidget.h" +#include +#include +#include +#include + + +ProjectFSWidgetFactory::ProjectFSWidgetFactory() { + setDisplayName(QString::fromLatin1("Project filesystem")); + setId("project_filesystem"); +} + + +Core::NavigationView ProjectFSWidgetFactory::createWidget() { + Core::NavigationView view; + view.widget = new ProjectFilesystemWidget(); + QToolButton * btn = new QToolButton(); + btn->setIcon(Utils::Icons::RELOAD.icon()); + btn->setToolTip(tr("Reload tree")); + view.dockToolBarWidgets << btn; + connect(btn, SIGNAL(clicked()), view.widget, SLOT(projectsChanged())); + btn = new QToolButton(); + btn->setIcon(Utils::Icons::FILTER.icon()); + btn->setToolTip(tr("Setup filters ...")); + view.dockToolBarWidgets << btn; + connect(btn, SIGNAL(clicked()), view.widget, SLOT(filterClicked())); + return view; +} + + +void ProjectFSWidgetFactory::saveSettings(QSettings * settings, int position, QWidget * widget) { + //QMessageBox::information(0, "", QString::fromLatin1(widget->metaObject()->className())); + ProjectFilesystemWidget * w = qobject_cast(widget); + if (!w) return; + settings->beginGroup("ProjectFilesystem"); + settings->beginWriteArray("widget"); + settings->setArrayIndex(position); + settings->setValue("filters", w->currentFilters().toVariant()); + settings->setValue("search", w->currentSearch()); + settings->endArray(); + settings->endGroup(); + settings->sync(); +} + + +void ProjectFSWidgetFactory::restoreSettings(QSettings * settings, int position, QWidget * widget) { + ProjectFilesystemWidget * w = qobject_cast(widget); + if (!w) return; + settings->beginGroup("ProjectFilesystem"); + settings->beginReadArray("widget"); + settings->setArrayIndex(position); + w->setCurrentFilter(FilterDialog::Filter(settings->value("filters", FilterDialog::Filter().toVariant()))); + w->setCurrentSearch(settings->value("search", QString()).toString()); + settings->endArray(); + settings->endGroup(); +} diff --git a/test/project_fs/projectfilesystemwidgetplugin.h b/test/project_fs/projectfilesystemwidgetplugin.h new file mode 100644 index 0000000..ccd652d --- /dev/null +++ b/test/project_fs/projectfilesystemwidgetplugin.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + + +class ProjectFSWidgetFactory: public Core::INavigationWidgetFactory +{ +public: + ProjectFSWidgetFactory(); + ~ProjectFSWidgetFactory() {} + + Core::NavigationView createWidget(); + + void saveSettings(QSettings * settings, int position, QWidget * widget); + void restoreSettings(QSettings * settings, int position, QWidget * widget); + +}; diff --git a/test/qad/CMakeLists.txt b/test/qad/CMakeLists.txt new file mode 100644 index 0000000..351cfc2 --- /dev/null +++ b/test/qad/CMakeLists.txt @@ -0,0 +1,118 @@ +project(qad) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (NOT LIBPROJECT) + option(LIB "System install" 1) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif () +endif() + +if (NOT DEFINED ANDROID_PLATFORM) + find_package(OpenGL REQUIRED) +endif() +if (MINGW) + find_package(MinGW REQUIRED) +endif() + +if (NOT LIBPROJECT) + if (LIB) + if (WIN32) + set(CMAKE_INSTALL_PREFIX ${MINGW_DIR}) + else() + if(APPLE) + set(CMAKE_INSTALL_PREFIX /usr/local) + else() + if (DEFINED ANDROID_PLATFORM) + set(CMAKE_INSTALL_PREFIX ${ANDROID_SYSTEM_LIBRARY_PATH}/usr) + else() + set(CMAKE_INSTALL_PREFIX /usr) + endif() + endif() + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() +set(CMAKES "FindQAD.cmake" "QtWraps.cmake") +install(FILES ${CMAKES} DESTINATION ${CMAKE_ROOT}/Modules) + +macro(qad_install _TARGET _H_FILES _QM_FILES) + #message("qad_install ${_TARGET} ${_QM_FILES}") + if (LIBPROJECT) + sdk_install("qad" "${_TARGET}" "${_H_FILES}" "${_QM_FILES}") + else() + if (LIB) + if (WIN32) + if(NOT "x${_H_FILES}" STREQUAL "x") + #message("HFILES ${_H_FILES}") + install(FILES ${_H_FILES} DESTINATION ${MINGW_INCLUDE}/${_DIR}) + endif() + qt_install(TARGETS ${_TARGET} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${_TARGET} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${_TARGET} DESTINATION QtBin) + qt_install(FILES "${_QM_FILES}" DESTINATION QtLang) + else() + if(NOT _H_FILES STREQUAL "") + install(FILES ${_H_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/qad) + endif() + qt_install(TARGETS ${_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + else() + qt_install(TARGETS ${_TARGET} DESTINATION bin) + endif() + endif() +endmacro() + +macro(qad_plugin NAME _MODULES _LIBS) + set(PROJ_NAME qad_${NAME}_plugin) + include_directories("..") + #message(${_${NAME}_PLUGIN_LIBS}) + add_definitions(-DQT_PLUGIN) + add_definitions(-DQT_NO_DEBUG) + add_definitions(-DQT_SHARED) + add_definitions(-DQDESIGNER_EXPORT_WIDGETS) + find_qt(${QtVersions} Core Designer ${_MODULES}) + qt_sources(SRC) + qt_wrap(${SRC} CPPS out_CPP QMS out_QM) + qt_add_library(${PROJ_NAME} SHARED out_CPP) + qt_target_link_libraries(${PROJ_NAME} ${_LIBS} qad_${NAME} ${_${NAME}_PLUGIN_LIBS}) + qt_install(TARGETS ${PROJ_NAME} DESTINATION QtPlugins/designer) +endmacro() + +macro(qad_project NAME _MODULES _LIBS) + set(PROJ_NAME qad_${NAME}) + find_qt(${QtVersions} Core ${_MODULES}) + qt_sources(SRC) + qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS ${PROJ_NAME}_QM) + qt_add_library(${PROJ_NAME} SHARED out_CPP) + qt_target_link_libraries(${PROJ_NAME} ${_LIBS}) + qad_install(${PROJ_NAME} "${out_HDR}" ${PROJ_NAME}_QM) + set(${NAME}_UTILS 1) + if (LIBPROJECT) + set(${NAME}_UTILS ${UTILS}) + endif() + message(STATUS "Building ${PROJ_NAME}") + if (NOT DEFINED ANDROID_PLATFORM) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/plugin") + set(_${NAME}_PLUGIN_LIBS "${_LIBS}") + add_subdirectory(plugin) + endif() + endif() +endmacro() + +set(DIRS utils widgets application blockview graphic sql_table touch_widgets) +foreach(D ${DIRS}) + list(APPEND QT_MULTILIB_LIST qad_${D}) +endforeach(D) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +foreach(D ${DIRS}) + include_directories(${D}) + add_subdirectory(${D}) +endforeach(D) + diff --git a/test/qad/FindQAD.cmake b/test/qad/FindQAD.cmake new file mode 100644 index 0000000..0c8ba87 --- /dev/null +++ b/test/qad/FindQAD.cmake @@ -0,0 +1,63 @@ +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (POLICY CMP0011) + cmake_policy(SET CMP0011 NEW) +endif() +if(NOT LIBPROJECT) + include(QtWraps) +endif() +get_directory_property(hasParent PARENT_DIRECTORY) +set(_SEARCH_DIR) +if(WIN32) + find_package(MinGW REQUIRED) +endif() +if(APPLE) + include_directories(/usr/local/include) + link_directories(/usr/local/lib) +endif() +find_file(QAD_H_INCLUDE "qad_types.h" HINTS ${PIP_DIR}/include/qad ${MINGW_INCLUDE}/qad /usr/include/qad /usr/local/include/qad $ENV{SMSDK_DIR}/include/qad) +if (DEFINED ANDROID_PLATFORM) + set(QAD_INCLUDES ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/include/qad) +else() + get_filename_component(QAD_INCLUDES ${QAD_H_INCLUDE} PATH) +endif() +set(_SEARCH_DIR ${MINGW_LIB} /usr/lib /usr/local/lib $ENV{SMSDK_DIR}/lib) +set(_QAD_LIBS utils widgets application blockview graphic sql_table touch_widgets) +if(LIBPROJECT) + set(QAD_INCLUDES) +endif() +foreach(_Q ${_QAD_LIBS}) + string(TOUPPER ${_Q} _QU) + if(LIBPROJECT) + set(QAD_${_QU}_LIBRARY qad_${_Q}) + list(APPEND QAD_INCLUDES ${QAD_DIR}/${_Q}) + else() + foreach(_v ${_QT_VERSIONS_}) + find_library(QAD${_v}_${_QU}_LIBRARY qad_${_Q}${_v} ${_SEARCH_DIR}) + set(MULTILIB_${_Q}_SUFFIX_Qt${_v} ${_v}) + if(hasParent) + set(MULTILIB_${_Q}_SUFFIX_Qt${_v} ${_v} PARENT_SCOPE) + endif() + endforeach() + endif() + #message(STATUS "Library ${_Q} (${_QU}) -> ${QAD_${_QU}_LIBRARY} found in ${_SEARCH_DIR}") + list(APPEND QT_MULTILIB_LIST qad_${_Q}) +endforeach() +list(APPEND QT_MULTILIB_LIST qglview) +foreach(_v ${_QT_VERSIONS_}) + find_library(QGLVIEW${_v}_LIBRARY qglview${_v} ${_SEARCH_DIR}) + set(MULTILIB_qglview_SUFFIX_Qt${_v} ${_v}) + if(hasParent) + set(MULTILIB_qglview_SUFFIX_Qt${_v} ${_v} PARENT_SCOPE) + endif() +endforeach() +if(hasParent) + set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +endif() +if(NOT LIBPROJECT) + if (NOT _QAD_MSG) + set(_QAD_MSG 1 CACHE BOOL "msg_qad" FORCE) + message(STATUS "Found QAD") + endif() +endif() diff --git a/test/qad/QADConfig.cmake b/test/qad/QADConfig.cmake new file mode 100644 index 0000000..b80e857 --- /dev/null +++ b/test/qad/QADConfig.cmake @@ -0,0 +1 @@ +include(${QAD_DIR}/FindQAD.cmake) diff --git a/test/qad/QtWraps.cmake b/test/qad/QtWraps.cmake new file mode 100644 index 0000000..27566cd --- /dev/null +++ b/test/qad/QtWraps.cmake @@ -0,0 +1,519 @@ + + +set(_QT_VERSIONS_ 4 5) +foreach(_v ${_QT_VERSIONS_}) + if (NOT DEFINED TARGET_SUFFIX_Qt${_v}) + set(TARGET_SUFFIX_Qt${_v} "${_v}") + set(MULTILIB_SUFFIX_Qt${_v} "${_v}") + endif() + set(MOC_INC_Qt${_v}) +endforeach() +if (NOT DEFINED QT_MULTILIB_LIST) + set(QT_MULTILIB_LIST) +endif() +#set(TARGET_SUFFIX_Qt4 "") + + +# usage: find_qt([ ...] [Qt4] [Qt5]) +# find Qt4 or/and Qt5 packages +# if Qt4/5 found, set +# * Qt<4/5>_BIN var to Qt binary dir +# * Qt<4/5>_PLUGINS_DIR var to Qt plugins dir +macro(find_qt) + set(_QCOMP) + foreach(_v ${_QT_VERSIONS_}) + set(_NEED${_v} 0) + set(LOCAL_FOUND${_v} 0) + set(MOC_INC_Qt${_v}) + set(Qt${_v}_INCLUDES) + set(Qt${_v}_LIBRARIES) + set(Qt${_v}_BIN) + set(Qt${_v}_PLUGINS_DIR) + set(Qt${_v}_LANG_DIR) + endforeach() + set(_NEED_SOME 0) + set(_QUIET) + foreach(_i ${ARGN}) + set(_FOUND_NEED 0) + foreach(_v ${_QT_VERSIONS_}) + if ("x${_i}" STREQUAL "xQt${_v}") + set(_NEED${_v} 1) + set(_NEED_SOME 1) + set(_FOUND_NEED 1) + endif() + if ("x${_i}" STREQUAL "xQUIET") + set(_QUIET QUIET) + endif() + endforeach() + if (NOT _FOUND_NEED) + list(APPEND _QCOMP "${_i}") + endif() + endforeach() + if (NOT _NEED_SOME) + foreach(_v ${_QT_VERSIONS_}) + set(_NEED${_v} 1) + endforeach() + endif() + list(REMOVE_DUPLICATES _QCOMP) + if (_NEED4) + find_package(Qt4 ${_QUIET}) + if (Qt4_FOUND) + set(LOCAL_FOUND4 1) + get_filename_component(Qt4_BIN ${QT_QMAKE_EXECUTABLE} PATH) + set(Qt4_INCLUDES ${QT_INCLUDES}) + foreach(m ${_QCOMP}) + string(TOUPPER "${m}" _um) + #message("${QT_QT${_um}_FOUND}") + if (${QT_QT${_um}_FOUND}) + list(APPEND Qt4_LIBRARIES ${QT_QT${_um}_LIBRARY}) + endif() + endforeach() + set(Qt4_PLUGINS_DIR ${QT_PLUGINS_DIR}) + set(Qt4_LANG_DIR ${Qt4_BIN}/../translations) + endif() + endif() + if (_NEED5) + #set(_MSG 1) + #if (Qt5_FOUND) + # set(_MSG 0) + #endif() + find_package(Qt5 COMPONENTS LinguistTools UiPlugin Widgets ${_QCOMP} ${_QUIET}) + if (Qt5_FOUND) + set(LOCAL_FOUND5 1) + set(_Qt5Modules) + foreach(m ${_QCOMP}) + if (${Qt5${m}_FOUND}) + list(APPEND _Qt5Modules ${m}) + list(APPEND Qt5_INCLUDES ${Qt5${m}_INCLUDE_DIRS}) + list(APPEND Qt5_LIBRARIES ${Qt5${m}_LIBRARIES}) + endif() + endforeach() + #get_property(_up_dir TARGET Qt5::UiPlugin PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + #message("${_up_dir}") + #if (NOT "x${_up_dir}" STREQUAL "x") + # list(APPEND Qt5_INCLUDES ${_up_dir}) + #endif() + get_target_property(_Qt5_qmake Qt5::qmake LOCATION) + get_filename_component(Qt5_BIN ${_Qt5_qmake} PATH) + foreach(_p ${Qt5Gui_PLUGINS}) + get_target_property(_l ${_p} LOCATION) + get_filename_component(_l ${_l} DIRECTORY) + string(FIND "${_l}" "/" _ind REVERSE) + string(SUBSTRING "${_l}" 0 ${_ind} _l) + set(Qt5_PLUGINS_DIR ${_l}) + #message("Qt5: plugins dir: ${Qt5_PLUGINS_DIR}") + break() + endforeach() + string(FIND "${Qt5_BIN}" "/" _ind REVERSE) + string(SUBSTRING "${Qt5_BIN}" 0 ${_ind} _qt5_root) + if ("x${Qt5_PLUGINS_DIR}" STREQUAL "x") + set(Qt5_PLUGINS_DIR "${_qt5_root}/plugins") + endif() + if (APPLE) + list(APPEND MOC_INC_Qt5 "-F${_qt5_root}/lib") + endif() + set(Qt5_LANG_DIR ${Qt5_BIN}/../translations) + if (NOT _QT5_MSG) + message(STATUS "Found Qt5: ${_Qt5_qmake} (found version \"${Qt5_VERSION}\")") + set(_QT5_MSG 1 CACHE BOOL "msg_qt5" FORCE) + #message(STATUS "Found Qt5 modules: ${_Qt5Modules}") + endif() + endif() + endif() +# set() + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + foreach(_p ${Qt${_v}_INCLUDES}) + list(APPEND MOC_INC_Qt${_v} "-I${_p}") + #message("${_p}") + endforeach() + endif() + #message("${MOC_INC_Qt${_v}}") + endforeach() + +endmacro() + + +# usage: qt_sources( [NO_DEFAULT] [DIR ] [ ...]) +# collect all sources, optionally in directory and +# additional filters . By default filter files with regexps +# "*.h", "*.hpp", "*.c", "*.cpp", "*.ui", "*.qrc", "*.rc", "*.ts" and "lang/*.ts". +# Default filters disabled with option NO_DEFAULT +macro(qt_sources OUT) + set(${OUT}) + set(_DIR "") + set(_IS_DIR 0) + set(_NO_DEFAULT 0) + set(_DEFAULT) + set(_REGEXP) + set(_REGEXP_WD) + foreach(_i ${ARGN}) + if (_IS_DIR) + set(_DIR "${_i}") + set(_IS_DIR 0) + else() + if ("x${_i}" STREQUAL "xDIR") + set(_IS_DIR 1) + else() + if ("x${_i}" STREQUAL "xNO_DEFAULT") + set(_NO_DEFAULT 1) + else() + list(APPEND _REGEXP ${_i}) + endif() + endif() + endif() + endforeach() + if (NOT "x${_DIR}" STREQUAL "x") + #file(TO_CMAKE_PATH "${_DIR}//" _DIR) + string(REPLACE "\\" "/" _DIR "${_DIR}/") + string(REPLACE "//" "/" _DIR "${_DIR}") + endif() + foreach(_i ${_REGEXP}) + list(APPEND _REGEXP_WD "${_DIR}${_i}") + endforeach() + if (NOT _NO_DEFAULT) + set(_DEFAULT "${_DIR}*.h" "${_DIR}*.hpp" "${_DIR}*.c" "${_DIR}*.cpp" "${_DIR}*.ui" "${_DIR}*.qrc" "${_DIR}*.rc" "${_DIR}*.ts" "${_DIR}lang/*.ts") + endif() + #message("${_DEFAULT}, ${_REGEXP_WD}") + file(GLOB ${OUT} ${_DEFAULT} ${_REGEXP_WD}) +endmacro() + + +# usage: qt_wrap( ... [HDRS ] [CPPS ] [QMS ]) +# prepare sources for compile +# store headers to , all wrapped Qt4 files to _Qt4 and Qt5 files to _Qt5 +# version are automatic detected after find_qt() call +macro(qt_wrap) + set(HDR_VAR) + set(CPP_VAR) + set(QM_VAR) + set(_HDR_ARG 0) + set(_CPP_ARG 0) + set(_QM_ARG 0) + set(WAS_HDR_ARG 0) + set(WAS_CPP_ARG 0) + set(WAS_QM_ARG 0) + set(FILE_LIST) + foreach(_i ${ARGN}) + if (_HDR_ARG) + set(HDR_VAR ${_i}) + set(WAS_HDR_ARG 1) + set(_HDR_ARG 0) + else() + if (_CPP_ARG) + set(CPP_VAR ${_i}) + set(WAS_CPP_ARG 1) + set(_CPP_ARG 0) + else() + if (_QM_ARG) + set(QM_VAR ${_i}) + set(WAS_QM_ARG 1) + set(_QM_ARG 0) + else() + if ("x${_i}" STREQUAL "xHDRS") + set(_HDR_ARG 1) + else() + if ("x${_i}" STREQUAL "xCPPS") + set(_CPP_ARG 1) + else() + if ("x${_i}" STREQUAL "xQMS") + set(_QM_ARG 1) + else() + list(APPEND FILE_LIST "${_i}") + endif() + endif() + endif() + endif() + endif() + endif() + endforeach() + foreach(_v ${_QT_VERSIONS_}) + set(CPP${_v}_VAR) + set(QM${_v}_VAR) + if (LOCAL_FOUND${_v}) + if (WAS_CPP_ARG) + set(CPP${_v}_VAR ${CPP_VAR}_Qt${_v}) + endif() + if (WAS_QM_ARG) + set(QM${_v}_VAR ${QM_VAR}_Qt${_v}) + endif() + endif() + endforeach() + #message("found 4: ${LOCAL_FOUND4}, 5: ${LOCAL_FOUND5}") + #message("has HRDS: ${WAS_HDR_ARG}: ${HDR_VAR}") + #message("has CPPS4: ${WAS_CPP_ARG}: ${CPP4_VAR}") + #message("has CPPS5: ${WAS_CPP_ARG}: ${CPP5_VAR}") + #message("files: ${FILE_LIST}") + set(H_LIST) + set(CPP_LIST) + set(UI_LIST) + set(RES_LIST) + set(RC_LIST) + set(TS_LIST) + foreach(_i ${FILE_LIST}) + get_filename_component(_EXT "${_i}" EXT) + #message("${_EXT}") + if (NOT ("x${_EXT}" STREQUAL "x")) + if (("x${_EXT}" STREQUAL "x.h") OR ("x${_EXT}" STREQUAL "x.hpp")) + list(APPEND H_LIST "${_i}") + #message("header") + endif() + if (("x${_EXT}" STREQUAL "x.c") OR ("x${_EXT}" STREQUAL "x.cpp") OR ("x${_EXT}" STREQUAL "x.cxx")) + list(APPEND CPP_LIST "${_i}") + #message("source") + endif() + if ("x${_EXT}" STREQUAL "x.ui") + list(APPEND UI_LIST "${_i}") + endif() + if ("x${_EXT}" STREQUAL "x.qrc") + list(APPEND RES_LIST "${_i}") + endif() + if ("x${_EXT}" STREQUAL "x.rc") + list(APPEND RC_LIST "${_i}") + endif() + if ("x${_EXT}" STREQUAL "x.ts") + list(APPEND TS_LIST "${_i}") + endif() + endif() + endforeach() + if (WAS_HDR_ARG) + set(${HDR_VAR}) + list(APPEND ${HDR_VAR} ${H_LIST}) + endif() + set(SRC_CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v} AND WAS_CPP_ARG) + set(CMAKE_CURRENT_BINARY_DIR ${SRC_CMAKE_CURRENT_BINARY_DIR}/qt${_v}) + set(${CPP${_v}_VAR}) + if (WAS_QM_ARG) + set(${QM${_v}_VAR}) + endif() + set(MOCS${_v}) + set(CUIS${_v}) + set(CRES${_v}) + if (${_v} EQUAL 4) + qt4_wrap_cpp(MOCS${_v} ${H_LIST} OPTIONS -nw ${MOC_INC_Qt4}) + qt4_wrap_ui(CUIS${_v} ${UI_LIST}) + qt4_add_resources(CRES${_v} ${RES_LIST}) + if (WAS_QM_ARG) + set(${QM${_v}_VAR} "") + qt4_add_translation(${QM${_v}_VAR} ${TS_LIST}) + endif() + else() + if (${_v} EQUAL 5) + qt5_wrap_cpp(MOCS${_v} ${H_LIST} OPTIONS -nw ${MOC_INC_Qt5}) + qt5_wrap_ui(CUIS${_v} ${UI_LIST}) + qt5_add_resources(CRES${_v} ${RES_LIST}) + if (WAS_QM_ARG) + set(${QM${_v}_VAR} "") + qt5_add_translation(${QM${_v}_VAR} ${TS_LIST}) + endif() + else() + endif() + endif() + #message("${${QM${_v}_VAR}}") + list(APPEND ${CPP${_v}_VAR} ${CPP_LIST}) + list(APPEND ${CPP${_v}_VAR} ${RC_LIST}) + list(APPEND ${CPP${_v}_VAR} ${MOCS${_v}}) + list(APPEND ${CPP${_v}_VAR} ${CUIS${_v}}) + list(APPEND ${CPP${_v}_VAR} ${CRES${_v}}) + if (WAS_QM_ARG) + list(APPEND ${CPP${_v}_VAR} ${${QM${_v}_VAR}}) + endif() + #message("${${QM${_v}_VAR}}") + endif() + endforeach() + set(CMAKE_CURRENT_BINARY_DIR ${SRC_CMAKE_CURRENT_BINARY_DIR}) +endmacro() + + +macro(_qt_split_add_args _P _A) + set(${_P}) + set(${_A}) + foreach(_i ${ARGN}) + if (NOT ("x${${_i}_Qt${_v}}" STREQUAL "x")) + list(APPEND ${_A} ${${_i}_Qt${_v}}) + else() + if (("x${_i}" STREQUAL "xWIN32") OR ("x${_i}" STREQUAL "xMACOSX_BUNDLE") OR ("x${_i}" STREQUAL "xSTATIC") OR ("x${_i}" STREQUAL "xSHARED") OR ("x${_i}" STREQUAL "xMODULE") OR ("x${_i}" STREQUAL "xEXCLUDE_FROM_ALL")) + list(APPEND ${_P} ${_i}) + else() + if ("x${${_i}}" STREQUAL "x") + list(APPEND ${_A} ${_i}) + else() + list(APPEND ${_A} ${${_i}}) + endif() + endif() + endif() + endforeach() +endmacro() + + +# usage: qt_add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...]) +# add executables for founded Qt versions, target names is +# all needed Qt includes automatic added to corresponding targets +# cant be CPPS variable passed to qt_wrap() or some list or filename +macro(qt_add_executable _NAME) + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + _qt_split_add_args(_PREF _ARGS ${ARGN}) + #message("${_PREF}") + #message("${_ARGS}") + set(_TARGET ${_NAME}${TARGET_SUFFIX_Qt${_v}}) + add_executable(${_TARGET} ${_PREF} ${_ARGS}) + set(_${_NAME}_is_qt 1) + target_include_directories(${_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/qt${_v} ${Qt${_v}_INCLUDES}) +### add_definitions(${Qt5Widgets_DEFINITIONS}) +### target_compile_definitions(${_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/qt${_v} ${Qt${_v}_INCLUDES}) + #message("add exe: \"${_TARGET}\", \"${_PREF}\"") + endif() + endforeach() +endmacro() + + +# usage: qt_add_library( [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 [source2 ...]) +# add library for founded Qt versions, target names is +# all needed Qt includes automatic added to corresponding targets +# cant be CPPS variable passed to qt_wrap() or some list or filename +macro(qt_add_library _NAME) + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + _qt_split_add_args(_PREF _ARGS ${ARGN}) + #message("${_PREF}") + #message("${_ARGS}") + set(_TARGET ${_NAME}${TARGET_SUFFIX_Qt${_v}}) + add_library(${_TARGET} ${_PREF} ${_ARGS}) + set(_${_NAME}_is_qt 1) + target_include_directories(${_TARGET} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/qt${_v} ${Qt${_v}_INCLUDES}) + #message("add lib: \"${_TARGET}\"") + endif() + endforeach() +endmacro() + + +macro(_qt_multitarget_suffix_ _OUT _IN _v) + if (NOT "x${MULTILIB_${_IN}_SUFFIX_Qt${_v}}" STREQUAL "x") + set(${_OUT} "${MULTILIB_${_IN}_SUFFIX_Qt${_v}}") + else() + set(${_OUT} "${MULTILIB_SUFFIX_Qt${_v}}") + endif() +endmacro() + + +# usage: qt_target_link_libraries( ... ...) +# link targets for founded Qt versions, target names is +# all needed Qt libraries automatic added to corresponding targets +# if QT_MULTILIB_LIST contains some linking library, to it name automatic +# add or individual libname<_SUFFIX_Qt?> +macro(qt_target_link_libraries _NAME) + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + set(_TARGET ${_NAME}${TARGET_SUFFIX_Qt${_v}}) + set(_ARGS) + foreach(_i ${ARGN}) + set(_ind -1) + list(FIND QT_MULTILIB_LIST "${_i}" _ind) + if (_ind GREATER -1) + _qt_multitarget_suffix_(_TS ${_i} ${_v}) + list(APPEND _ARGS ${_i}${_TS}) + else() + list(APPEND _ARGS ${_i}) + endif() + endforeach() + target_link_libraries(${_TARGET} ${Qt${_v}_LIBRARIES} ${_ARGS}) + #message("link ${_TARGET}: ${Qt${_v}_LIBRARIES} ${_ARGS}") + endif() + endforeach() +endmacro() + + +# usage: qt_install(...), syntax similar to original "install()" +# install targets for founded Qt versions +# if DESTINATION set to "QtBin" files will be installed +# to corresponding Qt binary directory +# if DESTINATION set to "QtLang" files will be installed +# to corresponding Qt translation directory +# if DESTINATION starts with "QtPlugins" files will be installed +# to corresponding Qt plugins directory +macro(qt_install) + set(_prev_inst) + #message("command: ${ARGN}") + foreach(_v ${_QT_VERSIONS_}) + if (LOCAL_FOUND${_v}) + set(_INST_LIST) + set(_IS_TARGET 0) + set(_IS_FILES 0) + set(_IS_DEST 0) + set(_IS_LANG 0) + set(_INVALID 0) + foreach(_i ${ARGN}) + if ("x${_i}" STREQUAL "xQtLang") + set(_IS_LANG 1) + endif() + endforeach() + foreach(_i ${ARGN}) + if (_IS_TARGET) + set(_IS_TARGET 0) + if (_${_i}_is_qt) + set(_INST_LIST ${_INST_LIST} ${_i}${TARGET_SUFFIX_Qt${_v}}) + else() + set(_INST_LIST ${_INST_LIST} ${_i}) + endif() + else() + if (_IS_FILES) + set(_IS_FILES 0) + if (_IS_LANG) + if (("x${${_i}_Qt${_v}}" STREQUAL "x") OR ("x${_i}" STREQUAL "xDESTINATION")) + #return() + set(_INVALID 1) + else() + set(_INST_LIST ${_INST_LIST} ${${_i}_Qt${_v}}) + endif() + else() + set(_INST_LIST ${_INST_LIST} ${_i}) + endif() + else() + if (_IS_DEST) + set(_IS_DEST 0) + if ("x${_i}" STREQUAL "xQtBin") + set(_INST_LIST ${_INST_LIST} "${Qt${_v}_BIN}") + else() + if ("x${_i}" STREQUAL "xQtLang") + set(_INST_LIST ${_INST_LIST} "${Qt${_v}_LANG_DIR}") + else() + set(_ind -1) + string(FIND "${_i}" "QtPlugins" _ind) + if (_ind GREATER -1) + string(REPLACE "QtPlugins" "${Qt${_v}_PLUGINS_DIR}" _o "${_i}") + set(_INST_LIST ${_INST_LIST} "${_o}") + else() + set(_INST_LIST ${_INST_LIST} ${_i}) + endif() + endif() + endif() + else() + set(_INST_LIST ${_INST_LIST} ${_i}) + if ("x${_i}" STREQUAL "xTARGETS") + set(_IS_TARGET 1) + else() + if ("x${_i}" STREQUAL "xFILES") + set(_IS_FILES 1) + else() + if ("x${_i}" STREQUAL "xDESTINATION") + set(_IS_DEST 1) + endif() + endif() + endif() + endif() + endif() + endif() + endforeach() + if (NOT _INVALID) + if (NOT ("x${_prev_inst}" STREQUAL "x${_INST_LIST}")) + #message("install: ${_INST_LIST}") + install(${_INST_LIST}) + set(_prev_inst "${_INST_LIST}") + endif() + endif() + endif() + endforeach() +endmacro() diff --git a/test/qad/application/CMakeLists.txt b/test/qad/application/CMakeLists.txt new file mode 100644 index 0000000..1c3f057 --- /dev/null +++ b/test/qad/application/CMakeLists.txt @@ -0,0 +1 @@ +qad_project(application "Gui;Widgets" "qad_widgets") diff --git a/test/qad/application/edockwidget.cpp b/test/qad/application/edockwidget.cpp new file mode 100644 index 0000000..cd99ee2 --- /dev/null +++ b/test/qad/application/edockwidget.cpp @@ -0,0 +1,96 @@ +#include "edockwidget.h" +#include "qad_types.h" +#include +#include + + +void EDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features) { + btn_dock->setVisible(features.testFlag(DockWidgetFloatable)); + btn_hide->setVisible(features.testFlag(DockWidgetClosable)); + QDockWidget::setFeatures(features); +} + + +void EDockWidget::setWindowTitle(const QString & title) { + lbl_title->setText(title); + QDockWidget::setWindowTitle(title); +} + + +void EDockWidget::setWindowIcon(const QIcon & icon) { +//#ifndef Q_OS_MACOS + lbl_icon->setPixmap(icon.pixmap(QSize(256, 256))); + QDockWidget::setWindowIcon(icon); + if (!icon.isNull()) { + lbl_icon->setScaledContents(true); + lbl_icon->setFixedSize(preferredIconSize(1.5, this)); + } + //#endif +} + + +bool EDockWidget::event(QEvent * e) { + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) { + updateStyle(); + } + return QDockWidget::event(e); +} + + +void EDockWidget::init() { + header = new QFrame(); + header->setFrameShape(QFrame::StyledPanel); + QBoxLayout * lay = new QBoxLayout(features().testFlag(QDockWidget::DockWidgetVerticalTitleBar) ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight); + lay->setContentsMargins(2, 2, 2, 2); + lay->setSpacing(2); + lbl_icon = new QLabel(); +//#ifndef Q_OS_MACOS + QIcon wi = windowIcon(); + if (!wi.isNull()) { + lbl_icon->setPixmap(wi.pixmap(QSize(256,256))); +/*#if QT_VERSION >= 0x500000 + if (lbl_icon->pixmap()) + const_cast(lbl_icon->pixmap())->setDevicePixelRatio(1.); +#endif*/ + //qDebug() << windowTitle() << wi.pixmap(QSize(256,256)).size(); + lbl_icon->setScaledContents(true); + } + lbl_icon->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +//#endif + lbl_title = new QLabel(windowTitle()); + lbl_title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + btn_dock = new QToolButton(); + //btn_dock->setIconSize(QSize(16, 16)); + btn_dock->setAutoRaise(true); + btn_dock->setFocusPolicy(Qt::NoFocus); + btn_dock->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + btn_hide = new QToolButton(); + //btn_hide->setIconSize(QSize(16, 16)); + btn_hide->setAutoRaise(true); + btn_hide->setFocusPolicy(Qt::NoFocus); + btn_hide->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + connect(btn_dock, SIGNAL(clicked(bool)), this, SLOT(dockClicked())); + connect(btn_hide, SIGNAL(clicked(bool)), this, SLOT(hide())); + lay->addWidget(lbl_icon); + lay->addWidget(lbl_title); + lay->addWidget(btn_dock); + lay->addWidget(btn_hide); + header->setLayout(lay); + updateStyle(); + setTitleBarWidget(header); +} + + +void EDockWidget::updateStyle() { + QSize icon_size = preferredIconSize(0.75, this); + int bm = 2 * style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin, 0, this); + QSize btn_size = icon_size; + btn_size += QSize(bm, bm); + btn_dock->setIcon(style()->standardIcon(QStyle::SP_TitleBarNormalButton)); + btn_dock->setIconSize(icon_size); + btn_dock->setFixedSize(btn_size); + btn_hide->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + btn_hide->setIconSize(icon_size); + btn_hide->setFixedSize(btn_size); + lbl_icon->setFixedSize(preferredIconSize(1.5, this)); +} diff --git a/test/qad/application/edockwidget.h b/test/qad/application/edockwidget.h new file mode 100644 index 0000000..e5a3a15 --- /dev/null +++ b/test/qad/application/edockwidget.h @@ -0,0 +1,39 @@ +#ifndef EDOCKWIDGET_H +#define EDOCKWIDGET_H + +#include +#include +#include +#include +#include +#include +#include + + +class EDockWidget: public QDockWidget +{ + Q_OBJECT +public: + explicit EDockWidget(const QString & title, QWidget * parent = 0, Qt::WindowFlags flags = 0): QDockWidget(title, parent, flags) {init();} + explicit EDockWidget(QWidget * parent = 0, Qt::WindowFlags flags = 0): QDockWidget(parent, flags) {init();} + ~EDockWidget() {delete btn_hide; delete btn_dock; delete lbl_title; delete lbl_icon; delete header;} + + void setFeatures(QDockWidget::DockWidgetFeatures features); + void setWindowTitle(const QString & title); + void setWindowIcon(const QIcon & icon); + +private: + bool event(QEvent * e); + void init(); + void updateStyle(); + + QFrame * header; + QLabel * lbl_title, * lbl_icon; + QToolButton * btn_hide, * btn_dock; + +private slots: + void dockClicked() {setFloating(!isFloating());} + +}; + +#endif // EDOCKWIDGET_H diff --git a/test/qad/application/emainwindow.cpp b/test/qad/application/emainwindow.cpp new file mode 100644 index 0000000..3cd054b --- /dev/null +++ b/test/qad/application/emainwindow.cpp @@ -0,0 +1,423 @@ +#include "emainwindow.h" +#include +#include +#include +#include + + +EMainWindow::EMainWindow(QWidget * parent): QMainWindow(parent), action_show_all_tools(this), action_hide_all_tools(this), +action_show_all_docks(this), action_hide_all_docks(this), first_show(true) { + tid = 0; + menu_recent = 0; + action_clear_recent = new QAction(QIcon(":/icons/edit-clear.png"), tr("Clear recent list"), this); + connect(action_clear_recent, SIGNAL(triggered()), this, SLOT(clearRecent())); + qRegisterMetaType("Qt::DockWidgetArea"); + action_show_all_tools.setText(trUtf8("Show all")); + action_show_all_docks.setText(trUtf8("Show all")); + action_hide_all_tools.setText(trUtf8("Hide all")); + action_hide_all_docks.setText(trUtf8("Hide all")); + action_show_all_tools.setIcon(QIcon(":/icons/layer-visible-on.png")); + action_show_all_docks.setIcon(QIcon(":/icons/layer-visible-on.png")); + action_hide_all_tools.setIcon(QIcon(":/icons/layer-visible-off.png")); + action_hide_all_docks.setIcon(QIcon(":/icons/layer-visible-off.png")); + max_recent = 8; + setChanged(false); + initMenus(); + initSession(); + tid = startTimer(200); // bad timer, too much overload +} + + +EMainWindow::~EMainWindow() { + if (tid > 0) killTimer(tid); + tid = 0; + saveSession(); +} + + +void EMainWindow::setRecentFiles(const QStringList & rl) { + clearRecent(); + for (int i = rl.size() - 1; i >= 0; --i) + addToRecent(rl[i]); +} + + +QStringList EMainWindow::recentFiles() const { + QStringList ret; + foreach (QAction * a, actions_recent) + ret << a->data().toString(); + return ret; +} + + +void EMainWindow::setRecentMenu(QMenu * m) { + menu_recent = m; + prepareRecent(); +} + + +void EMainWindow::showEvent(QShowEvent * e) { + QWidget::showEvent(e); + initMenus(); + if (!first_show) return; + first_show = false; + QList docks(findChildren()); + foreach (QDockWidget * d, docks) { + connect(d, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(changedDock())); + connect(d, SIGNAL(topLevelChanged(bool)), this, SLOT(changedDock())); + } + changedDock(); +} + + +void EMainWindow::closeEvent(QCloseEvent * e) { + if (!checkSave()) e->ignore(); +} + + +bool EMainWindow::eventFilter(QObject * o, QEvent * e) { + //qDebug() << o << e; + if (tbars.contains((QTabBar*)o) && e->type() == QEvent::MouseButtonDblClick) { + int tab = ((QTabBar*)o)->tabAt(((QMouseEvent * )e)->pos()); + if (tab >= 0) { + QDockWidget * dock = (QDockWidget * )(((QTabBar*)o)->tabData(tab).toULongLong()); + if (dock != 0) { + dock->setFloating(true); + } + } + } + if (e->type() == QEvent::Show || e->type() == QEvent::Hide || e->type() == QEvent::ChildAdded || e->type() == QEvent::ChildRemoved || e->type() == QEvent::MouseButtonPress) { + if (tbars.contains((QTabBar*)o) || tdocks.contains((QDockWidget*)o)) { + if (e->type() == QEvent::MouseButtonPress) { + if (((QMouseEvent*)e)->button() == Qt::RightButton) { + bool popup = tbars.contains((QTabBar*)o); + if (tdocks.contains((QDockWidget*)o)) + popup = popup || ((QDockWidget*)o)->titleBarWidget()->geometry().contains(((QMouseEvent*)e)->pos()); + if (popup) { + createPopupMenu()->popup(((QMouseEvent*)e)->globalPos()); + return true; + } + } + } + if (e->type() == QEvent::Show || e->type() == QEvent::Hide /*|| e->type() == QEvent::ChildAdded || e->type() == QEvent::ChildRemoved*/) { + //qDebug() << "filter"; + //QMetaObject::invokeMethod(this, "changedDock", Qt::QueuedConnection); + changedDock(); + } + } + } + return QMainWindow::eventFilter(o, e); +} + + +void EMainWindow::timerEvent(QTimerEvent * e) { + if (e->timerId() == tid) { + changedDock(); + return; + } + QMainWindow::timerEvent(e); +} + + +QMenu * EMainWindow::createPopupMenu() { + QMenu * menuPopup = new QMenu; + QWidgetAction * wa; + QLabel * lbl; + QAction * a; + QFont f; + f.setBold(true); + // Toolbars + QList tools = findChildren(); + if (!tools.isEmpty()) { + wa = new QWidgetAction(menuPopup); + lbl = new QLabel(); + lbl->setFrameShape(QFrame::StyledPanel); + lbl->setFrameShadow(QFrame::Sunken); + lbl->setText(trUtf8("Toolbars")); + lbl->setFont(f); + lbl->setAlignment(Qt::AlignCenter); + wa->setDefaultWidget(lbl); + menuPopup->addAction(wa); + foreach (QToolBar * i, tools) { + if (i->property("ribbon").toBool()) continue; + a = new QAction(i->windowTitle(), menuPopup); + a->setCheckable(true); + a->setChecked(!i->isHidden()); + a->setIcon(i->windowIcon()); + connect(a, SIGNAL(toggled(bool)), i, SLOT(setVisible(bool))); + menuPopup->addAction(a); + } + menuPopup->addSeparator(); + menuPopup->addAction(&action_show_all_tools); + menuPopup->addAction(&action_hide_all_tools); + } + // Docks + QList docks = findChildren(); + if (!docks.isEmpty()) { + wa = new QWidgetAction(menuPopup); + lbl = new QLabel(); + lbl->setFrameShape(QFrame::StyledPanel); + lbl->setFrameShadow(QFrame::Sunken); + lbl->setText(trUtf8("Docks")); + lbl->setFont(f); + lbl->setAlignment(Qt::AlignCenter); + wa->setDefaultWidget(lbl); + menuPopup->addAction(wa); + foreach (QDockWidget * i, docks) { + if (i->property("ribbon").toBool()) continue; + a = new QAction(i->windowTitle(), menuPopup); + a->setCheckable(true); + a->setChecked(!i->isHidden()); + a->setIcon(i->windowIcon()); + connect(a, SIGNAL(toggled(bool)), i, SLOT(setVisible(bool))); + menuPopup->addAction(a); + } + menuPopup->addSeparator(); + menuPopup->addAction(&action_show_all_docks); + menuPopup->addAction(&action_hide_all_docks); + } + return menuPopup; +} + + +void EMainWindow::addToRecent(const QString & path) { + if (path.isEmpty()) return; + QFileInfo fi(path); + QString fp = fi.absoluteFilePath(); + bool insert = true; + for (int i = 0; i < actions_recent.size(); ++i) + if (actions_recent[i]->data().toString() == fp) { + actions_recent.push_front(actions_recent.takeAt(i)); + insert = false; + prepareRecent(); + break; + } + if (!insert) return; + QAction * a = new QAction(this); + a->setData(fp); + connect(a, SIGNAL(triggered()), this, SLOT(recentTriggered())); + actions_recent.push_front(a); + while (actions_recent.size() > max_recent) + delete actions_recent.takeLast(); + prepareRecent(); +} + + +void EMainWindow::prepareRecent() { + for (int i = 0; i < actions_recent.size(); ++i) { + QAction * a = actions_recent[i]; + a->setText(QString("&%1 - %2").arg(i + 1).arg(a->data().toString())); + } + if (!menu_recent) return; + menu_recent->clear(); + menu_recent->addActions(actions_recent); + menu_recent->addSeparator(); + menu_recent->addAction(action_clear_recent); +} + + +void EMainWindow::initMenus() { + action_show_all_tools.disconnect(); + action_hide_all_tools.disconnect(); + action_show_all_docks.disconnect(); + action_hide_all_docks.disconnect(); + + QList tools = findChildren(); + foreach (QToolBar * i, tools) { + if (i->property("ribbon").toBool()) continue; + i->toggleViewAction()->setIcon(i->windowIcon()); + connect(&action_show_all_tools, SIGNAL(triggered(bool)), i, SLOT(show())); + connect(&action_hide_all_tools, SIGNAL(triggered(bool)), i, SLOT(hide())); + } + + QList docks = findChildren(); + foreach (QDockWidget * i, docks) { + if (i->property("ribbon").toBool()) continue; + i->toggleViewAction()->setIcon(i->windowIcon()); + connect(&action_show_all_docks, SIGNAL(triggered(bool)), i, SLOT(show())); + connect(&action_hide_all_docks, SIGNAL(triggered(bool)), i, SLOT(hide())); + } + + QList actions = findChildren(); + foreach (QAction * i, actions) + i->setIconVisibleInMenu(true); + addActions(actions); +} + + +void EMainWindow::initSession() { + connect(&session, SIGNAL(loading(QPIConfig & )), this, SLOT(sessionLoading(QPIConfig & ))); + connect(&session, SIGNAL(saving(QPIConfig & )), this, SLOT(sessionSaving(QPIConfig & ))); +} + + +void EMainWindow::saveSession() { + session.save(); +} + + +void EMainWindow::loadSession() { + session.load(); +} + + +bool EMainWindow::checkSave() { + if (!isWindowModified()) return true; + return saveFile(true); +} + + +void EMainWindow::changedDock() { + if (isHidden()) return; + QList tabs(findChildren()); + QList docks = findChildren(); +// QSet docks_tabs; + QDockWidget * dock; +// qDebug() << "### change"; + foreach (QTabBar * t, tabs) { + if (!t->objectName().isEmpty() || t->isHidden()) continue; + if (!tbars.contains(t)) { + tbars << t; + connect(t, SIGNAL(tabCloseRequested(int)), this, SLOT(closeDock(int))); + t->installEventFilter(this); +#ifndef Q_OS_MACOS + t->setIconSize(dockTabsIconSize()); +#endif + t->setTabsClosable(true); + } +// qDebug() << "tab" << t << t->count(); + for (int i = 0; i < t->count(); ++i) { + dock = (QDockWidget * )t->tabData(i).toULongLong(); + //qDebug() << i << t->tabData(i); + if (!docks.contains(dock)) continue; +#ifndef Q_OS_MACOS + t->setIconSize(dockTabsIconSize()); +#endif + t->setTabIcon(i, dock->windowIcon()); +// docks_tabs << dock; + } + } + + foreach (QDockWidget * d, docks) { + if (d->titleBarWidget() == 0) continue; + QWidget * ctb = d->titleBarWidget(); + if (!d->property("__titleWidget").isValid()) { + d->setProperty("__titleWidget", qulonglong(ctb)); + QWidget * ntb = new QWidget(); + int m = style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin); + ntb->setLayout(new QBoxLayout(QBoxLayout::BottomToTop)); + ntb->layout()->setContentsMargins(m, m, 0, 0); + d->setProperty("__titleEmptyWidget", qulonglong(ntb)); + } + if (!tdocks.contains(d)) { + tdocks << d; +// qDebug() << "connect" << d; +// connect(d, SIGNAL(destroyed(QObject*)), this, SLOT(changedDockClose(QObject*)), Qt::UniqueConnection); + d->installEventFilter(this); + } + //d->titleBarWidget()->setHidden(docks_tabs.contains(d)); + if (tabifiedDockWidgets(d).isEmpty()) { + if (d->titleBarWidget() != (QWidget * )(d->property("__titleWidget").toULongLong())) + d->setTitleBarWidget((QWidget * )(d->property("__titleWidget").toULongLong())); + } else { + if (d->titleBarWidget() != (QWidget * )(d->property("__titleEmptyWidget").toULongLong())) { + d->setTitleBarWidget((QWidget * )(d->property("__titleEmptyWidget").toULongLong())); + d->layout()->setContentsMargins(0, 20, 0, 0); + } + } + } +} + + +//void EMainWindow::changedDockClose(QObject * dock) { +// qDebug() << "changedDockClose" << dock; +// if (!dock) return; +// foreach (QTabBar * t, tbars) { +// for (int i = 0; i < t->count(); ++i) +// if (t->tabData(i).toULongLong() == (qulonglong)dock) { +// t->removeTab(i); +// break; +// } +// } +//} + + +void EMainWindow::closeDock(int index) { + QDockWidget * dock = (QDockWidget * )((QTabBar*)sender())->tabData(index).toULongLong(); + if (dock == 0) return; + dock->close(); +} + + +void EMainWindow::recentTriggered() { + QAction * a = qobject_cast(sender()); + if (!a) return; + QString path = a->data().toString(); + if (path.isEmpty()) return; + if (!checkSave()) return; + if (load(path)) + addToRecent(path); +} + + +void EMainWindow::setMaxRecentItems(int mr) { + max_recent = qMax(0, mr); + for (int i = actions_recent.size() - 1; i >= mr; --i) + delete actions_recent.takeLast(); +} + + +void EMainWindow::newFile() { + if (!checkSave()) return; + reset(true); + setWindowModified(false); +} + + +void EMainWindow::openFile() { + if (!checkSave()) return; + QString ret = QFileDialog::getOpenFileName(this, trUtf8("Select file to open"), file_name, loadFilter()); + if (ret.isEmpty()) return; + if (load(ret)) + addToRecent(ret); +} + + +void EMainWindow::openFiles() { + if (!checkSave()) return; + QStringList ret = QFileDialog::getOpenFileNames(this, trUtf8("Select files to open"), file_name, loadFilter()); + foreach (QString s, ret) { + if (load(s)) + addToRecent(s); + } +} + + +bool EMainWindow::saveFile(bool ask) { + if (ask) { + int ret = QMessageBox::question(this, windowTitle(), trUtf8("Save changes%1?").arg(!file_name.isEmpty() ? (trUtf8(" in") + " \"" + file_name + "\"") : ""), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); + if (ret == QMessageBox::Cancel) return false; + if (ret == QMessageBox::Save) return saveFile(); + return true; + } + if (file_name.isEmpty()) return saveAsFile(); + if (save(file_name)) + addToRecent(file_name); + return true; +} + + +bool EMainWindow::saveAsFile() { + QString ret = QFileDialog::getSaveFileName(this, trUtf8("Select file to save"), file_name, saveFilter()); + if (ret.isEmpty()) return false; + if (save(ret)) + addToRecent(ret); + return true; +} + + +void EMainWindow::clearRecent() { + qDeleteAll(actions_recent); + actions_recent.clear(); + prepareRecent(); +} diff --git a/test/qad/application/emainwindow.h b/test/qad/application/emainwindow.h new file mode 100644 index 0000000..ef104c0 --- /dev/null +++ b/test/qad/application/emainwindow.h @@ -0,0 +1,127 @@ +#ifndef EMAINWINDOW_H +#define EMAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "session_manager.h" +#include "ribbon.h" + +class UAction: public QAction { + Q_OBJECT +public: + UAction(int ind,const QString & text, QObject * parent): QAction(text, parent) { + index = ind; + connect(this, SIGNAL(triggered()), this, SLOT(triggered())); + connect(this, SIGNAL(toggled(bool)), this, SLOT(toggled(bool))); + connect(this, SIGNAL(hovered()), this, SLOT(hovered())); + } + UAction(int ind, const QIcon & icon, const QString & text, QObject * parent): QAction(icon, text, parent) { + index = ind; + connect(this, SIGNAL(triggered()), this, SLOT(triggered())); + connect(this, SIGNAL(toggled(bool)), this, SLOT(toggled(bool))); + connect(this, SIGNAL(hovered()), this, SLOT(hovered())); + } +public slots: + void show() {setVisible(true);} + void hide() {setVisible(false);} + void setCheckedTrue() {setChecked(true);} + void setCheckedFalse() {setChecked(false);} +private: + int index; +private slots: + void triggered() {emit itriggered(this, index);} + void toggled(bool t) {emit itoggled(t, this, index);} + void hovered() {emit ihovered(this);} +signals: + void itriggered(QAction *, int); + void itoggled(bool, QAction *, int); + void ihovered(QAction * action); +}; + + +class EMainWindow: public QMainWindow +{ + Q_OBJECT + Q_PROPERTY(int maxRecentItems READ maxRecentItems WRITE setMaxRecentItems) +public: + EMainWindow(QWidget * parent = 0); + ~EMainWindow(); + + virtual void reset(bool full = false) {} + virtual bool load(const QString & path) {return true;} + virtual bool save(const QString & path) {return true;} + + void addSeparator() {} + void setRecentFiles(const QStringList & rl); + QStringList recentFiles() const; + void setRecentMenu(QMenu * m); + + int maxRecentItems() const {return max_recent;} + +protected: + // Qt`s overloaded + void showEvent(QShowEvent * ); + void closeEvent(QCloseEvent * ); + bool eventFilter(QObject * o, QEvent * e); + void timerEvent(QTimerEvent * e); + QMenu * createPopupMenu(); + void addToRecent(const QString & path); + void prepareRecent(); + + void init(const QString & config) {session.setFile(config); initMenus(); initSession(); loadSession();} // unusable + void saveSession(); + void loadSession(); + virtual void savingSession(QPIConfig & conf) {} + virtual void loadingSession(QPIConfig & conf) {} + virtual QSize dockTabsIconSize() const {return iconSize();} + virtual QString loadFilter() {return "All files(*)";} + virtual QString saveFilter() {return "All files(*)";} + + bool checkSave(); + void setChanged(bool yes = true) {isChanged = yes; setWindowModified(yes);} + + void initMenus(); + void initSession(); + + QAction action_show_all_tools, action_hide_all_tools, action_show_all_docks, action_hide_all_docks; + QString file_name; + QList tbars; + QList tdocks; + QList actions_recent; + QAction * action_clear_recent; + QMenu * menu_recent; + SessionManager session; + bool isChanged, first_show; + int tid, max_recent; + +private slots: + void changedDock(); + void sessionLoading(QPIConfig & conf) {loadingSession(conf);} + void sessionSaving(QPIConfig & conf) {savingSession(conf);} +// void changedDockClose(QObject * dock); + void closeDock(int index); + void recentTriggered(); + +public slots: + void setMaxRecentItems(int mr); + void changed() {setChanged(true);} + void newFile(); + void openFile(); + void openFiles(); + bool saveFile(bool ask = false); + bool saveAsFile(); + void clearRecent(); + +signals: + +}; + +#endif // MAINWINDOW_H diff --git a/test/qad/application/etabwidget.cpp b/test/qad/application/etabwidget.cpp new file mode 100644 index 0000000..4da9dc3 --- /dev/null +++ b/test/qad/application/etabwidget.cpp @@ -0,0 +1,104 @@ +#include "etabwidget.h" + + +ETabWidget::ETabWidget(QWidget* parent): QTabWidget(parent) { + tabBar()->setMouseTracking(true); + tabBar()->installEventFilter(this); +} + + +void ETabWidget::retranslate() { + for (int i = 0; i < buttons.size(); ++i) + buttons[i].toolTip = QApplication::translate("MainWindow", buttons[i].srcToolTip.toUtf8(), 0/*, QCoreApplication::UnicodeUTF8*/); + QList bl = findChildren(); + foreach (QToolButton * i, bl) + i->setToolTip(QApplication::translate("MainWindow", i->property("sourceToolTip").toString().toUtf8(), 0/*, QCoreApplication::UnicodeUTF8*/)); +} + + +int ETabWidget::addTab(QWidget * page, const QIcon & icon, const QString & label) { + int ret = QTabWidget::addTab(page, icon, label); + QWidget * w = new QWidget(); + w->setLayout(new QBoxLayout(QBoxLayout::RightToLeft)); + w->layout()->setContentsMargins(0, 0, 0, 0); + w->layout()->setSpacing(2); + QToolButton * b; + foreach (const TabButton & i, buttons) { + b = new QToolButton(); + b->setToolTip(i.toolTip); + b->setIconSize(QSize(16, 16)); + b->setIcon(i.icon); + //b->setFlat(true); + b->setProperty("sourceToolTip", i.toolTip); + b->setProperty("buttonRole", i.role); + connect(b, SIGNAL(clicked(bool)), this, SLOT(buttonClicked())); + w->layout()->addWidget(b); + b->setVisible(i.visible); + } + tabBar()->setTabButton(ret, QTabBar::RightSide, w); + return ret; +} + + +void ETabWidget::setButtonVisible(int role, bool yes) { + QList bl = findChildren(); + foreach (QToolButton * i, bl) + if (i->property("buttonRole").toInt() == role) + i->setVisible(yes); + QWidget * w; + for (int i = 0; i < buttons.size(); ++i) { + if (buttons[i].role == role) + buttons[i].visible = yes; + w = tabBar()->tabButton(i, QTabBar::RightSide); + if (w != 0) w->adjustSize(); + } + tabBar()->adjustSize(); +} + +/* +void ETabWidget::removeTab(int index) { + tbs.removeAll(qobject_cast(tabBar()->tabButton(index, QTabBar::RightSide)->layout()->itemAt(1)->widget())); + tbs.removeAll(qobject_cast(tabBar()->tabButton(index, QTabBar::RightSide)->layout()->itemAt(0)->widget())); + QTabWidget::removeTab(index); +} +*/ + +bool ETabWidget::eventFilter(QObject * o, QEvent * e) { + static int prev = -1; + if (e->type() == QEvent::MouseMove) { + QTabBar * t = qobject_cast(o); + if (t == 0) return QTabWidget::eventFilter(o, e); + for (int i = 0; i < count(); ++i) + if (t->tabRect(i).contains(((QMouseEvent * )e)->pos())) { + if (i != prev) { + prev = i; + emit tabHovered(i); + } + return QTabWidget::eventFilter(o, e); + } + if (-1 != prev) { + prev = -1; + emit tabHovered(-1); + } + } + if (e->type() == QEvent::Leave) { + if (-1 != prev) { + prev = -1; + emit tabHovered(-1); + } + } + return QTabWidget::eventFilter(o, e); +} + + +void ETabWidget::buttonClicked() { + QToolButton * s = qobject_cast(sender()); + if (s == 0) return; + QWidget * pw = s->parentWidget(); + if (pw == 0) return; + for (int i = 0; i < count(); ++i) + if (tabBar()->tabButton(i, QTabBar::RightSide) == pw) { + emit tabButtonClicked(i, s->property("buttonRole").toInt()); + return; + } +} diff --git a/test/qad/application/etabwidget.h b/test/qad/application/etabwidget.h new file mode 100644 index 0000000..ad9f304 --- /dev/null +++ b/test/qad/application/etabwidget.h @@ -0,0 +1,54 @@ +#ifndef ETABWIDGET_H +#define ETABWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class ETabWidget: public QTabWidget +{ + Q_OBJECT +public: + explicit ETabWidget(QWidget * parent = 0); + + void retranslate(); + void addTabButton(int role, const QIcon & icon, const QString & toolTip = QString()) {buttons << TabButton(role, icon, toolTip);} + int addTab(QWidget * page, const QIcon & icon, const QString & label); + int addTab(QWidget * page, const QString & label) {return addTab(page, QIcon(), label);} + void setButtonVisible(int role, bool yes); + +private: + bool eventFilter(QObject * o, QEvent * e); + void tabInserted(int) {emit countChanged();} + void tabRemoved(int) {emit countChanged();} + + struct TabButton { + TabButton(int r, const QIcon & i, const QString & t) {role = r; icon = i; visible = true; srcToolTip = t; toolTip = QApplication::translate("MainWindow", t.toUtf8(), 0/*, QCoreApplication::UnicodeUTF8*/);} + int role; + bool visible; + QIcon icon; + QString srcToolTip; + QString toolTip; + }; + + QList buttons; + +private slots: + void buttonClicked(); + +signals: + void countChanged(); + void tabHovered(int tab); + void tabButtonClicked(int tab, int role); + +}; + +#endif // ETABWIDGET_H diff --git a/test/qad/application/historyview.cpp b/test/qad/application/historyview.cpp new file mode 100644 index 0000000..2e13223 --- /dev/null +++ b/test/qad/application/historyview.cpp @@ -0,0 +1,161 @@ +#include "historyview.h" + + +HistoryView::HistoryView(QWidget* parent): QListWidget(parent) { + setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + setSelectionMode(QAbstractItemView::MultiSelection); + setEditTriggers(NoEditTriggers); + active_ = true; + duplicates_ = false; + index = 0; + setLimit(32); + setHistoryColor(palette().color(QPalette::Highlight)); + connect(this, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemClicked(QListWidgetItem*))); + connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged())); + registerAction(-1, tr("History cleared"), QImage(":/icons/clear-history.png")); +} + + +HistoryView::~HistoryView() { +} + + +QByteArray HistoryView::current() const { + QListWidgetItem * ci = currentItem(); + if (!ci) return QByteArray(); + int i = row(ci); + if (i < 0 || i >= history_.size()) return QByteArray(); + return history_[i].command; +} + + +void HistoryView::addEntry(int action, int count_, const QString & suffix) { + if (!active_) return; + QByteArray ba; + emit commandRequest(ba); + if (!duplicates_) + if (current() == ba) + return; + int cnt = count(); + for (int i = index; i < cnt; ++i) + if (i >= 0) delete takeItem(index); + QListWidgetItem * li = new QListWidgetItem(actions_.value(action).icon, actionText(action, count_) + suffix); + blockSignals(true); + addItem(li); + setCurrentItem(li); + index = count(); + history_.resize(index); + history_.back() = Entry(action, ba); + checkLimit(); + scrollToItem(item(index - 1)); + for (int i = 0; i < index; ++i) + item(i)->setSelected(true); + blockSignals(false); + emit changed(); + emit redoAvailable(false); + emit undoAvailable((index > 1)); + emit clearAvailable(history_.count() > 1); +} + + +void HistoryView::registerAction(int action, const QString & text, const QImage & icon) { + actions_[action] = HistoryView::Action(action, text, icon); +} + + +QString HistoryView::actionText(int action, int count_) { + return QString(actions_.value(action).text).replace("%count", QString::number(count_)); +} + + +void HistoryView::checkLimit() { + if (count() < limit_ + 1) return; + int c = count() - limit_; + for (int i = 0; i < c; ++i) { + if (i >= index - 1) { + if (count() < 2) break; + delete takeItem(1); + history_.remove(1); + } else { + if (count() < 1) break; + delete takeItem(0); + history_.pop_front(); + index--; + } + } + if (index < 1) index = 1; + if (index > count()) index = count(); +} + + +void HistoryView::itemClicked(QListWidgetItem * item) { + if (!active_) return; + if (index == row(item) + 1) return; + index = row(item) + 1; + //qDebug() << actions[index - 1].command; + emit commandExecute(history_[index - 1].command); + emit changed(); + itemSelectionChanged(); +} + + +void HistoryView::itemSelectionChanged() { + if (!active_) return; + if (index < 1) index = 1; + //qDebug() << "changed" << count(); + blockSignals(true); + setCurrentItem(item(index - 1)); + for (int i = 0; i < index; ++i) + item(i)->setSelected(true); + for (int i = index; i < count(); ++i) + item(i)->setSelected(false); + blockSignals(false); + emit redoAvailable(index < count()); + emit undoAvailable((index > 1)); +} + + +void HistoryView::setLimit(int l) { + limit_ = l; + checkLimit(); + emit redoAvailable(index < count()); + emit undoAvailable((index > 1)); +} + + +void HistoryView::setHistoryColor(const QColor & c) { + color_ = c; + QPalette pal(palette()); + pal.setColor(QPalette::Highlight, color_); + pal.setColor(QPalette::HighlightedText, pal.color(QPalette::Text)); + setPalette(pal); +} + + +void HistoryView::clear(bool silent) { + history_.clear(); + QListWidget::clear(); + if (!silent) addEntry(-1); + emit clearAvailable(false); + emit redoAvailable(index < count()); + emit undoAvailable((index > 1)); +} + + +void HistoryView::undo() { + if (index <= 1) return; + index--; + emit commandExecute(history_[index - 1].command); + emit changed(); + itemSelectionChanged(); +} + + +void HistoryView::redo() { + if (index >= count()) return; + index++; + emit commandExecute(history_[index - 1].command); + emit changed(); + itemSelectionChanged(); +} diff --git a/test/qad/application/historyview.h b/test/qad/application/historyview.h new file mode 100644 index 0000000..e125ad3 --- /dev/null +++ b/test/qad/application/historyview.h @@ -0,0 +1,79 @@ +#ifndef HISTORYVIEW_H +#define HISTORYVIEW_H + +#include +#include + + +class HistoryView: public QListWidget +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive WRITE setActive) + Q_PROPERTY(bool duplicatesEnabled READ isDuplicatesEnabled WRITE setDuplicatesEnabled) + Q_PROPERTY(int limit READ limit WRITE setLimit) + Q_PROPERTY(QColor historyColor READ historyColor WRITE setHistoryColor) +public: + explicit HistoryView(QWidget * parent = 0); + ~HistoryView(); + + bool isActive() const {return active_;} + bool isDuplicatesEnabled() const {return duplicates_;} + int limit() const {return limit_;} + QColor historyColor() const {return color_;} + QByteArray current() const; + + void addEntry(int action, int count = 0, const QString & suffix = QString()); + void registerAction(int action, const QString & text, const QImage & icon = QImage()); + +private: + struct Action { + Action(int i = -1, const QString & t = QString(), const QImage & c = QImage()): id(i), text(t) { + QPixmap px = QPixmap::fromImage(c); + icon.addPixmap(px, QIcon::Active); + icon.addPixmap(px, QIcon::Disabled); + icon.addPixmap(px, QIcon::Normal); + icon.addPixmap(px, QIcon::Selected); + } + int id; + QString text; + QIcon icon; + }; + struct Entry { + Entry(int a = -1, const QByteArray & c = QByteArray()): action(a), command(c) {} + int action; + QByteArray command; + }; + + void checkLimit(); + QString actionText(int action, int count_); + + QMap actions_; + QVector history_; + QColor color_; + bool active_, duplicates_; + int index, limit_; + +public slots: + void setActive(bool yes) {active_ = yes;} + void setDuplicatesEnabled(bool yes) {duplicates_ = yes;} + void setLimit(int l); + void setHistoryColor(const QColor & c); + void clear(bool silent = false); + void undo(); + void redo(); + +private slots: + void itemClicked(QListWidgetItem * item); + void itemSelectionChanged(); + +signals: + void undoAvailable(bool); + void redoAvailable(bool); + void clearAvailable(bool); + void commandRequest(QByteArray & s); + void commandExecute(const QByteArray & s); + void changed(); + +}; + +#endif // HISTORYVIEW_H diff --git a/test/qad/application/lang/qad_application_ru.ts b/test/qad/application/lang/qad_application_ru.ts new file mode 100644 index 0000000..27dd4e8 --- /dev/null +++ b/test/qad/application/lang/qad_application_ru.ts @@ -0,0 +1,57 @@ + + + + + EMainWindow + + + + Show all + Показать все + + + + + Hide all + Скрыть все + + + + Toolbars + Панели инструментов + + + + Docks + Окна + + + + Select file to open + Выбрать файл для открытия + + + + Save changes%1? + Сохранить изменения%1? + + + + in + в + + + + Select file to save + Выберите файл для сохранения + + + + HistoryView + + + History cleared + История очищена + + + diff --git a/test/qad/application/plugin/CMakeLists.txt b/test/qad/application/plugin/CMakeLists.txt new file mode 100644 index 0000000..326c825 --- /dev/null +++ b/test/qad/application/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(application "Gui;Widgets" "") diff --git a/test/qad/application/plugin/edockwidgetplugin.cpp b/test/qad/application/plugin/edockwidgetplugin.cpp new file mode 100644 index 0000000..61a5d87 --- /dev/null +++ b/test/qad/application/plugin/edockwidgetplugin.cpp @@ -0,0 +1,69 @@ +#include "edockwidget.h" +#include "edockwidgetplugin.h" +#include + + +EDockWidgetPlugin::EDockWidgetPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void EDockWidgetPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool EDockWidgetPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * EDockWidgetPlugin::createWidget(QWidget * parent) { + return new EDockWidget(parent); +} + + +QString EDockWidgetPlugin::name() const { + return QLatin1String("EDockWidget"); +} + + +QString EDockWidgetPlugin::group() const { + return QLatin1String("Containers"); +} + + +QIcon EDockWidgetPlugin::icon() const { + return QIcon(":/icons/edockwidget.png"); +} + + +QString EDockWidgetPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString EDockWidgetPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool EDockWidgetPlugin::isContainer() const { + return true; +} + + +QString EDockWidgetPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString EDockWidgetPlugin::includeFile() const { + return QLatin1String("edockwidget.h"); +} + diff --git a/test/qad/application/plugin/edockwidgetplugin.h b/test/qad/application/plugin/edockwidgetplugin.h new file mode 100644 index 0000000..0edc9bd --- /dev/null +++ b/test/qad/application/plugin/edockwidgetplugin.h @@ -0,0 +1,36 @@ +#ifndef EDOCKWIDGETPLUGIN_H +#define EDOCKWIDGETPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class EDockWidgetPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + EDockWidgetPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/application/plugin/emainwindowplugin.cpp b/test/qad/application/plugin/emainwindowplugin.cpp new file mode 100644 index 0000000..630d637 --- /dev/null +++ b/test/qad/application/plugin/emainwindowplugin.cpp @@ -0,0 +1,69 @@ +#include "emainwindow.h" +#include "emainwindowplugin.h" +#include + + +EMainWindowPlugin::EMainWindowPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void EMainWindowPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool EMainWindowPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * EMainWindowPlugin::createWidget(QWidget * parent) { + return new EMainWindow(parent); +} + + +QString EMainWindowPlugin::name() const { + return QLatin1String("EMainWindow"); +} + + +QString EMainWindowPlugin::group() const { + return QLatin1String("Containers"); +} + + +QIcon EMainWindowPlugin::icon() const { + return QIcon(); +} + + +QString EMainWindowPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString EMainWindowPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool EMainWindowPlugin::isContainer() const { + return true; +} + + +QString EMainWindowPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString EMainWindowPlugin::includeFile() const { + return QLatin1String("emainwindow.h"); +} + diff --git a/test/qad/application/plugin/emainwindowplugin.h b/test/qad/application/plugin/emainwindowplugin.h new file mode 100644 index 0000000..51c420c --- /dev/null +++ b/test/qad/application/plugin/emainwindowplugin.h @@ -0,0 +1,36 @@ +#ifndef EMAINWINDOWPLUGIN_H +#define EMAINWINDOWPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class EMainWindowPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + EMainWindowPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/application/plugin/historyviewplugin.cpp b/test/qad/application/plugin/historyviewplugin.cpp new file mode 100644 index 0000000..7113e6e --- /dev/null +++ b/test/qad/application/plugin/historyviewplugin.cpp @@ -0,0 +1,69 @@ +#include "historyview.h" +#include "historyviewplugin.h" +#include + + +HistoryViewPlugin::HistoryViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void HistoryViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool HistoryViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * HistoryViewPlugin::createWidget(QWidget * parent) { + return new HistoryView(parent); +} + + +QString HistoryViewPlugin::name() const { + return QLatin1String("HistoryView"); +} + + +QString HistoryViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon HistoryViewPlugin::icon() const { + return QIcon(":/icons/historyview.png"); +} + + +QString HistoryViewPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString HistoryViewPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool HistoryViewPlugin::isContainer() const { + return true; +} + + +QString HistoryViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString HistoryViewPlugin::includeFile() const { + return QLatin1String("historyview.h"); +} + diff --git a/test/qad/application/plugin/historyviewplugin.h b/test/qad/application/plugin/historyviewplugin.h new file mode 100644 index 0000000..16bf1c4 --- /dev/null +++ b/test/qad/application/plugin/historyviewplugin.h @@ -0,0 +1,36 @@ +#ifndef HISTORYVIEWPLUGIN_H +#define HISTORYVIEWPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class HistoryViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + HistoryViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // HISTORYVIEWPLUGIN_H diff --git a/test/qad/application/plugin/qad_application.cpp b/test/qad/application/plugin/qad_application.cpp new file mode 100644 index 0000000..61d1067 --- /dev/null +++ b/test/qad/application/plugin/qad_application.cpp @@ -0,0 +1,21 @@ +#include "qad_application.h" +#include "edockwidgetplugin.h" +#include "emainwindowplugin.h" +#include "historyviewplugin.h" + + +QADApplication::QADApplication(QObject * parent): QObject(parent) { + //m_widgets.append(new EDockWidgetPlugin(this)); + m_widgets.append(new EMainWindowPlugin(this)); + m_widgets.append(new HistoryViewPlugin(this)); +} + + +QList QADApplication::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_graphic_plugin, QADApplication) +#endif diff --git a/test/qad/application/plugin/qad_application.h b/test/qad/application/plugin/qad_application.h new file mode 100644 index 0000000..5ccdcbd --- /dev/null +++ b/test/qad/application/plugin/qad_application.h @@ -0,0 +1,23 @@ +#ifndef QAD_APPLICATION_H +#define QAD_APPLICATION_H + +#include +#include + +class QADApplication: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.application") +#endif +public: + explicit QADApplication(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_APPLICATION_H diff --git a/test/qad/application/qad_application.qrc b/test/qad/application/qad_application.qrc new file mode 100644 index 0000000..6a6c8f9 --- /dev/null +++ b/test/qad/application/qad_application.qrc @@ -0,0 +1,31 @@ + + + lang/qad_application_ru.ts + ../icons/application-exit.png + ../icons/dialog-close.png + ../icons/configure.png + ../icons/document-edit.png + ../icons/document-new.png + ../icons/document-save.png + ../icons/document-save-all.png + ../icons/document-save-as.png + ../icons/document-open.png + ../icons/document-open-recent.png + ../icons/document-close.png + ../icons/edit-clear.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/edit-undo.png + ../icons/edit-redo.png + ../icons/border-line.png + ../icons/edockwidget.png + ../icons/historyview.png + ../icons/clear-history.png + ../icons/layer-visible-off.png + ../icons/layer-visible-on.png + + diff --git a/test/qad/application/qsingleapplication.cpp b/test/qad/application/qsingleapplication.cpp new file mode 100644 index 0000000..428c4bb --- /dev/null +++ b/test/qad/application/qsingleapplication.cpp @@ -0,0 +1,87 @@ +#include +#include "qsingleapplication.h" + + +#define QSA_SHMSIZE 4096 +#define QSA_MSGSIZE (QSA_SHMSIZE - sizeof(quint32) - sizeof(quint32)) + + +QSingleApplication::QSingleApplication(const QString & app_name): QThread() { + shm.setKey(app_name); + exiting = false; + first = !shm.attach(); + if (!first) { + shm.detach(); + first = !shm.attach(); + if (!first) + return; + } + shm.create(QSA_SHMSIZE); + shm.attach(); + shm.lock(); + void * d = shm.data(); + if (d) memset(d, 0, sizeof(quint32)); + shm.unlock(); + start(); + //qDebug() << "start listen"; +} + + +QSingleApplication::~QSingleApplication() { + if (first) { + exiting = true; + quit(); + if (!wait(100)) + terminate(); + } + if (shm.isAttached()) shm.detach(); +} + + +void QSingleApplication::sendMessage(const QByteArray & m) { + //qDebug() << "send message" << first << shm.isAttached(); + if (first || !shm.isAttached()) return; + if (m.size() > int(QSA_MSGSIZE)) { + qDebug() << "[QSingleApplication] Too large message:" << m.size() << ">" << QSA_MSGSIZE; + return; + } + shm.lock(); + quint32 num(0), size = m.size(); + void * d = shm.data(); + if (d) { + memcpy(&num, d, sizeof(quint32)); + num++; + memcpy(d, &num, sizeof(quint32)); + memcpy((((char*)d) + sizeof(quint32)), &size, sizeof(quint32)); + memcpy((((char*)d) + sizeof(quint32) + sizeof(quint32)), m.constData(), size); + } + shm.unlock(); +} + + +void QSingleApplication::run() { + quint32 num(0), pnum(0), size(0); + while (!exiting) { + shm.lock(); + const void * d = shm.constData(); + if (d) { + memcpy(&num, d, sizeof(quint32)); + if (pnum != num) { + pnum = num; + //qDebug() << "new message" << num; + memcpy(&size, (((const char*)d) + sizeof(quint32)), sizeof(quint32)); + if (size <= int(QSA_MSGSIZE)) { + QByteArray msg; + msg.resize(size); + memcpy(msg.data(), (((const char*)d) + sizeof(quint32) + sizeof(quint32)), size); + emit messageReceived(msg); + } else { + qDebug() << "[QSingleApplication] Invalid message size:" << size; + } + } + } + shm.unlock(); + if (exiting) break; + msleep(10); + } +} diff --git a/test/qad/application/qsingleapplication.h b/test/qad/application/qsingleapplication.h new file mode 100644 index 0000000..45a83df --- /dev/null +++ b/test/qad/application/qsingleapplication.h @@ -0,0 +1,30 @@ +#ifndef QSINGLEAPPLICATION_H +#define QSINGLEAPPLICATION_H + +#include +#include + +class QSingleApplication: public QThread +{ + Q_OBJECT +public: + QSingleApplication(const QString & app_name = QString("qapp")); + ~QSingleApplication(); + + bool isFirst() const {return first;} + +private: + void run(); + + QSharedMemory shm; + bool first, exiting; + +public slots: + void sendMessage(const QByteArray & m); + +signals: + void messageReceived(QByteArray); + +}; + +#endif // QSINGLEAPPLICATION_H diff --git a/test/qad/application/ribbon.cpp b/test/qad/application/ribbon.cpp new file mode 100644 index 0000000..b6fea1d --- /dev/null +++ b/test/qad/application/ribbon.cpp @@ -0,0 +1,230 @@ +#include "ribbon.h" +#include "qad_types.h" +#include + + +Ribbon::Ribbon(QMainWindow * parent_): QToolBar() { + tab = 0; + scroll_area = 0; + delay_e = true; + delay = 1000; + hovered = -1; + setObjectName("ribbon"); + setProperty("ribbon", true); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + parent = parent_; + if (parent_) + parent_->installEventFilter(this); + init(); +} + + +Ribbon::~Ribbon() { +} + + +bool Ribbon::eventFilter(QObject * o, QEvent * e) { + //qDebug() << e; + if (o == parent) { + if (e->type() == QEvent::Resize || e->type() == QEvent::WindowActivate) + _resize(); + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) + _setIconsSize(); + return QToolBar::eventFilter(o, e); + } + if (e->type() == QEvent::ActionChanged) { + QToolButton * b = qobject_cast((QObject * )o->property("ribbonButton").toLongLong()); + if (b != 0) + b->setEnabled(qobject_cast(o)->isEnabled()); + } + return QToolBar::eventFilter(o, e); +} + + +void Ribbon::timerEvent(QTimerEvent * e) { + if (hovers.value(e->timerId(), -1) == hovered) + tab->setCurrentIndex(hovered); + hovers.remove(e->timerId()); + killTimer(e->timerId()); +} + + +void Ribbon::_resize() { + return; // WARNING + for (int i = 0; i < tab->count(); ++i) { + int h = ((QScrollArea*)(tab->widget(i)))->sizeHint().height(); + if (((QScrollArea*)(tab->widget(i)))->horizontalScrollBar()->isVisible()) + h += ((QScrollArea*)(tab->widget(i)))->horizontalScrollBar()->height(); + ((QScrollArea*)(tab->widget(i)))->setMinimumHeight(h); + } +} + + +void Ribbon::_setIconsSize() { + //qDebug() << "resize" << preferredIconSize() << QApplication::font(); + setTabIconSize(preferredIconSize(2, this)); + setIconSize(preferredIconSize(3, this)); +} + + +void Ribbon::setVisible(bool yes) { + QToolBar::setVisible(yes); + if (parent == 0) return; + if (parent->menuBar() == 0) return; + parent->menuBar()->setVisible(!yes); +} + + +void Ribbon::init() { + if (parent == 0) return; + if (parent->menuBar() == 0) return; + QList lm = parent->menuBar()->actions(), la; + clear(); + if (scroll_area) delete scroll_area; + buttons.clear(); + tab = new ETabWidget(); + tab->setObjectName("ribbon_tab_widget"); + connect(tab, SIGNAL(tabHovered(int)), this, SLOT(tabHovered(int))); + connect(tab, SIGNAL(currentChanged(int)), this, SIGNAL(currentTabChanged(int))); + tab->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + QFrame * g; + QBoxLayout * l, * tl; + QToolButton * b; + //tab->setIconSize(QSize(32, 32)); + foreach (QAction * i, lm) { + if (!i->menu()) continue; + //if (!i->menu()->isVisible()) continue; + la = i->menu()->actions(); + QIcon tic = i->icon(); + if (!tic.isNull()) + i->setProperty("__icon", QVariant::fromValue(tic)); + else + tic = i->property("__icon").value(); +//#ifdef Q_OS_MACOS +// tic = QIcon(); +//#endif + tab->addTab(new QWidget(), tic, i->text()); + //qDebug() << this << i->icon() << i->text(); + //continue; + /*QScrollArea * sa = new QScrollArea(); + sa->setWidget(new QWidget()); + sa->setWidgetResizable(true); + sa->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sa->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + sa->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + sa->setFrameShape(QFrame::NoFrame); + tab->addTab(sa, i->icon(), i->text());*/ + tab->widget(tab->count() - 1)->setProperty("ribbonAction", qlonglong((void * )i)); + i->setIcon(QIcon()); + tl = new QBoxLayout(QBoxLayout::LeftToRight); + tl->setSpacing(2); +#ifndef Q_OS_MACOS + tl->setContentsMargins(2, 2, 2, 2); +#else + tl->setContentsMargins(2, 0, 2, 2); +#endif + g = new QFrame(); g->setFrameShape(QFrame::StyledPanel); + l = new QBoxLayout(QBoxLayout::LeftToRight); + g->setLayout(l); + l->setSpacing(2); + l->setContentsMargins(2, 2, 2, 2); + foreach (QAction * j, la) { + if (j->isSeparator()) { + if (l->isEmpty()) continue; + tl->addWidget(g); + g = new QFrame(); g->setFrameShape(QFrame::StyledPanel); + l = new QBoxLayout(QBoxLayout::LeftToRight); + l->setSpacing(2); + l->setContentsMargins(2, 2, 2, 2); + g->setLayout(l); + continue; + } + if (qobject_cast(j)) { + QWidget * _w = qobject_cast(j)->defaultWidget(); + l->addWidget(_w); + _w->show(); + continue; + } + b = new QToolButton(); + b->setEnabled(j->isEnabled()); + b->setProperty("ribbonAction", qlonglong((void * )j)); + j->setProperty("ribbonButton", qlonglong((void * )b)); + j->installEventFilter(this); + if (j->menu() != 0) { + b->setPopupMode(QToolButton::InstantPopup); + b->setMenu(j->menu()); + } else { + b->setCheckable(j->isCheckable()); + if (b->isCheckable()) { + b->setChecked(j->isChecked()); + connect(b, SIGNAL(toggled(bool)), j, SLOT(setChecked(bool))); + connect(b, SIGNAL(clicked(bool)), j, SIGNAL(triggered(bool))); + connect(j, SIGNAL(toggled(bool)), b, SLOT(setChecked(bool))); + } else + connect(b, SIGNAL(clicked()), j, SLOT(trigger())); + } + //b->setIconSize(QSize(16, 16)); + b->setIcon(j->icon()); + b->setText(j->text()); + b->setToolTip(j->text()); + //b->addAction(j); + //b->setShortcut(j->shortcut()); + b->setAutoRaise(true); + b->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + b->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + buttons << b; + l->addWidget(b); + } + tl->addWidget(g); + tl->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed)); + //sa->widget()->setLayout(tl); + tab->widget(tab->count() - 1)->setLayout(tl); + } + setFloatable(false); + setMovable(false); + /*scroll_area = new QScrollArea(); + scroll_area->setFrameShape(QFrame::NoFrame); + scroll_area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll_area->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + scroll_area->setWidget(tab);*/ + _resize(); + //addWidget(scroll_area); + addWidget(tab); + parent->addToolBar(Qt::TopToolBarArea, this); + parent->menuBar()->hide(); + tab->setAutoFillBackground(false); + _setIconsSize(); +} + + +void Ribbon::retranslate() { + QAction * a; + foreach (QToolButton * i, buttons) { + a = (QAction * )(i->property("ribbonAction").toLongLong()); + if (a == 0) continue; + i->setText(a->text()); + i->setToolTip(a->toolTip()); + //i->setShortcut(a->shortcut()); + } + for (int i = 0; i < tab->count(); ++i) { + a = (QAction * )(tab->widget(i)->property("ribbonAction").toLongLong()); + if (a == 0) continue; + tab->setTabText(i, a->text()); + } + _resize(); +} + + +void Ribbon::setIconSize(const QSize & size) { + foreach (QToolButton * i, buttons) + i->setIconSize(size); + _resize(); +} + + +void Ribbon::setTabIconSize(const QSize & size) { +#ifndef Q_OS_MACOS + tab->setIconSize(size); +#endif + _resize(); +} diff --git a/test/qad/application/ribbon.h b/test/qad/application/ribbon.h new file mode 100644 index 0000000..c2fb617 --- /dev/null +++ b/test/qad/application/ribbon.h @@ -0,0 +1,63 @@ +#ifndef RIBBON_H +#define RIBBON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "etabwidget.h" + + +class Ribbon: public QToolBar +{ + Q_OBJECT +public: + explicit Ribbon(QMainWindow * parent = 0); + ~Ribbon(); + + void init(); + void retranslate(); + void setIconSize(const QSize & size); + void setTabIconSize(const QSize & size); + void setButtonStyle(const Qt::ToolButtonStyle & style) {foreach (QToolButton * i, buttons) i->setToolButtonStyle(style);} + void setAutoSwitchEnabled(bool yes) {delay_e = yes;} + void setAutoSwitchDelay(float delay_s) {delay = delay_s * 1000;} + void setCurrentTab(int tab_) {if (tab_ < 0 || tab_ >= tab->count()) return; tab->setCurrentIndex(tab_);} + int currentTab() const {return tab->currentIndex();} + QTabWidget * tabWidget() const {return tab;} + +private: + bool eventFilter(QObject * o, QEvent * e); + void timerEvent(QTimerEvent * e); + void _resize(); + void _setIconsSize(); + + int hovered, delay; + bool delay_e; + QList buttons; + QMap hovers; + ETabWidget * tab; + QScrollArea * scroll_area; + QMainWindow * parent; + +private slots: + void tabHovered(int tab) {if (!delay_e) return; hovers.clear(); hovered = tab; hovers.insert(startTimer(delay), tab);} + +public slots: + void setVisible(bool yes); + void setHidden(bool yes) {setVisible(!yes);} + void show() {setVisible(true);} + void hide() {setVisible(false);} + +signals: + void currentTabChanged(int); + +}; + +#endif // RIBBON_H diff --git a/test/qad/blockview/CMakeLists.txt b/test/qad/blockview/CMakeLists.txt new file mode 100644 index 0000000..9f788a4 --- /dev/null +++ b/test/qad/blockview/CMakeLists.txt @@ -0,0 +1,4 @@ +qad_project(blockview "Gui;Widgets" "qad_utils;qad_widgets") +if (blockview_UTILS) + add_subdirectory(blockeditor) +endif() diff --git a/test/qad/blockview/alignedtextitem.cpp b/test/qad/blockview/alignedtextitem.cpp new file mode 100644 index 0000000..5c6107c --- /dev/null +++ b/test/qad/blockview/alignedtextitem.cpp @@ -0,0 +1,62 @@ +#include "alignedtextitem.h" +#include +#include +#include + + +AlignedTextItem::AlignedTextItem(QGraphicsItem * parent): QGraphicsItem(parent), text_(this) { + align_ = Qt::AlignTop | Qt::AlignHCenter; + text_.setData(1003, true); + setFont(font()); + _move(); +} + + +AlignedTextItem::AlignedTextItem(const QString & text, QGraphicsItem * parent): QGraphicsItem(parent), text_(this) { + align_ = Qt::AlignTop | Qt::AlignHCenter; + text_.setData(1003, true); + setFont(font()); + setText(text); +} + +void AlignedTextItem::setFont(const QFont & f) { + font_ = f; + text_.setFont(sceneFont(f)); + _move(); +} + + +QFont AlignedTextItem::sceneFont(const QFont & f) { + QFont ret = f; + double scl = 16. / QApplication::fontMetrics().size(0, "0").height(); + ret.setPointSizeF(ret.pointSizeF() * scl); + return ret; +} + + +QPointF AlignedTextItem::_point(Qt::Alignment a) const { + QRectF br = text_.boundingRect(); + QPointF ret; + switch (Qt::AlignmentFlag(int(align_ & Qt::AlignHorizontal_Mask))) { + case Qt::AlignRight: ret.rx() = br.left(); break; + case Qt::AlignHCenter: ret.rx() = br.center().x(); break; + case Qt::AlignLeft: ret.rx() = br.right(); break; + default: break; + } + switch (Qt::AlignmentFlag(int(align_ & Qt::AlignVertical_Mask))) { + case Qt::AlignBottom: ret.ry() = br.top(); break; + case Qt::AlignVCenter: ret.ry() = br.center().y(); break; + case Qt::AlignTop: ret.ry() = br.bottom(); break; + default: break; + } + return ret; +} + + +void AlignedTextItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) { + if (isSelected()) { + painter->setPen(QPen(Qt::DashLine)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(boundingRect()); + } +} diff --git a/test/qad/blockview/alignedtextitem.h b/test/qad/blockview/alignedtextitem.h new file mode 100644 index 0000000..b0df285 --- /dev/null +++ b/test/qad/blockview/alignedtextitem.h @@ -0,0 +1,48 @@ +#ifndef ALIGNEDTEXTITEM_H +#define ALIGNEDTEXTITEM_H + +#include +#include +#include +#include + + +class AlignedTextItem: public QGraphicsItem { +public: + AlignedTextItem(QGraphicsItem * parent = 0); + AlignedTextItem(const QString & text, QGraphicsItem * parent = 0); + + void setText(const QString & t) {text_.setText(t); _move();} + void setFont(const QFont & f); + void setPen(const QPen & p) {text_.setPen(p); _move();} + void setBrush(const QBrush & b) {text_.setBrush(b); _move();} + void setAlignment(Qt::Alignment align) {align_ = align; _move();} + + QString text() const {return text_.text();} + QFont font() const {return /*text_.font()*/font_;} + QPen pen() const {return text_.pen();} + QBrush brush() const {return text_.brush();} + Qt::Alignment alignment() const {return align_;} + + void clear() {setText(QString());} + + enum {Type = UserType + 0x100}; + + static QFont sceneFont(const QFont & f); + +protected: + virtual QRectF boundingRect() const {return text_.boundingRect().translated(text_.pos());} + virtual void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0); + virtual int type() const {return Type;} + + void _move() {text_.setPos(-_point(align_));} + QPointF _point(Qt::Alignment a) const; + + QGraphicsSimpleTextItem text_; + Qt::Alignment align_; + QFont font_; + +}; + + +#endif // ALIGNEDTEXTITEM_H diff --git a/test/qad/blockview/blockbase.cpp b/test/qad/blockview/blockbase.cpp new file mode 100644 index 0000000..d93d87c --- /dev/null +++ b/test/qad/blockview/blockbase.cpp @@ -0,0 +1,109 @@ +#include "blockbase.h" +#include "alignedtextitem.h" +#include "qvariantedit.h" + + +QDataStream & operator <<(QDataStream & s, const QGraphicsItem * item) { + if (!item) { + s << int(-1); + return s; + } + const QGraphicsRectItem * irect = qgraphicsitem_cast(item); + const QGraphicsEllipseItem * iell = qgraphicsitem_cast(item); + const QGraphicsSimpleTextItem * itext = qgraphicsitem_cast(item); + const AlignedTextItem * iatext = qgraphicsitem_cast(item); + const QGraphicsLineItem * iline = qgraphicsitem_cast(item); + const QGraphicsPathItem * ipath = qgraphicsitem_cast(item); + const QGraphicsPixmapItem * ipixmap = qgraphicsitem_cast(item); + if (irect) { + s << int(0) << (irect->pen()) << (irect->brush()) << (irect->rect()); + } else if (iell) { + s << int(1) << (iell->pen()) << (iell->brush()) << (iell->rect()); + } else if (itext) { + s << int(2) << (itext->pen()) << (itext->brush()) << (itext->font()) << (itext->text()); + } else if (iatext) { + s << int(6) << (iatext->pen()) << (iatext->brush()) << (iatext->font()) << (iatext->text()) << int(iatext->alignment()); + } else if (iline) { + s << int(3) << (iline->pen()) << (iline->line()); + } else if (ipath) { + s << int(4) << (ipath->pen()) << (ipath->path()); + } else if (ipixmap) { + s << int(7) << (ipixmap->pixmap()) << (ipixmap->transform()); + } else { + s << int(-1); + return s; + } + s << (item->pos()) << (item->rotation()) << int(item->flags()); + return s; +} + + +QDataStream & operator >>(QDataStream & s, QGraphicsItem *& item) { + int type_; s >> type_; + if (type_ < 0) { + item = 0; + return s; + } + QGraphicsRectItem * nrect = 0; + QGraphicsEllipseItem * nell = 0; + QGraphicsSimpleTextItem * ntext = 0; + AlignedTextItem * natext = 0; + QGraphicsLineItem * nline = 0; + QGraphicsPathItem * npath = 0; + QGraphicsPixmapItem * npixmap = 0; + item = 0; + switch (type_) { + case 0: + nrect = new QGraphicsRectItem(); item = nrect; + {QPen _v; s >> _v; nrect->setPen(_v);} + {QBrush _v; s >> _v; nrect->setBrush(_v);} + {QRectF _v; s >> _v; nrect->setRect(_v);} + break; + case 1: + nell = new QGraphicsEllipseItem(); item = nell; + {QPen _v; s >> _v; nell->setPen(_v);} + {QBrush _v; s >> _v; nell->setBrush(_v);} + {QRectF _v; s >> _v; nell->setRect(_v);} + break; + case 2: + ntext = new QGraphicsSimpleTextItem(); item = ntext; + {QPen _v; s >> _v; ntext->setPen(_v);} + {QBrush _v; s >> _v; ntext->setBrush(_v);} + {QFont _v; s >> _v; ntext->setFont(_v);} + {QString _v; s >> _v; ntext->setText(_v);} + break; + case 6: + natext = new AlignedTextItem(); item = natext; + {QPen _v; s >> _v; natext->setPen(_v);} + {QBrush _v; s >> _v; natext->setBrush(_v);} + {QFont _v; s >> _v; natext->setFont(_v);} + {QString _v; s >> _v; natext->setText(_v);} + {int _v; s >> _v; natext->setAlignment((Qt::AlignmentFlag)_v);} + break; + case 3: + nline = new QGraphicsLineItem(); item = nline; + {QPen _v; s >> _v; nline->setPen(_v);} + {QLineF _v; s >> _v; nline->setLine(_v);} + break; + case 4: + npath = new QGraphicsPathItem(); item = npath; + {QPen _v; s >> _v; npath->setPen(_v);} + {QPainterPath _v; s >> _v; npath->setPath(_v);} + break; + case 5: + npixmap = new QGraphicsPixmapItem(); item = npixmap; + {QPixmap _v; s >> _v; npixmap->setPixmap(_v);} + break; + case 7: + npixmap = new QGraphicsPixmapItem(); item = npixmap; + {QPixmap _v; s >> _v; npixmap->setPixmap(_v);} + {QTransform _t; s >> _t; npixmap->setTransform(_t);} + break; + } + if (item) { + {QPointF _v; s >> _v; item->setPos(_v);} + {qreal _v; s >> _v; item->setRotation(_v);} + {int _v; s >> _v; item->setFlags((QGraphicsItem::GraphicsItemFlags)_v);} + } + return s; +} diff --git a/test/qad/blockview/blockbase.h b/test/qad/blockview/blockbase.h new file mode 100644 index 0000000..f52d36f --- /dev/null +++ b/test/qad/blockview/blockbase.h @@ -0,0 +1,59 @@ +#ifndef BLOCKBASE_H +#define BLOCKBASE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qad_types.h" + + +/// data: +/// 1002 - flag for move parent (true) +/// 1003 - flag for visualize selection (true) +/// 1004 - BlockItemPin ("pin") +/// 1005 - BlockBusItem ("connection") +/// 1006 - BlockItem ("item") +/// 1007 - BlockItem selection ("item_selection") +/// 1008 - item is NOT decor, ignore for function decors() (true) +/// 1009 - item is scene decor ("decor") +/// 1010 - BlockItem decor (src text for QGraphicsSimpleTextItem) +/// 1011 - item is BlockItem decor ("decor") +/// 1100 - flag for correct move (true) + +static const int _blockitem_current_version_ = 1; + + +QDataStream & operator <<(QDataStream & s, const QGraphicsItem * item); +QDataStream & operator >>(QDataStream & s, QGraphicsItem *& item); + + +class BlockItemBase: public QObject +{ + Q_OBJECT + Q_ENUMS(Action) +public: + + enum Action { + BlockAdd = 1, + BlockMove, + BlockRemove, + BlockCopy, + BusAdd, + BusRemove, + BusPointAdd, + BusPointMove, + BusPointRemove, + BusSegmentAdd, + BusSegmentMove, + BusSegmentRemove, + Paste + }; + +}; + +#endif // BLOCKBASE_H diff --git a/test/qad/blockview/blockbusitem.cpp b/test/qad/blockview/blockbusitem.cpp new file mode 100644 index 0000000..a15424a --- /dev/null +++ b/test/qad/blockview/blockbusitem.cpp @@ -0,0 +1,814 @@ +#include "blockview.h" + + +BlockBusItem::BlockBusItem(bool temp): QGraphicsObject(), PropertyStorage() { + temp_ = temp; + _init(); + if (!temp) setData(1005, "connection"); + else hide(); +} + + +BlockBusItem::BlockBusItem(const BlockBusItem & other): QGraphicsObject(), PropertyStorage() { + temp_ = false; + _init(); + setData(1005, "connection"); + setPen(other.pen()); + setBrush(other.brush()); + setBusType(other.busType()); + max_ep = other.max_ep; + pol = other.pol; + segments = other.segments; + im_bus = other.im_bus; + im_end = other.im_end; + im_bus_scale = other.im_bus_scale; + im_end_scale = other.im_end_scale; + updateGeometry(); +} + + +void BlockBusItem::_init() { + setZValue(1.); + setBusType(-1); + setAcceptHoverEvents(true); + ph.setColor(Qt::blue); ph.setJoinStyle(Qt::MiterJoin); + bh.setColor(Qt::blue); bh.setStyle(Qt::SolidPattern); + pu = pa = pr = ph; bu = ba = br = bh; + grid_step = 10.; + pu.setWidth(1); + pu.setColor(Qt::black); bu.setColor(Qt::black); + pa.setColor(Qt::darkGreen); ba.setColor(Qt::darkGreen); + pr.setColor(Qt::darkRed); br.setColor(Qt::darkRed); + pn.setColor(Qt::gray); pn.setStyle(Qt::DashLine); + if (temp_) { + pu.setStyle(Qt::DashLine); + pu.setColor(Qt::darkGray); + bu.setColor(Qt::darkGray); + } + setPen(pu); setBrush(bu); + max_ep = 0; + selPoint = selSegment = state_ = -1; + pen_width = 2.; + point_size = 3.; + im_bus_scale = im_end_scale = 1.; + moved = deleted = mark_in = mark_out = new_segment = mm_cancel = lm_point = false; + anim_point_size.setTargetObject(this); + anim_point_size.setPropertyName("pointSize"); + anim_point_size.setStartValue(0); + anim_point_size.setEasingCurve(QEasingCurve::OutQuad); + anim_point_size.setEndValue(point_size); + anim_point_size.setDuration(200); +} + + +void BlockBusItem::reconnect() { + if (temp_) return; + if (!scene()) return; + if (scene()->views().isEmpty()) return; + QMetaObject::invokeMethod(scene()->views().back(), "reconnectAll"); +} + + +bool BlockBusItem::sceneEvent(QEvent * e) { + if (temp_) return QGraphicsObject::sceneEvent(e); + switch (e->type()) { + case QEvent::GraphicsSceneHoverEnter: hoverEnterEvent((QGraphicsSceneHoverEvent * )e); break; + case QEvent::GraphicsSceneHoverMove: hoverMoveEvent((QGraphicsSceneHoverEvent * )e); break; + case QEvent::GraphicsSceneHoverLeave: hoverLeaveEvent((QGraphicsSceneHoverEvent * )e); break; + case QEvent::GraphicsSceneMousePress: mousePressEvent((QGraphicsSceneMouseEvent * )e); break; + case QEvent::GraphicsSceneMouseMove: mouseMoveEvent((QGraphicsSceneMouseEvent * )e); break; + case QEvent::GraphicsSceneMouseRelease: mouseReleaseEvent((QGraphicsSceneMouseEvent * )e); break; + default: break; + } + return QGraphicsObject::sceneEvent(e); +} + + +int BlockBusItem::addPoint(const QPointF & point, bool update) { + int ei = pol.indexOf(point); + if (ei >= 0) return ei; + if (selSegment < 0 || selSegment >= pol.size() - 1) return -1; + pol << quantize(nearestPointOnLine(pol[segments[selSegment].first], pol[segments[selSegment].second], point), grid_step); + selPoint = pol.size() - 1; + segments << QPair(selPoint, segments[selSegment].second); + segments[selSegment].second = selPoint; + selSegment = -1; + updateGeometry(); + if (scene() != 0 && update) scene()->update(); + return pol.size() - 1; +} + + +int BlockBusItem::segmentPointPair(int point, int * seg) const { + for (int i = 0; i < segments.size(); ++i) { + if (segments[i].first == point) { + if (seg) *seg = i; + return segments[i].second; + } + if (segments[i].second == point) { + if (seg) *seg = i; + return segments[i].first; + } + } + if (seg) *seg = -1; + return -1; +} + + +void BlockBusItem::removePoint(int index) { + if (index < 0 || index > pol.size() - 1) return; + int sc = 0, fs = -1, ss = -1; + for (int i = 0; i < segments.size(); ++i) + if (segments[i].first == index || + segments[i].second == index) { + sc++; + if (fs < 0) fs = i; + else ss = i; + } + int ei(0); + switch (sc) { + case 1: + segments.removeAt(fs); + break; + case 2: + if (segments[ss].first == index) ei = segments[ss].second; + else ei = segments[ss].first; + if (segments[fs].first == index) segments[fs].first = ei; + else segments[fs].second = ei; + segments.removeAt(ss); + break; + default: return; + } + pol.remove(index); + for (int i = 0; i < segments.size(); ++i) { + if (segments[i].first >= index) + segments[i].first--; + if (segments[i].second >= index) + segments[i].second--; + } + selPoint = -1; + checkDelete(); + updateGeometry(); + if (scene() != 0) scene()->update(); +} + + +void BlockBusItem::removeSegment(int index) { + if (index < 0 || index > segments.size() - 1) return; + int pif = segments[index].first, pis = segments[index].second; + if (pif > pis) qSwap(pif, pis); + int scf = 0, scs = 0; + for (int i = 0; i < segments.size(); ++i) { + if (segments[i].first == pif || + segments[i].second == pif) + scf++; + if (segments[i].first == pis || + segments[i].second == pis) + scs++; + } + if (scs <= 2) removePoint(pis); + if (scf <= 2) removePoint(pif); + if (scs <= 2 || scf <= 2) selSegment = -1; + if (scene() != 0) scene()->update(); + +} + + +void BlockBusItem::appendPoint(const QPointF & p) { + pol << p; + if (pol.size() > 1) + segments << QPair(pol.size() - 2, pol.size() - 1); + updateGeometry(); +} + + +void BlockBusItem::appendPoint(qreal x, qreal y) { + appendPoint(QPointF(x, y)); +} + + +void BlockBusItem::clear() { + pol.clear(); + segments.clear(); + updateGeometry(); +} + + +void BlockBusItem::movePolyline(const QPointF & dp) { + pol.translate(dp); + prepareGeometryChange(); +} + + +void BlockBusItem::movePoint(int index, const QPointF & dp) { + pol[index] += dp; + prepareGeometryChange(); +} + + +void BlockBusItem::setWidth(const double & w) { + pen_width = w; + update(); +} + + +void BlockBusItem::setColor(const QColor & c) { + pu.setColor(c); + bu.setColor(c); + update(); +} + + +void BlockBusItem::markAsInput() { + mark_in = true; + mark_out = false; +} + + +void BlockBusItem::markAsOutput() { + mark_in = false; + mark_out = true; +} + + +void BlockBusItem::unmark() { + mark_in = mark_out = false; +} + + +void BlockBusItem::simplify(bool full) { + int pcnt = pol.size(); + for (int s = 0; s < segments.size(); ++s) { + if (pol[segments[s].first] != pol[segments[s].second]) continue; + int ti = segments[s].first, fi = segments[s].second; + segments.removeAt(s); + pol.remove(fi); + for (int i = 0; i < segments.size(); ++i) { + if (segments[i].first == fi) segments[i].first = ti; + if (segments[i].second == fi) segments[i].second = ti; + if (segments[i].first > fi) segments[i].first--; + if (segments[i].second > fi) segments[i].second--; + } + } + if (full) { + QList segs; + for (int p = 0; p < pol.size(); ++p) { + if (pointSegmentsCount(p, &segs) != 2) continue; + int s0 = segs[0], s1 = segs[1]; + QPointF cp = pol[p], sp[2]; + for (int i = 0; i < 2; ++i) { + if (segments[segs[i]].first == p) sp[i] = pol[segments[segs[i]].second]; + else sp[i] = pol[segments[segs[i]].first]; + } + QLineF l0(sp[0], cp), l1(cp, sp[1]); + if (qAbs(l0.angle() - l1.angle()) > 0.1) continue; + if (segments[s0].first == p) { + if (segments[s1].first == p) segments[s0].first = segments[s1].second; + else segments[s0].first = segments[s1].first; + } else { + if (segments[s1].first == p) segments[s0].second = segments[s1].second; + else segments[s0].second = segments[s1].first; + } + segments.removeAt(s1); + pol.remove(p); + for (int i = 0; i < segments.size(); ++i) { + if (segments[i].first >= p) segments[i].first--; + if (segments[i].second >= p) segments[i].second--; + } + p = -1; + } + } + if (pcnt == pol.size()) return; + updateGeometry(); + //if (scene()) scene()->update(); +} + + +void BlockBusItem::adjustLine() { +} + + +int BlockBusItem::endpointCount() const { + return endpoints().size(); +} + + + +QList BlockBusItem::connectedBlocks() const { + QList pins = connections_.values(); + QSet ret; + foreach (BlockItemPin * p, pins) + ret << p->parent(); + return ret.toList(); +} + + +QList BlockBusItem::connectedPins() const { + return connections_.values(); +} + + +void BlockBusItem::setBusState(bool state) { + int s = state ? 1 : 0; + if (state_ == s) return; + state_ = s; + update(); +} + + +void BlockBusItem::clearBusState() { + if (state_ == -1) return; + state_ = -1; + update(); +} + + +QByteArray BlockBusItem::save() const { + ChunkStream cs; + cs << cs.chunk(1, busType()) << cs.chunk(2, busName()) << cs.chunk(3, width()) << cs.chunk(4, pen()) + << cs.chunk(5, brush()) << cs.chunk(6, pol) << cs.chunk(7, segments) << cs.chunk(8, props) + << cs.chunk(9, im_bus_scale) << cs.chunk(10, im_end_scale); + return cs.data(); +} + + +void BlockBusItem::load(const QByteArray & data) { + clear(); + if (data.isEmpty()) return; + ChunkStream cs(data); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: setBusType(cs.getData()); break; + case 2: setBusName(cs.getData()); break; + case 3: setWidth(cs.getData()); break; + case 4: setPen(cs.getData()); break; + case 5: setBrush(cs.getData()); break; + case 6: pol = cs.getData(); break; + case 7: segments = cs.getData > >(); break; + case 8: props = cs.getData >(); break; + case 9: im_bus_scale = cs.getData(); break; + case 10: im_end_scale = cs.getData(); break; + } + } + updateGeometry(); +} + + +BlockBusItem * BlockBusItem::copy() const { + return new BlockBusItem(*this); +} + + +void BlockBusItem::saveState() { + segments_s = segments; + ends_ind_s = ends_ind; + ends_s = ends; + pol_s = pol; +} + + +void BlockBusItem::restoreState() { + segments = segments_s; + ends_ind = ends_ind_s; + ends = ends_s; + pol = pol_s; +} + + +void BlockBusItem::updateGeometry() { + ends = endpoints(); + ends_ind.clear(); + for (int e = 0; e < ends.size(); ++e) { + int ce = ends[e]; + for (int s = 0; s < segments.size(); ++s) { + if (segments[s].first == ce) { + ends_ind << QPair(segments[s].first, segments[s].second); + break; + } + if (segments[s].second == ce) { + ends_ind << QPair(segments[s].second, segments[s].first); + break; + } + } + } + reconnect(); + prepareGeometryChange(); +} + + +bool BlockBusItem::checkDelete() { + if (pol.size() >= 2 && segments.size() >= 1) return false; + deleteLater(); + return true; +} + + +void BlockBusItem::emitAction(BlockItemBase::Action a) { + QMetaObject::invokeMethod(scene()->views().back(), "schemeAction", Q_ARG(BlockItemBase::Action, a), Q_ARG(QList, QList() << this)); + QMetaObject::invokeMethod(scene()->views().back(), "connectionsChanged"); +} + + +QVector BlockBusItem::endpoints() const { + QVector counts(pol.size(), 0), ret; + for (int i = 0; i < segments.size(); ++i) { + counts[segments[i].first]++; + counts[segments[i].second]++; + } + for (int i = 0; i < counts.size(); ++i) { + if (counts[i] == 1) + ret << i; + } + return ret; +} + + +QVector BlockBusItem::endpointLine(int ep, double angle) const { + QVector ret; + int seg = -1; + int np = segmentPointPair(ep, &seg), pp = np; + if (ep < 0 || np < 0) return ret; + if (pol[np] == pol[ep]) return ret; + //QPointF sp = pol[np] - pol[ep]; + QLineF l(pol[ep], pol[np]); + //qDebug() << "first" << l.angle() << angle << (l.angle() != angle); + if (qAbs(l.angle() - angle) > 0.1) return ret; + //qDebug() << "check next" << segments.size(); + for (int i = 0; i < segments.size(); ++i) { + //qDebug() << i << np << pointSegmentsCount(np); + if (np < 0) break; + if (pointSegmentsCount(np) != 2) break; + if (i > 0) { + QLineF l(pol[pp], pol[np]); + //qDebug() << i << l.angle() << angle; + if (qAbs(l.angle() - angle) > 0.1) break; + } + ret << np; + pp = np; + np = neighborSegmentPoint(np, &seg); + } + return ret; + +} + + +int BlockBusItem::pointSegmentsCount(int point, QList * segs) const { + int ret = 0; + if (segs) segs->clear(); + for (int i = 0; i < segments.size(); ++i) + if (segments[i].first == point || segments[i].second == point) { + ret++; + if (segs) segs->append(i); + } + return ret; +} + + +int BlockBusItem::neighborSegmentPoint(int point, int * seg) const { + if (point < 0 || !seg) return -1; + for (int i = 0; i < segments.size(); ++i) { + if (i == *seg) continue; + if (segments[i].first == point) {*seg = i; return segments[i].second;} + if (segments[i].second == point) {*seg = i; return segments[i].first ;} + } + return -1; +} + + +void BlockBusItem::testPoint(QPointF pos, int * sel_point, int * sel_segment) { + for (int i = 0; i < pol.size(); ++i) { + if ((pol[i] - pos).manhattanLength() <= 10.) { // Point + *sel_point = i; + *sel_segment = -1; + return; + } + } + for (int i = 0; i < segments.size(); ++i) { + if (distPointToLine(pol[segments[i].first], pol[segments[i].second], pos) <= 7.) { // Segment + *sel_point = -1; + *sel_segment = i; + return; + } + } + *sel_point = -1; + *sel_segment = -1; +} + + +void BlockBusItem::hoverEnterEvent(QGraphicsSceneHoverEvent * e) { + tt = bus_name + (bus_name.isEmpty() ? "" : "\n\n") + tr("Add point: Ctrl + LeftClick\nRemove point\\segment: Ctrl + RightClick\nNew branch: Shift + LeftClick\nRemove connection: Shift + RightClick"); +} + + +void BlockBusItem::hoverMoveEvent(QGraphicsSceneHoverEvent * e) { + if (temp_) return; + QPointF sp = e->scenePos(); + int pp = selPoint; + int ps = selSegment; + bool empt = !(selPoint >= 0 || selSegment >= 0); + testPoint(sp, &selPoint, &selSegment); + if ((selPoint >= 0 && pp != selPoint) || (selSegment >= 0 && ps != selSegment)) { + if (((BlockView *)scene()->views().back())->isBlockAnimationEnabled()) { + setPointSize(0); + anim_point_size.start(); + } else setPointSize(anim_point_size.endValue().toDouble()); + } + + if (selPoint >= 0 || selSegment >= 0) { + if (empt) { + QList pins = connectedPins(); + foreach (BlockItemPin * p, pins) { + p->animAccept(); + } + } + setToolTip(tt); + update(); + return; + } + setToolTip(QString()); + QList il = scene()->items(sp, Qt::ContainsItemBoundingRect, Qt::DescendingOrder), bil; + bil << this; + for (int i = 0; i < il.size(); ++i) { + QGraphicsItem * b = il[i]; + if (b->data(1005) == "connection" && b != this) { + int tp = -1, ts = -1; + ((BlockBusItem*)b)->testPoint(sp, &tp, &ts); + if (tp >= 0 || ts >= 0) { + foreach (QGraphicsItem * b2, bil) + b2->stackBefore(b); + break; + } + bil << b; + } + } + update(); +} + + +void BlockBusItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) { + if (temp_) return; + selPoint = selSegment = -1; + setPen(pu); setBrush(bu); + setToolTip(QString()); + anim_point_size.stop(); + update(); + QGraphicsObject::hoverLeaveEvent(e); +} + + +void BlockBusItem::mousePressEvent(QGraphicsSceneMouseEvent * e) { + if (temp_) return; + lp = quantize(e->scenePos(), grid_step); + if (new_segment) { + QMetaObject::invokeMethod(scene()->views().back(), "newBranchCancel"); + } + new_segment = false; + if ((selPoint < 0 || selPoint > pol.size() - 1) && (selSegment < 0)) { + QGraphicsObject::mousePressEvent(e); + return; + } + int btncnt = 0; + if (endpoints().contains(selPoint) && e->button() == Qt::LeftButton) + QMetaObject::invokeMethod(scene()->views().back(), "startBusPointMove", Q_ARG(int, busType())); + if (e->buttons().testFlag(Qt::LeftButton)) btncnt++; + if (e->buttons().testFlag(Qt::RightButton)) btncnt++; + if (e->buttons().testFlag(Qt::MidButton)) btncnt++; + if (btncnt > 0) mm_mods = e->modifiers(); + //qDebug() << "press" << e; + if (btncnt >= 2 && e->button() == Qt::RightButton) { + mm_cancel = true; + moved = false; + QPointF lp = qp - press_pos; + //qDebug() << lp; + if (selPoint >= 0 && selPoint <= pol.size() - 1) { + pol[selPoint] += lp; + } + if (selSegment >= 0 && selSegment <= segments.size() - 1) { + pol[segments[selSegment].first] += lp; + pol[segments[selSegment].second] += lp; + } + moved = true; + prepareGeometryChange(); + return; + } + if (e->modifiers().testFlag(Qt::ShiftModifier)) { + if (e->buttons().testFlag(Qt::LeftButton)) { + if (selSegment >= 0) + press_pos = quantize(nearestPointOnLine(pol[segments[selSegment].first], pol[segments[selSegment].second], e->scenePos()), grid_step); + else { + if (selPoint >= 0) + press_pos = pol[selPoint]; + else + return; + } + if (max_ep >= 2) { + if (endpointCount() >= max_ep) + if (pointSegmentsCount(selPoint) >= 2 || selSegment >= 0) + return; + } + QMetaObject::invokeMethod(scene()->views().back(), "newBranch", Q_ARG(BlockBusItem * , this)); + new_segment = true; + return; + } + if (e->buttons().testFlag(Qt::RightButton)) { + deleteLater(); + } + } + if (e->modifiers().testFlag(Qt::ControlModifier)) { + if (e->buttons().testFlag(Qt::RightButton)) { + if (selPoint >= 0 && selPoint <= pol.size() - 1) { + removePoint(selPoint); + simplify(); + if (!checkDelete()) emitAction(BlockItemBase::BusPointRemove); + return; + } + if (selSegment >= 0 && selSegment <= segments.size() - 1) { + removeSegment(selSegment); + simplify(); + if (!checkDelete()) emitAction(BlockItemBase::BusSegmentRemove); + return; + } + } + if (e->buttons().testFlag(Qt::LeftButton) && selSegment >= 0) { + if (addPoint(e->scenePos()) >= 0) + emitAction(BlockItemBase::BusPointAdd); + return; + } + } + if (e->modifiers().testFlag(Qt::ShiftModifier)) { + if (e->buttons().testFlag(Qt::RightButton)) { + if (deleted) return; + deleted = true; + } + } +} + + +void BlockBusItem::mouseMoveEvent(QGraphicsSceneMouseEvent * e) { + if (temp_ || mm_cancel) return; + if (((selPoint < 0 || selPoint > pol.size() - 1) && (selSegment < 0)) && !new_segment) { + QGraphicsObject::mouseMoveEvent(e); + return; + } + qp = quantize(e->scenePos(), grid_step); + lp = qp - lp; + if (e->buttons().testFlag(Qt::LeftButton) && mm_mods.testFlag(Qt::ShiftModifier) && new_segment) { + QMetaObject::invokeMethod(scene()->views().back(), "newBranchTrace", Q_ARG(BlockBusItem * , this), Q_ARG(QPointF, e->scenePos())); + return; + } + if (new_segment) { + new_end = qp; + prepareGeometryChange(); + } else { + if (e->buttons().testFlag(Qt::LeftButton)) { + lm_point = selPoint >= 0; + if (selPoint >= 0 && selPoint <= pol.size() - 1) + pol[selPoint] += lp; + if (selSegment >= 0 && selSegment <= segments.size() - 1) { + pol[segments[selSegment].first] += lp; + pol[segments[selSegment].second] += lp; + } + moved = true; + prepareGeometryChange(); + } + } + lp = qp; +} + + +void BlockBusItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * e) { + mm_mods = 0; + int btncnt = 0; + if (e->buttons().testFlag(Qt::LeftButton)) btncnt++; + if (e->buttons().testFlag(Qt::RightButton)) btncnt++; + if (e->buttons().testFlag(Qt::MidButton)) btncnt++; + if (btncnt == 0) mm_cancel = false; + if (new_segment) { + QMetaObject::invokeMethod(scene()->views().back(), "newBranchAccept", Q_ARG(BlockBusItem * , this)); + updateGeometry(); + selPoint = selSegment = -1; + } + if (moved) { + simplify(false); + if (lm_point) { + emitAction(BlockItemBase::BusPointMove); + } else { + reconnect(); + emitAction( BlockItemBase::BusSegmentMove); + } + } + QMetaObject::invokeMethod(scene()->views().back(), "endBusPointMove"); + moved = new_segment = false; + QGraphicsObject::mouseReleaseEvent(e); +} + + +void BlockBusItem::paint(QPainter * p, const QStyleOptionGraphicsItem * o, QWidget * w) { + ph.setWidthF(pen_width + point_size / 2.); + pu.setWidthF(pen_width); + pa.setWidthF(pen_width); + pr.setWidthF(pen_width); + pn.setWidthF(pen_width); + if (pol.size() < 2) return; + if (selPoint >= 0 || selSegment >= 0) { + p->setPen(pa); + p->setBrush(ba); + } else { + p->setPen(pu); + p->setBrush(bu); + } + //if (mark_in) {p->setPen(pa); p->setBrush(ba);} + //if (mark_out) {p->setPen(pr); p->setBrush(br);} + if (im_bus.isNull()) { + for (int i = 0; i < segments.size(); ++i) { + p->drawLine(pol[segments[i].first], pol[segments[i].second]); + } + } else { + QBrush br; + br.setTextureImage(im_bus); + for (int i = 0; i < segments.size(); ++i) { + QPointF sp(pol[segments[i].first]), ep(pol[segments[i].second]); + QTransform tf; + tf.translate(sp.x(), sp.y()); + tf.rotate(-QLineF(sp, ep).angle()); + tf.translate(0., -im_bus.height() / 2. * im_bus_scale); + tf.scale(im_bus_scale, im_bus_scale); + /* + br.setTransform(tf); + p->setPen(QPen(br, im_bus.height(), Qt::SolidLine, Qt::FlatCap, Qt::BevelJoin)); + p->drawLine(sp, ep); + */ + p->save(); + p->setTransform(tf, true); + p->setPen(Qt::NoPen); + p->setBrush(br); + //p->drawLine(QPointF(0., 0.), QPointF(QLineF(sp, ep).length(), 0.)); + p->drawRect(QRectF(0., 0., QLineF(sp, ep).length() / qMax(im_bus_scale, 1E-3), im_bus.height())); + p->restore(); + } + } + if (!im_end.isNull()) { + for (int i = 0; i < ends_ind.size(); ++i) { + QPointF sp = pol[ends_ind[i].first], ep = pol[ends_ind[i].second]; + QTransform tf; + tf.translate(sp.x(), sp.y()); + tf.rotate(-QLineF(sp, ep).angle()); + tf.translate(-pen_width, -im_end.height() / 2. * im_end_scale); + tf.scale(im_end_scale, im_end_scale); + p->save(); + p->setTransform(tf, true); + p->drawImage(0, 0, im_end); + p->restore(); + } + } + if (state_ >= 0) { + if (state_ == 0) { + pr.setWidthF(pen_width + 1.); + p->setPen(pr); + } else if (state_ == 1) { + pa.setWidthF(pen_width + 1.); + p->setPen(pa); + } + p->setOpacity(0.5); + for (int i = 0; i < segments.size(); ++i) { + p->drawLine(pol[segments[i].first], pol[segments[i].second]); + } + p->setOpacity(1.); + } + if (!im_bus.isNull() && (selPoint >= 0 || selSegment >= 0)) { + p->setPen(pa); + p->setBrush(ba); + for (int i = 0; i < segments.size(); ++i) { + p->drawLine(pol[segments[i].first], pol[segments[i].second]); + } + } + //if (mark_in) {p->setPen(pa); p->setBrush(ba);} + //if (mark_out) {p->setPen(pr); p->setBrush(br);} + if (selPoint >= 0) { + p->save(); + p->setPen(ph); + p->setBrush(bh); + p->translate(pol[selPoint]); + p->drawEllipse(QPointF(0, 0), point_size, point_size); + p->restore(); + } + if (selSegment >= 0) { + p->save(); + p->setPen(ph); + p->drawLine(pol[segments[selSegment].first], pol[segments[selSegment].second]); + p->restore(); + } +} + + +void BlockBusItem::setPointSize(double s) { + point_size = s; + update(); +} + + +QRectF BlockBusItem::boundingRect() const { + QPolygonF p(pol); + if (new_segment) p << new_end; + return enlargedRect(p.boundingRect(), 0, 0, 10.f); +} diff --git a/test/qad/blockview/blockbusitem.h b/test/qad/blockview/blockbusitem.h new file mode 100644 index 0000000..d453e31 --- /dev/null +++ b/test/qad/blockview/blockbusitem.h @@ -0,0 +1,143 @@ +#ifndef BLOCKBUSITEM_H +#define BLOCKBUSITEM_H + +#include "blockitem.h" + + +/// data: +/// 1002 - flag for move parent (true) +/// 1003 - flag for visualize selection (true) +/// 1004 - BlockItemPin ("pin") +/// 1005 - BlockBusItem ("connection") +/// 1006 - BlockItem ("item") +/// 1007 - BlockItem selection ("item_selection") +/// 1008 - item is NOT decor, ignore for function decors() (true) +/// 1009 - item is scene decor ("decor") +/// 1010 - BlockItem decor (src text for QGraphicsSimpleTextItem) +/// 1011 - item is BlockItem decor ("decor") +/// 1100 - flag for correct move (true) + + +class BlockBusItem: public QGraphicsObject, public PropertyStorage { + Q_OBJECT + Q_INTERFACES(QGraphicsItem) + Q_PROPERTY(double pointSize READ pointSize WRITE setPointSize DESIGNABLE false SCRIPTABLE false) + friend class BlockView; +public: + BlockBusItem(bool temp = false); + BlockBusItem(const BlockBusItem & other); + ~BlockBusItem() {;} + + void setGridStep(double gs) {grid_step = gs;} + void setEndpointsNumber(int num) {max_ep = num;} + void setImages(const QImage & bus, const QImage & end = QImage()) {im_bus = bus; im_end = end; update();} + void setBusImageScale(double s) {im_bus_scale = s; update();} + void setEndpointImageScale(double s) {im_end_scale = s; update();} + void setBusType(int type_) {bus_type = type_;} + void setBusName(const QString & name) {bus_name = name;} + int busType() const {return bus_type;} + QString busName() const {return bus_name;} + double busImageScale() const {return im_bus_scale;} + double endpointImageScale() const {return im_end_scale;} + void appendPoint(const QPointF & p); + void appendPoint(qreal x, qreal y); + void testPoint(QPointF pos, int * sel_point, int * sel_segment); + void clear(); + /*void setStart(const QPointF & p) {pol[0] = p; scene()->update();} + void setStart(qreal x, qreal y) {setStart(QPointF(x, y));} + void setFinish(const QPointF & p) {pol[pol.size() - 1] = p; scene()->update();} + void setFinish(qreal x, qreal y) {setFinish(QPointF(x, y));} + void setPoint(int index, const QPointF & p) {pol[index] = p; scene()->update();} + void setPoint(int index, qreal x, qreal y) {setPoint(index, QPointF(x, y));}*/ + void setPen(const QPen & p) {p_ = p; update();} + QPen pen() const {return p_;} + void setBrush(const QBrush & b) {b_ = b; update();} + QBrush brush() const {return b_;} + //void disconnectBrick() {BrickBase::disconnect(brickFrom, portFrom, brickTo, portTo);} + void movePolyline(const QPointF & dp); + void movePoint(int index, const QPointF & dp); + double width() const {return pen_width;} + void setWidth(const double & w); + void setColor(const QColor & c); + int addPoint(const QPointF & point, bool update = true); + int segmentPointPair(int point, int * seg = 0) const; + void removePoint(int index); + void removeSegment(int index); + void markAsInput(); + void markAsOutput(); + void unmark(); + void simplify(bool full = true); + void adjustLine(); + int endpointCount() const; + bool isBusSelected() const {return selSegment >= 0 || selPoint >= 0;} + QList connectedBlocks() const; + QList connectedPins() const; + + void setBusState(bool state); + bool busState() const {return state_ > 0;} + void clearBusState(); + + QByteArray save() const; + void load(const QByteArray & data); + BlockBusItem * copy() const; + + void saveState(); + void restoreState(); + + enum {Type = UserType + 2}; + +protected: + void _init(); + void reconnect(); + void updateGeometry(); + bool checkDelete(); + void emitAction(BlockItemBase::Action a); + QVector endpoints() const; + QVector endpointLine(int ep, double angle) const; + int pointSegmentsCount(int point, QList * segs = 0) const; + int neighborSegmentPoint(int point, int * seg) const; + int type() const {return Type;} + QRectF boundingRect() const; + bool sceneEvent(QEvent * e); + void hoverEnterEvent(QGraphicsSceneHoverEvent * e); + void hoverMoveEvent(QGraphicsSceneHoverEvent * e); + void hoverLeaveEvent(QGraphicsSceneHoverEvent * e); + void mousePressEvent(QGraphicsSceneMouseEvent * e); + void mouseMoveEvent(QGraphicsSceneMouseEvent * e); + void mouseReleaseEvent(QGraphicsSceneMouseEvent * e); + void paint(QPainter * p, const QStyleOptionGraphicsItem * o, QWidget * w = 0); + + QPointF lp, new_start, new_end, press_pos, qp; + QPen p_, ph, pu, pa, pr, pn; + QBrush b_, bh, bu, ba, br; + QString tt, bus_name; + QList > segments, ends_ind, segments_s, ends_ind_s; + QMap connections_; + QVector ends, ends_s; + QImage im_bus, im_end; + QPolygonF pol, bpol, pol_s; + Qt::KeyboardModifiers mm_mods; + bool temp_; + double pen_width, grid_step, im_bus_scale, im_end_scale; + int selPoint, selSegment, max_ep, bus_type, state_; + bool moved, deleted, mark_in, mark_out, new_segment, mm_cancel, lm_point; + +private: + double pointSize() const {return point_size;} + void setPointSize(double s); + + double point_size; + QPropertyAnimation anim_point_size; +}; + + +inline QDataStream & operator <<(QDataStream & s, const BlockBusItem * b) {s << b->save(); return s;} +inline QDataStream & operator >>(QDataStream & s, BlockBusItem *& b) { + QByteArray ba; s >> ba; + b = new BlockBusItem(); + b->load(ba); + return s; +} + + +#endif // BLOCKBUSITEM_H diff --git a/test/qad/blockview/blockeditor.cpp b/test/qad/blockview/blockeditor.cpp new file mode 100644 index 0000000..f2fa148 --- /dev/null +++ b/test/qad/blockview/blockeditor.cpp @@ -0,0 +1,389 @@ +#include "blockeditor.h" +#include "ui_blockeditor.h" +#include "drawtools.h" +#include "blockview.h" +#include +#include +#include +#include +#include + + +BlockEditor::BlockEditor(QWidget *parent) : QWidget(parent), ui(new Ui::BlockEditor) { + init = false; + m_editorMode = false; + m_pinsEditable = true; + ui->setupUi(this); + src_title = windowTitle(); + connect(ui->blockView->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + block.setFlags(0); + ui->blockView->addItem(&block); + ui->blockView->viewport()->installEventFilter(this); + DrawTools * drawtools = new DrawTools(ui->blockView); + connect(drawtools, SIGNAL(itemCreated(QGraphicsItem*)), this, SLOT(addItem(QGraphicsItem*))); + drawtools->textEditCombo()->addItems(QStringList() << "%name" << "%value" << "%id"); + ui->layoutProperties->addWidget(drawtools->propertyWidget()); + ui->actionRemove_items->setEnabled(false); + ui->button_color->setColor(Qt::lightGray); + ui->treePins->setItemDelegateForColumn(1, new PinBusDelegate()); + connect(ui->treePins, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(updateBlock())); + ui->treePins->viewport()->installEventFilter(this); + + QToolBar * bar; + bar = new QToolBar(ui->widgetBar); + bar->setOrientation(Qt::Vertical); + bar->addActions(drawtools->actionsForAdd()); + ui->widgetBar->setMinimumSize(bar->sizeHint()); + + bar = new QToolBar(ui->widgetBarZ); + bar->setOrientation(Qt::Vertical); + bar->addActions(drawtools->actionsForZ()); + bar->addSeparator(); + bar->addActions(QList() << ui->actionRemove_items); + ui->widgetBarZ->setMinimumSize(bar->sizeHint()); + init = true; + on_buttonClear_clicked(); +} + + +BlockEditor::~BlockEditor() { + init = false; + delete ui; +} + + +void BlockEditor::loadFile(QString path) { + if (path.isEmpty()) return; + QFile f(path); + if (f.open(QIODevice::ReadOnly)) { + cur_file = path; + loadModel(f.readAll()); + QDir::setCurrent(QFileInfo(path).dir().path()); + } + setWindowTitle(src_title + QString(" - %1").arg(QFileInfo(path).baseName())); +} + + +void BlockEditor::loadModel(const QByteArray &model) { + BlockItem b; + b.loadModel(model); + ui->spin_w->setValue(b.width()); + ui->spin_h->setValue(b.height()); + ui->spin_margin->setValue(b.pinsMargin()); + ui->button_color->setColor(b.color()); + block.loadModel(model); + treePinsClear(); + ui->treePins->blockSignals(true); + QVector pins = block.pins(); + foreach (BlockItemPin * p, pins) { + QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << p->text() << QString::number(p->busType())); + ti->setData(0, Qt::UserRole, qulonglong(p)); + ti->setData(0, Qt::UserRole + 1, (int)p->alignment()); + ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled); + pin_tli[p->alignment()]->addChild(ti); + } + foreach (QGraphicsItem * i, block.decors()) { + i->setData(1002, false); + i->setData(1100, true); + } + ui->treePins->blockSignals(false); +} + + +QByteArray BlockEditor::saveModel() { + return block.saveModel(); +} + + +void BlockEditor::setEditorMode(bool editorMode) { + m_editorMode = editorMode; + ui->buttonClear->setVisible(!editorMode); + ui->buttonSave->setVisible(!editorMode); + ui->buttonSaveAs->setVisible(!editorMode); + ui->buttonLoad->setVisible(!editorMode); +} + + +void BlockEditor::setPinsEditable(bool pinsEditable) { + m_pinsEditable = pinsEditable; + ui->treePins->setVisible(pinsEditable); + ui->buttonPinAdd->setVisible(pinsEditable); + ui->buttonPinClear->setVisible(pinsEditable); + ui->buttonPinDelete->setVisible(pinsEditable); + ui->buttonPinDup->setVisible(pinsEditable); + ui->groupPins->setVisible(pinsEditable); +} + + +void BlockEditor::selectionChanged() { + if (!init) return; + ui->actionRemove_items->setEnabled(!ui->blockView->scene()->selectedItems().isEmpty()); +} + + +void BlockEditor::addItem(QGraphicsItem *item) { + block.addDecor(item); + item->setData(1002, false); + item->setData(1100, true); +} + + +void BlockEditor::updateBlock() { + block.setSize(ui->spin_w->value(), ui->spin_h->value()); + block.setColor(ui->button_color->color()); + block.setPinsMargin(ui->spin_margin->value()); +} + + +void BlockEditor::treePinsClear() { + ui->treePins->blockSignals(true); + ui->treePins->clear(); + QFont bf(font()); bf.setBold(true); + QList al = QList() << Qt::AlignLeft << Qt::AlignRight << Qt::AlignTop << Qt::AlignBottom; + QStringList an = QStringList() << "Left" << "Right" << "Top" << "Bottom"; + pin_tli.clear(); + for (int i = 0; i < al.size(); ++i) { + QTreeWidgetItem * ti = new QTreeWidgetItem(); + ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDropEnabled); + ti->setData(0, Qt::UserRole, al[i]); + ti->setText(0, an[i]); + ti->setFont(0, bf); + ui->treePins->addTopLevelItem(ti); + ti->setFirstColumnSpanned(true); + ti->setExpanded(true); + pin_tli[al[i]] = ti; + } + ui->treePins->blockSignals(false); +} + + +bool BlockEditor::eventFilter(QObject *o, QEvent *e) { + if (!init) QWidget::eventFilter(o, e); + if (o == ui->treePins->viewport()) { + if (e->type() == QEvent::Drop) { + QTimer::singleShot(0, this, SLOT(arrangePins())); + } + } + if (o == ui->blockView->viewport()) { + if (e->type() == QEvent::Resize) { + ui->blockView->centerOn(&block); + } + } + return QWidget::eventFilter(o, e); +} + + +void BlockEditor::on_actionRemove_items_triggered() { + QList si = ui->blockView->scene()->selectedItems(); + foreach (QGraphicsItem * i, si) + block.removeDecor(i); +} + + +void BlockEditor::on_buttonSave_clicked() { + if (cur_file.isEmpty()) { + on_buttonSaveAs_clicked(); + return; + } + QFile f(cur_file); + if (f.open(QIODevice::WriteOnly)) { + f.write(saveModel()); + //setWindowTitle(src_title + QString(" - %1").arg(QFileInfo(c).baseName())); + } +} + + +void BlockEditor::on_buttonSaveAs_clicked() { + QString c = QFileDialog::getSaveFileName(this, "Save block model to file", cur_file, "*.blockmodel"); + if (!c.isEmpty()) { + QFile f(c); + if (f.open(QIODevice::WriteOnly)) { + cur_file = c; + f.write(saveModel()); + setWindowTitle(src_title + QString(" - %1").arg(QFileInfo(c).baseName())); + } + } +} + + +void BlockEditor::on_buttonLoad_clicked() { + QString c = QFileDialog::getOpenFileName(this, "Save block model to file", cur_file, "*.blockmodel"); + loadFile(c); +} + + +void BlockEditor::on_buttonClear_clicked() { + BlockItem b; + ui->spin_w->setValue(b.width()); + ui->spin_h->setValue(b.height()); + ui->spin_margin->setValue(b.pinsMargin()); + ui->button_color->setColor(b.color()); + block.loadModel(QByteArray()); + treePinsClear(); +} + + + +void BlockEditor::on_buttonPinAdd_clicked() { + ui->treePins->blockSignals(true); + QTreeWidgetItem * ti = new QTreeWidgetItem(QStringList() << "" << "0"); + ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled); + ti->setData(0, Qt::UserRole, qulonglong(block.addPin(Qt::AlignLeft, ti->text(1).toInt(), ti->text(0)))); + ti->setData(0, Qt::UserRole + 1, (int)Qt::AlignLeft); + pin_tli[Qt::AlignLeft]->addChild(ti); + ui->treePins->setCurrentItem(ti); + ui->treePins->blockSignals(false); + updateBlock(); +} + + +void BlockEditor::on_buttonPinDup_clicked() { + QTreeWidgetItem * ci = ui->treePins->currentItem(); + if (ci == 0) return; + ui->treePins->blockSignals(true); + QTreeWidgetItem * ti = ci->clone(); + ti->setText(0, ti->text(0)); + ti->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled); + Qt::Alignment al = (Qt::Alignment)ci->data(0, Qt::UserRole + 1).toInt(); + ti->setData(0, Qt::UserRole, qulonglong(block.addPin(al, ci->text(1).toInt(), ci->text(0)))); + ti->setData(0, Qt::UserRole + 1, (int)al); + pin_tli[al]->addChild(ti); + ui->treePins->setCurrentItem(ti); + ui->treePins->blockSignals(false); +} + + +void BlockEditor::on_buttonPinDelete_clicked() { + ui->treePins->blockSignals(true); + QList si = ui->treePins->selectedItems(); + foreach (QTreeWidgetItem * i, si) { + if (!i->parent()) continue; + block.removePin((BlockItemPin*)(i->data(0, Qt::UserRole).toLongLong())); + delete i; + } + ui->treePins->blockSignals(false); +} + + +void BlockEditor::on_buttonPinClear_clicked() { + treePinsClear(); + block.clearPins(); +} + + +void BlockEditor::on_treePins_itemChanged(QTreeWidgetItem * item, int column) { + if (!item) return; + BlockItemPin * pin = (BlockItemPin*)item->data(0, Qt::UserRole).toULongLong(); + if (!pin) return; + switch (column) { + case 0: + ui->treePins->blockSignals(true); + item->setText(0, item->text(0)); + pin->setText(item->text(0)); + ui->treePins->blockSignals(false); + break; + case 1: pin->setBusType(item->text(1).toInt()); break; + }; +} + + + +void BlockEditor::arrangePins() { + QVector pins = block.pins(); +// block.clearPins(); + QList tli = pin_tli.values(); + foreach (QTreeWidgetItem * ti, tli) { + for (int i = 0; i < ti->childCount(); ++i) { + foreach (BlockItemPin * p, pins) + if (p == (BlockItemPin*)(ti->child(i)->data(0, Qt::UserRole).toULongLong())) { + p->setAlignment((Qt::Alignment)ti->data(0, Qt::UserRole).toInt()); + BlockItemPin * np = block.addPin(p, false); + ti->child(i)->setData(0, Qt::UserRole, qulonglong(np)); + ti->child(i)->setData(0, Qt::UserRole + 1, ti->data(0, Qt::UserRole).toInt()); + break; + } + } + } + +// for (int i = 0; i < ui->treePins->topLevelItemCount(); ++i) { +// QTreeWidgetItem * ti = ui->treePins->topLevelItem(i); +// if (tli.contains(ti)) continue; +// ti = ui->treePins->takeTopLevelItem(i); +// pin_tli[ti->data(0, Qt::UserRole + 1).toInt()]->addChild(ti); +// foreach (BlockItemPin * p, pins) +// if (p->text() == ti->text(0)) { +// p->setAlignment((Qt::Alignment)ti->data(0, Qt::UserRole + 1).toInt()); +// block.addPin(p, false); +// break; +// } +// } + block.arrangePins(); +} + + +/// *********************************************************** +/// *********************************************************** +/// *********************************************************** +/// *********************************************************** + + +QWidget * PinAlignDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QComboBox * combo = new QComboBox(parent); + int cv = index.data().toInt(); + combo->addItem("Left", int(Qt::AlignLeft)); if (cv == Qt::AlignLeft) combo->setCurrentIndex(0); + combo->addItem("Right", int(Qt::AlignRight)); if (cv == Qt::AlignRight) combo->setCurrentIndex(1); + combo->addItem("Top", int(Qt::AlignTop)); if (cv == Qt::AlignTop) combo->setCurrentIndex(2); + combo->addItem("Bottom", int(Qt::AlignBottom)); if (cv == Qt::AlignBottom) combo->setCurrentIndex(3); + combo->setGeometry(option.rect); + return combo; +} + + +QString PinAlignDelegate::displayText(const QVariant & value, const QLocale & locale) const { + int cv = value.toInt(); + switch (cv) { + case Qt::AlignLeft: return "Left"; break; + case Qt::AlignRight: return "Right"; break; + case Qt::AlignTop: return "Top"; break; + case Qt::AlignBottom: return "Bottom"; break; + } + return "unknown"; +} + + +void PinAlignDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + model->setData(index, ((QComboBox*)editor)->itemData(((QComboBox*)editor)->currentIndex()).toInt()); +} + + +/// *********************************************************** +/// *********************************************************** +/// *********************************************************** +/// *********************************************************** + + +QWidget * PinBusDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QSpinBox * spin = new QSpinBox(parent); + spin->setMinimum(-2147483648); + spin->setMaximum(2147483647); + spin->setValue(index.data().toInt()); + return spin; +} + + +QString PinBusDelegate::displayText(const QVariant & value, const QLocale & locale) const { + int cv = value.toInt(); + return QString::number(cv); +} + + +void PinBusDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + model->setData(index, ((QSpinBox*)editor)->value()); +} + + + +//void BlockEditor::on_treePins_itemSelectionChanged() { +// arrangePins(); +// qDebug() << "111111111111111"; +//} diff --git a/test/qad/blockview/blockeditor.h b/test/qad/blockview/blockeditor.h new file mode 100644 index 0000000..c2d3f71 --- /dev/null +++ b/test/qad/blockview/blockeditor.h @@ -0,0 +1,91 @@ +#ifndef BLOCKEDITOR_H +#define BLOCKEDITOR_H + +#include +#include +#include +#include "blockitem.h" + + +namespace Ui { +class BlockEditor; +} + + +class BlockEditor : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool editorMode READ editorMode WRITE setEditorMode) + Q_PROPERTY(bool pinsEditable READ pinsEditable WRITE setPinsEditable) +public: + explicit BlockEditor(QWidget *parent = 0); + ~BlockEditor(); + + + bool editorMode() const {return m_editorMode;} + bool pinsEditable() const {return m_pinsEditable;} + +public slots: + void loadFile(QString path); + void loadModel(const QByteArray & model); + QByteArray saveModel(); + void setEditorMode(bool editorMode); + void setPinsEditable(bool pinsEditable); + +private slots: + void selectionChanged(); + void addItem(QGraphicsItem * item); + void updateBlock(); + void treePinsClear(); + void arrangePins(); + void on_actionRemove_items_triggered(); + void on_buttonSave_clicked(); + void on_buttonSaveAs_clicked(); + void on_buttonLoad_clicked(); + void on_buttonClear_clicked(); + void on_buttonPinAdd_clicked(); + void on_buttonPinDup_clicked(); + void on_buttonPinDelete_clicked(); + void on_buttonPinClear_clicked(); + void on_treePins_itemChanged(QTreeWidgetItem *item, int column); +// void on_treePins_itemSelectionChanged(); + +private: + bool eventFilter(QObject * o, QEvent * e); + + Ui::BlockEditor *ui; + QMap pin_tli; + BlockItem block; + QString src_title, cur_file; + bool init; + bool m_editorMode; + bool m_pinsEditable; +}; + + + +class PinAlignDelegate: public QStyledItemDelegate { + Q_OBJECT +public: + PinAlignDelegate(QObject * parent = 0): QStyledItemDelegate(parent) {} + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const; + QString displayText(const QVariant & value, const QLocale & locale) const; + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const {return QSize(60, 26);} +}; + + +class PinBusDelegate: public QStyledItemDelegate { + Q_OBJECT +public: + PinBusDelegate(QObject * parent = 0): QStyledItemDelegate(parent) {} + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const; + QString displayText(const QVariant & value, const QLocale & locale) const; + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const {return QSize(60, 26);} +private: + typedef QPair ISPair; + QVector buses; +}; + +#endif // BLOCKEDITOR_H diff --git a/test/qad/blockview/blockeditor.ui b/test/qad/blockview/blockeditor.ui new file mode 100644 index 0000000..6ff5ad3 --- /dev/null +++ b/test/qad/blockview/blockeditor.ui @@ -0,0 +1,543 @@ + + + BlockEditor + + + + 0 + 0 + 847 + 810 + + + + Block editor + + + + :/icons/blockview.png:/icons/blockview.png + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + Block parameters + + + Qt::AlignCenter + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Width: + + + + + + + 1000 + + + 20 + + + 100 + + + + + + + Heigth: + + + + + + + 1000 + + + 20 + + + 60 + + + + + + + Pins margin: + + + + + + + 100 + + + 10 + + + 10 + + + + + + + Color: + + + + + + + + + + + Pins + + + Qt::AlignCenter + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 200 + + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + 200 + + + + Name + + + + + Bus + + + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Clone + + + + :/icons/edit-copy.png:/icons/edit-copy.png + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Remove selected + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Remove all + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + + + 0 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + + + + + + + + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + false + + + true + + + + + + + + + + + + + + + Save + + + + :/icons/document-save.png:/icons/document-save.png + + + Ctrl+S + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 10 + 20 + + + + + + + + Save as ... + + + + :/icons/document-save.png:/icons/document-save.png + + + Ctrl+S + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Load ... + + + + :/icons/document-open.png:/icons/document-open.png + + + Ctrl+O + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clear + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + Remove items + + + Del + + + + + + BlockView + QGraphicsView +
blockview.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+
+ + + + + + + + spin_w + valueChanged(int) + BlockEditor + updateBlock() + + + 170 + 21 + + + 254 + 1 + + + + + spin_h + valueChanged(int) + BlockEditor + updateBlock() + + + 250 + 49 + + + 373 + 5 + + + + + spin_margin + valueChanged(int) + BlockEditor + updateBlock() + + + 350 + 90 + + + 492 + 8 + + + + + button_color + colorChanged(QColor) + BlockEditor + updateBlock() + + + 561 + 115 + + + 613 + 6 + + + + + + updateBlock() + +
diff --git a/test/qad/blockview/blockeditor/CMakeLists.txt b/test/qad/blockview/blockeditor/CMakeLists.txt new file mode 100644 index 0000000..5a4f907 --- /dev/null +++ b/test/qad/blockview/blockeditor/CMakeLists.txt @@ -0,0 +1,26 @@ +project(blockeditor) +find_qt(${QtVersions} Core Gui Widgets) +qt_sources(SRC) +qt_wrap(${SRC} CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets qad_blockview) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/qad/blockview/blockeditor/blockeditor_main.cpp b/test/qad/blockview/blockeditor/blockeditor_main.cpp new file mode 100644 index 0000000..0a6a3a7 --- /dev/null +++ b/test/qad/blockview/blockeditor/blockeditor_main.cpp @@ -0,0 +1,16 @@ +#include +#include "blockeditor.h" + + +int main(int argc, char * argv[]) { + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + a.setWindowIcon(QIcon(":/icons/blockview.png")); + BlockEditor w; + if (a.arguments().size() > 1) + w.loadFile(a.arguments().back()); + w.show(); + return a.exec(); +} diff --git a/test/qad/blockview/blockeditor/icons/blockview.ico b/test/qad/blockview/blockeditor/icons/blockview.ico new file mode 100644 index 0000000..90d261e Binary files /dev/null and b/test/qad/blockview/blockeditor/icons/blockview.ico differ diff --git a/test/qad/blockview/blockeditor/resource_win_icon.rc b/test/qad/blockview/blockeditor/resource_win_icon.rc new file mode 100644 index 0000000..09b2a10 --- /dev/null +++ b/test/qad/blockview/blockeditor/resource_win_icon.rc @@ -0,0 +1,43 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif + +1 ICON icons/blockview.ico + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG +FILEFLAGS 0x9L +#else +FILEFLAGS 0x8L +#endif +FILEOS 0x40004L +FILETYPE 0x2L +FILESUBTYPE 0x0L +BEGIN +BLOCK "StringFileInfo" +BEGIN +BLOCK "040904b0" +BEGIN +VALUE "Comments", "\0" +VALUE "CompanyName", "BMSTU SM5\0" +VALUE "FileDescription", "Block Editor\0" +VALUE "FileVersion", "1,0,0,0\0" +VALUE "InternalName", "Block Editor\0" +VALUE "LegalTrademarks", "\0" +VALUE "OriginalFilename", "blockeditor.exe\0" +VALUE "PrivateBuild", "1\0" +VALUE "ProductName", "Block Editor\0" +VALUE "ProductVersion", "1, 0, 0, 0\0" +VALUE "SpecialBuild", "\0" +END +END +BLOCK "VarFileInfo" +BEGIN +VALUE "Translation", 0x409, 1200 +END +END diff --git a/test/qad/blockview/blockitem.cpp b/test/qad/blockview/blockitem.cpp new file mode 100644 index 0000000..c6d9854 --- /dev/null +++ b/test/qad/blockview/blockitem.cpp @@ -0,0 +1,548 @@ +#include "blockview.h" +#include + + +#define BLOCKITEM_DEFAULT_PIN_MARGIN 20 + +BlockItem::BlockItem(QGraphicsItem * parent): QGraphicsObject(parent), PropertyStorage(), +g_main(this), g_selection(this) { + setData(1006, "item"); + setZValue(2.); + setAcceptHoverEvents(true); + setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); + g_main.setData(1002, true); + g_selection.setData(1007, "item_selection"); + g_selection.setAcceptedMouseButtons(0); + g_selection.setZValue(10.); + g_selection.hide(); + g_selection.setData(1003, true); + col = Qt::lightGray; + _resize(QSizeF(100., 60.)); + QPen p(QColor(128, 128, 255), lineThickness(), Qt::DotLine); + p.setCosmetic(true); + g_selection.setPen(p); + g_selection.setBrush(QColor(128, 128, 255, 32)); + pins_margin = BLOCKITEM_DEFAULT_PIN_MARGIN; + anim_thick.setTargetObject(this); + anim_thick.setPropertyName("_thickness"); + anim_thick.setEasingCurve(QEasingCurve::OutQuad); + anim_thick.setDuration(300); + anim_sel.setTargetObject(this); + anim_sel.setPropertyName("_selRect"); + anim_sel.setEasingCurve(QEasingCurve::OutCubic); + anim_sel.setDuration(400); + t_sel.start(); + //g_main.setBrush(QColor(128, 128, 128, 64)); + /* + BlockItemPin * pin = new BlockItemPin(Qt::AlignRight, 0, this); + pin->setText("text"); + pin->setBusType(0); + addPin(pin); + pin = new BlockItemPin(Qt::AlignTop, this); + pin->setBusType(0); addPin(pin); + pin = new BlockItemPin(Qt::AlignLeft, this); + pin->setBusType(1); addPin(pin); + pin = new BlockItemPin(Qt::AlignRight, this); + pin->setBusType(1); addPin(pin);*/ +} + + +BlockItem::~BlockItem() { + clearDecors(); +} + + +void BlockItem::_resize(QSizeF s) { + g_main.setRect(QRectF(QPointF(), s)); + g_main.setPos(QPointF(-g_main.rect().center().x(), -10.)); + g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 8)); + g_selection.setPos(g_main.pos()); + QRadialGradient g(g_main.rect().width() / 2., 0, qMax(g_main.rect().width() / 2., g_main.rect().height())); + g.setSpread(QGradient::PadSpread); + g.setCoordinateMode(QGradient::LogicalMode); + g.setColorAt(0., col.darker(120.)); + g.setColorAt(1., col.lighter(120.)); + g_main.setBrush(QBrush(g)); + prepareGeometryChange(); + arrangePins(); +} + + +void BlockItem::_moveToTop(bool only_decors) { + qreal dy = -g_main.rect().center().y() + 10; + if (!only_decors) + moveBy(0., dy); + foreach (QGraphicsItem * d, decors_) + d->moveBy(0., -dy); + +} + + +BlockItemPin * BlockItem::addPin(BlockItemPin * pin, bool update_) { + pin->setParentItem(this); + if (!pins_[pin->alignment()].contains(pin)) pins_[pin->alignment()] << pin; + pin->parent_ = this; + if (update_) + arrangePins(); + return pin; +} + + +BlockItemPin * BlockItem::addPin(Qt::Alignment align, int bus_type, const QString & text, bool update_) { + BlockItemPin * pin = new BlockItemPin(align, bus_type, text, this); + pin->parent_ = this; + pins_[pin->alignment()] << pin; + if (update_) + arrangePins(); + return pin; +} + + +void BlockItem::removePin(BlockItemPin * pin) { + if (!pin) return; + QMutableMapIterator > it(pins_); + while (it.hasNext()) { + it.next(); + QVector & pv(it.value()); + if (pv.contains(pin)) + pv.remove(it.value().indexOf(pin)); + } + delete pin; + arrangePins(); +} + + +void BlockItem::addDecor(QGraphicsItem * item) { + if (decors_.contains(item)) return; + if (qgraphicsitem_cast(item)) + qgraphicsitem_cast(item)->setTransformationMode(Qt::SmoothTransformation); + if (qgraphicsitem_cast(item)) + qgraphicsitem_cast(item)->setData(1010, qgraphicsitem_cast(item)->text()); + if (qgraphicsitem_cast(item)) + qgraphicsitem_cast(item)->setData(1010, qgraphicsitem_cast(item)->text()); + item->setData(1002, true); + item->setData(1011, "decor"); + decors_ << item; + item->setParentItem(this); +} + + +void BlockItem::addDecor(QGraphicsItem & item) { + if (decors_.contains(&item)) return; + if (qgraphicsitem_cast(&item)) + qgraphicsitem_cast(&item)->setTransformationMode(Qt::SmoothTransformation); + if (qgraphicsitem_cast(&item)) + qgraphicsitem_cast(&item)->setData(1010, qgraphicsitem_cast(&item)->text()); + if (qgraphicsitem_cast(&item)) + qgraphicsitem_cast(&item)->setData(1010, qgraphicsitem_cast(&item)->text()); + item.setData(1002, true); + item.setData(1011, "decor"); + //decors_ << &item; + item.setParentItem(this); +} + + +void BlockItem::removeDecor(QGraphicsItem * item) { + if (scene() && item) + scene()->sendEvent(item, new QGraphicsSceneEvent(QEvent::Close)); + decors_.removeAll(item); + delete item; +} + + +QVector BlockItem::takePins() { + QVector ret = pins(); + pins_.clear(); + return ret; +} + + +void BlockItem::clearPins() { + QList > mp = pins_.values(); + for (int i = 0; i < mp.size(); ++i) + qDeleteAll(mp[i]); + pins_.clear(); +} + + +void BlockItem::clearDecors() { + bool pbs = false; + if (scene()) pbs = scene()->blockSignals(true); + if (scene()) + foreach (QGraphicsItem * i, decors_) + scene()->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); + qDeleteAll(decors_); + decors_.clear(); + if (scene()) { + scene()->blockSignals(pbs); + QMetaObject::invokeMethod(scene(), "selectionChanged"); + } +} + + +QVector BlockItem::pins() const { + QList > mp = pins_.values(); + QVector ret; + for (int i = 0; i < mp.size(); ++i) + ret << mp[i]; + return ret; +} + + +QByteArray BlockItem::saveModel() { + /*QDataStream s(&ret, QIODevice::ReadWrite); + QVector ps = pins(); + s << pos() << rotation() << size() << color() << ps.size(); + foreach (BlockItemPin * p, ps) + s << int(p->alignment()) << p->busType() << p->text() << p->devices(); + s << decors_.size(); + foreach (QGraphicsItem * i, decors_) + s << i;*/ + ChunkStream cs; + cs << cs.chunk(1, pos()) << cs.chunk(2, rotation()) << cs.chunk(3, size()) << cs.chunk(4, color()) << cs.chunk(5, pins()) + << cs.chunk(6, decors_) << cs.chunk(7, pins_margin) << cs.chunk(0xFF, _blockitem_current_version_); + return cs.data(); +} + + +void BlockItem::loadModel(const QByteArray & data) { + //qDebug() << "load from" << data.size() << "bytes"; + clearPins(); + clearDecors(); + col = Qt::lightGray; + _resize(QSizeF(100., 60.)); + if (data.isEmpty()) return; + /*QDataStream s(data); + {QPointF _v; s >> _v;} + {qreal _v; s >> _v;} + {QSizeF _v; s >> _v; setSize(_v);} + {QColor _v; s >> _v; setColor(_v);} + int _s; s >> _s; + for (int i = 0; i < _s; ++i) { + BlockItemPin * pin = new BlockItemPin(); + {int _v; s >> _v; pin->setAlignment((Qt::Alignment)_v);} + {int _v; s >> _v; pin->setBusType(_v);} + {QString _v; s >> _v; pin->setText(_v);} + {QMap _v; s >> _v; pin->setDevices(_v);} + addPin(pin); + } + s >> _s; + for (int i = 0; i < _s; ++i) { + if (s.atEnd()) break; + QGraphicsItem * ni = 0; + s >> ni; + if (ni) { + addDecor(ni); + } + }*/ + ChunkStream cs(data); + QVector tp; + QList dl; + int version = -1; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: /*setPos(cs.getData());*/ break; + case 2: /*setRotation(cs.getData());*/ break; + case 3: setSize(cs.getData()); break; + case 4: setColor(cs.getData()); break; + case 5: + cs.get(tp); + foreach (BlockItemPin * p, tp) addPin(p); + break; + case 6: + cs.get(dl); + foreach (QGraphicsItem * d, dl) addDecor(d); + break; + case 7: setPinsMargin(cs.getData()); break; + case 0xFF: cs.get(version); break; + } + } + if (version <= 0) + _moveToTop(true); +} + + +QByteArray BlockItem::save() const { + ChunkStream cs; + QMap > pp; + foreach (BlockItemPin * p, pins()) { + //qDebug() << "save pin" << p->text() << "->" << p->properties().size(); + pp[p->text()] = p->properties(); + } + cs << cs.chunk(1, pos()) << cs.chunk(2, rotation()) << cs.chunk(3, props) << cs.chunk(5, pp) << cs.chunk(6, size()); + cs << cs.chunk(10, data(2000)) << cs.chunk(11, data(2001)) << cs.chunk(12, prop_bindings) << cs.chunk(0xFF, _blockitem_current_version_); + return cs.data(); +} + + +void BlockItem::load(const QByteArray & data) { + if (data.isEmpty()) return; + ChunkStream cs(data); + QMap > _p; + int version = -1; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: setPos(cs.getData()); break; + case 2: setRotation(cs.getData()); break; + case 3: cs.get(props); break; + case 5: + cs.get(_p); + //qDebug() << "load pins" << _p.size(); + foreach (BlockItemPin * p, pins()) { + //qDebug() << "load pin" << p->text() << "->" << _p.contains(p->text()); + if (_p.contains(p->text())) + p->properties() = _p[p->text()]; + } + break; + case 6: setSize(cs.getData()); break; + case 10: setData(2000, cs.getData()); break; + case 11: setData(2001, cs.getData()); break; + case 12: prop_bindings = cs.getData > >(); break; + case 0xFF: cs.get(version); break; + } + } + if (version <= 0) { + _moveToTop(); + } +} + + +BlockItem * BlockItem::copy() const { + BlockItem * ret = new BlockItem(); + ret->setPos(pos()); + ret->setSize(size()); + ret->setColor(color()); + ret->setSelected(false); + ret->props = props; + ret->setPinsMargin(pinsMargin()); + QVector mp = pins(); + foreach (BlockItemPin * p, mp) { + BlockItemPin * np = new BlockItemPin(); + np->setBusType(p->busType()); + np->setAlignment(p->alignment()); + np->setText(p->text()); + np->setToolTip(p->toolTip()); + np->properties() = p->properties(); + ret->addPin(np); + } + QByteArray ba; + foreach (QGraphicsItem * i, decors_) { + ba.clear(); + QGraphicsItem * ni = 0; + QDataStream s(&ba, QIODevice::ReadWrite); s << i; + QDataStream s2(ba); s2 >> ni; + if (ni) + ret->addDecor(ni); + } + return ret; +} + + +QList BlockItem::connectedBuses() const { + QList ret; + foreach (BlockItemPin * p, pins()) + ret << p->connectedBuses(); + return ret; +} + + +BlockItemPin * BlockItem::pinByText(const QString & t) const { + foreach (BlockItemPin * p, pins()) + if (p->text() == t) + return p; + return 0; +} + + +BlockItemPin * BlockItem::pinAtBus(BlockBusItem * bus) const { + if (bus == 0) return 0; + foreach (BlockItemPin * p, pins()) + if (p->connectedBuses().contains(bus)) + return p; + return 0; +} + + +QRectF BlockItem::sceneRect() const { + return g_main.mapRectToScene(g_main.boundingRect()); +} + + +QRectF BlockItem::boundingRect() const { + return g_main.mapRectToParent(g_main.boundingRect()); +} + + +void BlockItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { + //QGraphicsItem::mouseMoveEvent(event); +} + + +void BlockItem::hoverEnterEvent(QGraphicsSceneHoverEvent * e) { + bool anim = ((BlockView *)scene()->views().back())->isBlockAnimationEnabled(); + if (anim) { + anim_thick.stop(); + anim_thick.setStartValue(thickness()); + anim_thick.setEndValue(2.5); + anim_thick.start(); + } else setThickness(2.5); + emit blockHoverEnter(this); +} + + +void BlockItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) { + bool anim = ((BlockView *)scene()->views().back())->isBlockAnimationEnabled(); + if (anim) { + anim_thick.stop(); + anim_thick.setStartValue(thickness()); + anim_thick.setEndValue(1); + anim_thick.start(); + } else setThickness(1); + emit blockHoverLeave(this); +} + + +#define _POS(m) (i - ((cp.size() - 1) / 2)) * m + +void BlockItem::arrangePins() { + //double w = g_main.rect().width(), h = g_main.rect().height(); + QVector pl = pins(); +// pl = pl.fromList(pins().toList()); + pins_.clear(); + foreach (BlockItemPin * p, pl) + pins_[p->alignment()] << p; + QVector cp = pins_.value(Qt::AlignBottom); + for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(_POS(pins_margin), bottom()); + cp = pins_.value(Qt::AlignTop); + for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(_POS(pins_margin), top()); + cp = pins_.value(Qt::AlignLeft); + for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(left(), i * pins_margin); + cp = pins_.value(Qt::AlignRight); + for (int i = 0; i < cp.size(); ++i) cp[i]->setPos(right(), i * pins_margin); +} + +#undef _POS + + +void BlockItem::removeBindings(const QString & bind_name) { + for(int i=0; i pb(prop_name, bind_name); + prop_bindings.removeAll(pb); + prop_bindings << pb; +} + + +void BlockItem::applyBinding(const QString & bind_name, const QVariant & bind_value) { + for(int i=0; i > & bindings) { + prop_bindings = bindings; +} + + +QList > BlockItem::getBindings() { + return prop_bindings; +} + + +QString BlockItem::getBindName(const QString & prop_name) const { + for(int i=0; iviews().back())->isBlockAnimationEnabled() && t_sel.elapsed() > 50) { + g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 16)); + anim_sel.setStartValue(selectionRect()); + anim_sel.setEndValue(enlargedRect(g_main.rect(), 0, 0, 8)); + anim_sel.start(); + } + t_sel.restart(); + g_selection.setVisible(value.toBool()); + } + return QGraphicsItem::itemChange(change, value); +} + + +double BlockItem::thickness() const { + return g_main.pen().widthF(); +} + + +void BlockItem::setThickness(double w) { + QPen p = g_main.pen(); + p.setWidthF(w); + g_main.setPen(p); +} + + +QRectF BlockItem::selectionRect() const { + return g_selection.rect(); +} + + +void BlockItem::setSelectionRect(const QRectF & r) { + g_selection.setRect(r); +} + diff --git a/test/qad/blockview/blockitem.h b/test/qad/blockview/blockitem.h new file mode 100644 index 0000000..9ac1d79 --- /dev/null +++ b/test/qad/blockview/blockitem.h @@ -0,0 +1,144 @@ +#ifndef BLOCKITEM_H +#define BLOCKITEM_H + +#include "blockitempin.h" +#include + +/// data: +/// 1002 - flag for move parent (true) +/// 1003 - flag for visualize selection (true) +/// 1004 - BlockItemPin ("pin") +/// 1005 - BlockBusItem ("connection") +/// 1006 - BlockItem ("item") +/// 1007 - BlockItem selection ("item_selection") +/// 1008 - item is NOT decor, ignore for function decors() (true) +/// 1009 - item is scene decor ("decor") +/// 1010 - BlockItem decor (src text for QGraphicsSimpleTextItem) +/// 1011 - item is BlockItem decor ("decor") +/// 1100 - flag for correct move (true) + +class BlockItem: public QGraphicsObject, public PropertyStorage +{ + friend class BlockView; + friend class BlockItemPin; + friend class DrawTools; + Q_OBJECT + Q_PROPERTY(double _thickness READ thickness WRITE setThickness DESIGNABLE false SCRIPTABLE false) + Q_PROPERTY(QRectF _selRect READ selectionRect WRITE setSelectionRect DESIGNABLE false SCRIPTABLE false) +public: + BlockItem(QGraphicsItem * parent = 0); + ~BlockItem(); + + BlockItem * copy() const; + BlockItemPin * addPin(BlockItemPin * pin, bool update_ = true); + BlockItemPin * addPin(Qt::Alignment align, int bus_type, const QString & text, bool update_ = true); + void removePin(BlockItemPin * pin); + void addDecor(QGraphicsItem * item); + void addDecor(QGraphicsItem & item); + void removeDecor(QGraphicsItem * item); + QVector takePins(); + void clearPins(); + void clearDecors(); + QVector pins() const; + QList decors() const {return decors_;} + QList connectedBuses() const; + BlockItemPin * pinByText(const QString & t) const; + BlockItemPin * pinAtBus(BlockBusItem * bus) const; + QColor color() const {return col;} + void setColor(QColor c) {col = c; _resize(size());} + QSizeF size() const {return g_main.rect().size();} + QRectF sceneRect() const; + qreal width() const {return size().width();} + qreal height() const {return size().height();} + int pinsMargin() const {return pins_margin;} + void setSize(QSizeF s) {_resize(s);} + void setSize(qreal w, qreal h) {setSize(QSizeF(w, h));} + void setWidth(qreal w) {setSize(QSizeF(w, size().height()));} + void setHeight(qreal h) {setSize(QSizeF(size().width(), h));} + void setPinsMargin(int marg) {if (marg > 1 && marg < 256) pins_margin = marg; arrangePins();} + + QByteArray saveModel(); + void loadModel(const QByteArray & data); + QByteArray save() const; + void load(const QByteArray & data); + void arrangePins(); + + void removeBindings(const QString & bind_name); + void removeBindingByProperty(const QString & prop_name); + void addBinding(const QString & prop_name, const QString & bind_name); + void applyBinding(const QString & bind_name, const QVariant & bind_value); + void applyBindings(const PropertyStorage & bindings); + void setBindings(const QList > & bindings); + QList > getBindings(); + QString getBindName(const QString & prop_name) const; + QStringList getBindNames() const; + QStringList getBindProps() const; + + enum {Type = UserType + 1}; + +protected: + void _resize(QSizeF s); + void _moveToTop(bool only_decors = false); + int type() const {return Type;} + QRectF boundingRect() const; + void mouseMoveEvent(QGraphicsSceneMouseEvent * event); + void hoverEnterEvent(QGraphicsSceneHoverEvent * event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent * event); + double left() const {return boundingRect().left();} + double right() const {return boundingRect().right();} + double top() const {return boundingRect().top();} + double bottom() const {return boundingRect().bottom();} + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0); + QVariant itemChange(GraphicsItemChange change, const QVariant & value); + + QGraphicsRectItem g_main, g_selection; + int pins_margin; + QColor col; + QMap > pins_; + QList decors_; + QList > prop_bindings; // + +private: + double thickness() const; + void setThickness(double w); + QRectF selectionRect() const; + void setSelectionRect(const QRectF & r); + + QPropertyAnimation anim_thick, anim_sel; + QTime t_sel; + +signals: + void blockHoverEnter(BlockItem * b); + void blockHoverLeave(BlockItem * b); +}; + + +inline QDataStream & operator <<(QDataStream & s, const BlockItemPin * p) { + ChunkStream cs; + cs << cs.chunk(1, int(p->alignment())) << cs.chunk(2, p->busType()) << cs.chunk(3, p->text()) << cs.chunk(4, p->toolTip()); + s << cs.data(); return s;} +inline QDataStream & operator >>(QDataStream & s, BlockItemPin *& p) { + ChunkStream cs(s); + p = new BlockItemPin(); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: p->setAlignment((Qt::Alignment)cs.getData()); break; + case 2: p->setBusType(cs.getData()); break; + case 3: p->setText(cs.getData()); break; + case 4: p->setToolTip(cs.getData()); break; + } + } + return s; +} + + +inline QDataStream & operator <<(QDataStream & s, const BlockItem * b) {s << b->save(); return s;} +inline QDataStream & operator >>(QDataStream & s, BlockItem *& b) { + QByteArray ba; s >> ba; + b = new BlockItem(); + b->load(ba); + return s; +} + + +#endif // BLOCKITEM_H diff --git a/test/qad/blockview/blockitempin.cpp b/test/qad/blockview/blockitempin.cpp new file mode 100644 index 0000000..fe0e2f8 --- /dev/null +++ b/test/qad/blockview/blockitempin.cpp @@ -0,0 +1,163 @@ +#include "blockview.h" +#include + + +BlockItemPin::BlockItemPin(Qt::Alignment a, int bus_type_, const QString & text_, QGraphicsObject * _parent): QGraphicsObject(_parent), ell_item(this), text_item(this) { + parent_ = 0; + setData(1004, "pin"); + setAcceptHoverEvents(true); + text_item.setData(1002, true); + ell_item.setData(1003, true); + br[Disconnected] = QBrush(Qt::lightGray); + br[Connected] = QBrush(Qt::darkGreen); + br[Hover] = QBrush(Qt::blue); + br[Drop] = QBrush(Qt::green); + br[Accept] = QBrush(Qt::green); + br[Reject] = QBrush(Qt::red); + anim_pin_size.setTargetObject(this); + anim_pin_size.setPropertyName("pinSize"); + anim_pin_size.setEasingCurve(QEasingCurve::OutElastic); + anim_pin_size.setDuration(700); + anim_accept.setTargetObject(this); + anim_accept.setPropertyName("pinSize"); + anim_accept.setEasingCurve(QEasingCurve::InOutQuad); + anim_accept.setDuration(150); + connect(&anim_accept, SIGNAL(finished()), this, SLOT(animationAccept())); + setState(Disconnected); + setAlignment(a); + setBusType(bus_type_); + setText(text_); + setZValue(2.); + setDirection(BlockItemPin::InputOutput); + _reparent(); +} + + +void BlockItemPin::animAccept() { + if (!((BlockView *)scene()->views().back())->isBlockAnimationEnabled() && anim_accept.state() != QAbstractAnimation::Running) return; + anim_accept.setStartValue(pinSize()); + anim_accept.setEndValue(10.); + anim_accept.start(); +} + + +void BlockItemPin::setState(State s) { + State os = state_; + state_ = s; + setBrush(br[int(state_)]); + if (s == Accept && os != Accept) { + animAccept(); + } + update(); +} + + +void BlockItemPin::enlargePin(bool enlarge) { + double sz = enlarge ? 12. : 7; + if (anim_accept.state() == QAbstractAnimation::Running && enlarge) { + anim_accept.stop(); + resizePin(sz); + } + if (!((BlockView *)scene()->views().back())->isBlockAnimationEnabled()) { + resizePin(sz); + return; + } + if (sz == anim_pin_size.endValue()) + return; + anim_pin_size.stop(); + anim_pin_size.setStartValue(pinSize()); + anim_pin_size.setEndValue(sz); + anim_pin_size.start(); +} + + +void BlockItemPin::resizePin(double r) { + ell_item.setRect(-r, -r, r+r, r+r); +} + + +double BlockItemPin::pinSize() const { + return ell_item.rect().width() / 2.; +} + + +void BlockItemPin::animationAccept() { + if (anim_accept.endValue().toDouble() == 7.) return; + anim_accept.setStartValue(pinSize()); + anim_accept.setEndValue(7.); + anim_accept.start(); +} + + +void BlockItemPin::_init(bool affect_parent) { + text_item.setFont(AlignedTextItem::sceneFont(QApplication::font())); + QRectF tbr = text_item.boundingRect(); + const double r = 7.; + ell_item.setRect(-r, -r, r+r, r+r); + ell_item.setSpanAngle(16*180); + text_item.resetTransform(); + text_item.setPos(0, -tbr.height() / 2.); + text_item.setTransformOriginPoint(0, tbr.height() / 2.); + switch (align) { + case Qt::AlignBottom: ell_item.setStartAngle(16*0); text_item.setRotation(-90.); text_item.moveBy(0, -r * 1.5); break; + case Qt::AlignRight: ell_item.setStartAngle(16*90); text_item.setRotation(0.); text_item.moveBy(-tbr.width() - r * 1.5, 0); break; + case Qt::AlignTop: ell_item.setStartAngle(16*180); text_item.setRotation(-90.); text_item.moveBy(0, tbr.width() + r * 1.5); break; + case Qt::AlignLeft: ell_item.setStartAngle(16*270); text_item.setRotation(0.); text_item.moveBy(r * 1.5, 0); break; + default: break; + } + if (affect_parent && parent_) + parent_->arrangePins(); +} + + +void BlockItemPin::_reparent() { + if (parentItem() == 0) return; + if (qgraphicsitem_cast(parentItem()) == 0) return; + QPen p = qgraphicsitem_cast(parentItem())->g_main.pen(); + ell_item.setPen(p); + if (scene()) { + text_item.setFont(AlignedTextItem::sceneFont(scene()->font())); + QRectF tbr = text_item.boundingRect(); + text_item.resetTransform(); + text_item.setPos(0, -tbr.height() / 2.); + text_item.setTransformOriginPoint(0, tbr.height() / 2.); + } +} + + +QGraphicsView * BlockItemPin::_view() const { + if (!scene()) return 0; + if (scene()->views().isEmpty()) return 0; + return scene()->views().back(); +} + + +QVariant BlockItemPin::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) { + if (change == QGraphicsItem::ItemParentChange) + _reparent(); + return QGraphicsItem::itemChange(change, value); +} + + +void BlockItemPin::hoverEnterEvent(QGraphicsSceneHoverEvent * e) { + QGraphicsView * v = _view(); + bool m_pin_mc = false; + if (v) { + QMetaObject::invokeMethod(v, "getPinMC", Q_ARG(bool*, &m_pin_mc)); + QMetaObject::invokeMethod(v, "pinHoverInOut", Qt::QueuedConnection, Q_ARG(BlockItemPin*, this)); + } + if ((state() != Disconnected) && !m_pin_mc) return; + saveState(); + setState(BlockItemPin::Hover); + enlargePin(true); + update(); +} + + +void BlockItemPin::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) { + QGraphicsView * v = _view(); + restoreState(); + enlargePin(false); + update(); + if (v) QMetaObject::invokeMethod(v, "pinHoverInOut", Q_ARG(BlockItemPin*, 0)); +} diff --git a/test/qad/blockview/blockitempin.h b/test/qad/blockview/blockitempin.h new file mode 100644 index 0000000..4599299 --- /dev/null +++ b/test/qad/blockview/blockitempin.h @@ -0,0 +1,112 @@ +#ifndef BLOCKITEMPIN_H +#define BLOCKITEMPIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blockbase.h" +#include "alignedtextitem.h" + + +class BlockItem; +class BlockBusItem; + + +class BlockItemPin: public QGraphicsObject, public PropertyStorage +{ + friend class BlockView; + friend class BlockItem; + Q_OBJECT + Q_PROPERTY(double pinSize READ pinSize WRITE resizePin DESIGNABLE false SCRIPTABLE false) +public: + BlockItemPin(Qt::Alignment a = Qt::AlignLeft, int bus_type = 0, const QString & text_ = QString(), QGraphicsObject * parent_ = 0); + + enum State { + Disconnected, + Connected, + Hover, + Drop, + Accept, + Reject + }; + + enum Direction { + None = 0x0, + Input = 0x1, + Output = 0x2, + InputOutput = 0x3 + }; + + void setPen(const QPen & p) {ell_item.setPen(p);} + QPen pen() const {return ell_item.pen();} + void setBrush(const QBrush & b) {ell_item.setBrush(b);} + QBrush brush() const {return ell_item.brush();} + + int busType() const {return bus_type;} + Qt::Alignment alignment() const {return align;} + Direction direction() const {return dir;} + QString text() const {return text_item.text();} + State state() const {return state_;} + + void setBusType(int type_) {bus_type = type_;} + void setAlignment(Qt::Alignment a) {align = a; _init(true);} + void setDirection(Direction d) {dir = d; _init(true);} + void setText(const QString & t) {text_item.setText(t); _init(true);} + void setState(State s); + + void saveState() {sstate_.push(state_);} + bool restoreState() {if (sstate_.isEmpty()) return false; setState(sstate_.pop()); return true;} + void clearStateStack() {sstate_.clear();} + + void enlargePin(bool enlarge); + + BlockItem * parent() const {return parent_;} + QList connectedBuses() const {return buses_;} + + enum {Type = UserType + 3}; + +public slots: + void animAccept(); + +protected: + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0) {} + QRectF boundingRect() const {return ell_item.boundingRect().translated(ell_item.pos()) | text_item.boundingRect().translated(text_item.pos());} + int type() const {return Type;} + QVariant itemChange(GraphicsItemChange change, const QVariant & value); + void hoverEnterEvent(QGraphicsSceneHoverEvent * e); + void hoverLeaveEvent(QGraphicsSceneHoverEvent * e); + void _init(bool affect_parent = false); + void _reparent(); + QGraphicsView * _view() const; + int bus_type; + State state_; + QGraphicsEllipseItem ell_item; + QGraphicsSimpleTextItem text_item; + QStack sstate_; + QList buses_; + BlockItem * parent_; + Qt::Alignment align; + Direction dir; + QBrush br[6]; + +private slots: + void animationAccept(); + +private: + void resizePin(double r); + double pinSize() const; + + QPropertyAnimation anim_pin_size; + QPropertyAnimation anim_accept; + +}; + + +#endif // BLOCKITEMPIN_H diff --git a/test/qad/blockview/blockview.cpp b/test/qad/blockview/blockview.cpp new file mode 100644 index 0000000..0d510fc --- /dev/null +++ b/test/qad/blockview/blockview.cpp @@ -0,0 +1,2062 @@ +#include "blockview.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const QString _BlockView_Mime_ = "_BlockView_copypaste_"; + + +BlockView::BlockView(QWidget * parent): QGraphicsView(parent), tmp_bus(true) { + _init(); +} + + +BlockView::BlockView(QGraphicsScene * scene, QWidget * parent): QGraphicsView(scene, parent), tmp_bus(true) { + _init(); +} + + +BlockView::~BlockView() { +} + + +void BlockView::_init() { + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + grid_visible = grid_snap = pm_connect = navigation = m_connect = m_trace_with_buses = prev_tcb = minimap = true; + mm_drag = moved = new_branch = new_bus = mm_cancel = iconnect = mm_copy = m_pin_mc = mm_thumb = move_bus_point = wheel_zoom = false; + match_bus = bus_from = 0; + mm_ci = 0; + hpin = 0; + ghost_ = 0; + grid_step = 10.; + grid_points = 1; + grid_pen = QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 1, Qt::NoPen); + thick = 1; + thumb_hide_delay = 500; + timer_thumb = 0; + smode = BlockView::MultiSelection; + cur_scl = thumb_scl = prev_app_scale = 1.; + _talpha = 0.; + ae_enabled = is_block_anim = true; + is_nav_anim = true; + nav_prev_aa = nav_prev_imaa = nav_prev_grid = true; + thumb_size = QSizeF(200, 200); + if (scene() == 0) { + scene_ = new QGraphicsScene; + setScene(scene_); + } + scene_ = scene(); + scene_->setSceneRect(-2500, -2500, 5000, 5000); + scene_->setItemIndexMethod(QGraphicsScene::NoIndex); + scene_->installEventFilter(this); + scene_->addItem(&tmp_bus); + widget_thumb.setParent(this); + //widget_thumb.setAutoFillBackground(true); + //widget_thumb.setStyleSheet("background-color: rgb(255, 0, 0);"); + widget_thumb.installEventFilter(this); + widget_thumb.setMouseTracking(true); + widget_thumb.show(); + widget_thumb.setWindowOpacity(0.); + thumb_anim.setTargetObject(this); + thumb_anim.setPropertyName("_thumb"); + thumb_anim.setEasingCurve(QEasingCurve::InCubic); + nav_anim.setTargetObject(this); + nav_anim.setPropertyName("_nav"); + nav_anim.setEasingCurve(QEasingCurve::Linear); + nav_anim.setDuration(150); + connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(checkPaste())); + connect(&nav_anim, SIGNAL(finished()), this, SLOT(_navFinished())); + connect(scene_, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(adjustThumb())); + connect(scene_, SIGNAL(selectionChanged()), this, SLOT(sceneSelectionChanged())); + connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled())); + connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled())); + centerOn(scene_->sceneRect().center()); + setCacheMode(CacheBackground); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setResizeAnchor(QGraphicsView::AnchorViewCenter); + setRenderHint(QPainter::Antialiasing); + setRenderHint(QPainter::SmoothPixmapTransform); + setMouseTracking(true); + sel_rect.setZValue(999.); + sel_rect.hide(); + QColor sc = palette().color(QPalette::Highlight); + sc.setAlphaF(0.6); + QPen pen(sc.darker(200), lineThickness() + 1., Qt::DotLine); + pen.setCosmetic(true); + sel_rect.setPen(pen); + sc.setAlphaF(0.2); + sel_rect.setBrush(QBrush(sc)); + checkPaste(true); + + /*AlignedTextItem * ti = new AlignedTextItem("This is text!"); + ti->setFlag(QGraphicsItem::ItemIsSelectable, true); + ti->setFlag(QGraphicsItem::ItemIsMovable, true); + addItem(ti); + ti->setAlignment(Qt::AlignBottom | Qt::AlignLeft); + ti->setText("text\nt"); + QGraphicsSimpleTextItem * t__ = new QGraphicsSimpleTextItem("TEXT"); + t__->setFlags(QGraphicsItem::ItemIsSelectable); + addItem(t__); + addItem(new QGraphicsLineItem(0, -50, 0, 50)); + addItem(new QGraphicsLineItem(-50, 0, 50, 0)); + + for (int i = 0; i < 5; ++i) { + BlockItem * it = new BlockItem(); + it->setPos(i*150, i*20); + it->addPin(new BlockItemPin()); + if (i == 2) + it->setFlags(QGraphicsItem::ItemIsSelectable); + scene_->addItem(it); + } + QGraphicsItem * it = new QGraphicsRectItem(0, 0, 200, 100); + it->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); + it->setData(1001, true); + scene_->addItem(it); + + BlockBusItem * bus = new BlockBusItem(); + bus->setImages(QImage("icons/bus_twin.png"), QImage("icons/bus_end_rj45.png")); + bus->setEndpointsNumber(3); + bus->setBusType(1); + bus->appendPoint(QPointF(20, 40)); + bus->appendPoint(QPointF(150, -20)); + bus->appendPoint(QPointF(40, -80)); + //bus->setImages(QImage("icons/bus_twin.png"), QImage("icons/bus_end_rj45.png")); + addItem(bus); + bus = new BlockBusItem(); + bus->appendPoint(QPointF(-20, 40)); + bus->appendPoint(QPointF(100, -30)); + bus->appendPoint(QPointF(20, -60)); + addItem(bus);*/ +} + + +void BlockView::_updateBack() { + setCacheMode(CacheNone); + invalidateScene(); + setCacheMode(CacheBackground); +} + + +bool BlockView::event(QEvent * e) { + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) { + double cscl = appScale(this); + QGraphicsView::scale(cscl / prev_app_scale, cscl / prev_app_scale); + prev_app_scale = cscl; + } + return QGraphicsView::event(e); +} + + +bool BlockView::eventFilter(QObject * o, QEvent * e) { + if (o == &widget_thumb) { + QMouseEvent * me = (QMouseEvent*)e; + switch (e->type()) { + case QEvent::Paint: + drawThumb(); + return true; + case QEvent::Enter: + thumbShow(); + break; + case QEvent::Leave: + restartTimer(timer_thumb, thumb_hide_delay); + break; + case QEvent::MouseButtonPress: + thumb_press = me->pos(); + widget_thumb.setCursor(Qt::ClosedHandCursor); + if (!thumb_vr.contains(thumb_press)) { + thumb_vr.moveCenter(thumb_press); + scrollFromThumb(); + } + break; + case QEvent::MouseButtonRelease: + widget_thumb.setCursor(Qt::OpenHandCursor); + break; + case QEvent::MouseMove: + if (me->buttons() == 0) + widget_thumb.setCursor(thumb_vr.contains(me->pos()) ? Qt::OpenHandCursor : Qt::CrossCursor); + if (me->buttons().testFlag(Qt::LeftButton)) { + thumb_vr.translate(me->pos() - thumb_press); + scrollFromThumb(); + thumb_press = me->pos(); + } + break; + default: break; + } + return QGraphicsView::eventFilter(o, e); + } + if (o == scene_) { + QGraphicsSceneMouseEvent * me = (QGraphicsSceneMouseEvent*)e; + QList mil; + QPointF mdp; + bool fmm_drag = false; + int btncnt = 0; + switch (e->type()) { + case QEvent::GraphicsSceneMouseDoubleClick: + mil = scene_->items(scene_point); + foreach (QGraphicsItem * i, mil) { + if (i->data(1006) == "item") { + //emit blockDoubleClicked((BlockItem * )i); + QMetaObject::invokeMethod(this, "blockDoubleClicked", Qt::QueuedConnection, Q_ARG(BlockItem * , (BlockItem*)i)); + return true; + } + if (i->data(1005) == "connection") { + if (qgraphicsitem_cast(i)->isBusSelected()) { + //emit busDoubleClicked(qgraphicsitem_cast(i)); + QMetaObject::invokeMethod(this, "busDoubleClicked", Qt::QueuedConnection, Q_ARG(BlockBusItem * , (BlockBusItem*)i)); + return true; + } + } + } + //return true; + break; + /*case QEvent::GraphicsSceneHoverMove: + mil = scene_->items(scene_point); + mm_ci = (mil.isEmpty() ? 0 : mil.front()); + if (mm_ci != 0) { + while (mm_ci->data(1005).toString() == "connection") { + if (qgraphicsitem_cast(mm_ci)) + if (qgraphicsitem_cast(mm_ci)->isBusSelected()) + break; + if (mil.size() > 1) { + mm_ci = mil[1]; + mil.pop_front(); + } else { + mm_ci = 0; + break; + } + } + } + break;*/ + case QEvent::GraphicsSceneMousePress: + if (mm_ci != 0) + if (mm_ci->data(1008).toBool()) + break; + //qDebug() << "press"; + if (me->buttons().testFlag(Qt::LeftButton)) btncnt++; + if (me->buttons().testFlag(Qt::RightButton)) btncnt++; + if (me->buttons().testFlag(Qt::MidButton)) btncnt++; + mm_cancel = btncnt >= 2; + match_bus = bus_from = 0; + hpin = 0; + copy_dp = QPointF(); + //qDebug() << mm_cancel << mm_copy << mm_drag << new_branch << new_bus; + if (mm_copy && mm_cancel) { + deleteCopyTemp(); + mm_copy = moved = false; + unsetCursor(); + } + if (new_bus && mm_cancel) { + new_bus = false; + unmarkPins(true); + reconnectAll(); + hideTmpBuses(); + m_trace_with_buses = prev_tcb; + } + if (new_branch && mm_cancel) { + new_branch = false; + hideTmpBuses(); + m_trace_with_buses = prev_tcb; + } + if (moved && mm_cancel) { + moved = false; + restoreSelState(); + hideTmpBuses(); + m_trace_with_buses = prev_tcb; + } + if (mm_cancel) return true; + mm_mods = me->modifiers(); + mm_drag = moved = false; + screen_point = me->screenPos(); + scene_point = me->scenePos(); + if ((me->button() == Qt::MidButton)) { + thumbShow(); + restartTimer(timer_thumb, thumb_hide_delay); + return true; + } + mil = scene_->items(scene_point); + mm_ci = (mil.isEmpty() ? 0 : mil.front()); + if (mil.isEmpty()) return true; + if (mm_ci != 0) { + if (mm_ci->data(1008).toBool()) + break; + while (mm_ci->data(1005).toString() == "connection") { + if (qgraphicsitem_cast(mm_ci)) + if (qgraphicsitem_cast(mm_ci)->isBusSelected()) + break; + if (mil.size() > 1) { + mm_ci = mil[1]; + mil.pop_front(); + } else { + mm_ci = 0; + break; + } + } + if (mm_ci->data(1003).toBool()) { + if (mil.size() > 1) { + mm_ci = mil[1]; + mil.pop_front(); + if (mm_ci->data(1003).toBool()) + if (mil.size() > 1) + mm_ci = mil[1]; + } else + mm_ci = 0; + } + if (mm_ci->data(1002).toBool()) { + QGraphicsItem * ti = mm_ci; + while (mm_ci->parentItem() != 0) + mm_ci = mm_ci->parentItem(); + if (!ti->data(1010).toString().isEmpty()) { // text item, check for rect + BlockItem * bi = qgraphicsitem_cast(mm_ci); + if (bi) { + if (!bi->sceneRect().contains(scene_point)) { + //qDebug() << "return"; + mm_ci = 0; + } + } + } + return true; + } + } + if (mm_ci) { + if (mm_ci->data(1004) == "pin" && m_connect) { + if (qgraphicsitem_cast(mm_ci)->state() == BlockItemPin::Hover) { + trace_from = mm_ci->scenePos(); + qgraphicsitem_cast(mm_ci)->clearStateStack(); + hideTmpBuses(); + tmp_bus.setBusType(qgraphicsitem_cast(mm_ci)->busType()); + tmp_bus.setEndpointsNumber(3); + tmp_bus.show(); + new_bus = true; + qDeleteAll(tmp_buses); + tmp_buses.clear(); + foreach (BlockItemPin * p, last_multiconnect_pl) { + tmp_buses << new BlockBusItem(true); + tmp_buses.back()->setBusType(p->busType()); + addItem(tmp_buses.back()); + } + //qDebug() << "new" << ; + prev_tcb = m_trace_with_buses; + newBusStarted(tmp_bus.busType()); + markPins(tmp_bus.busType()); + if (qgraphicsitem_cast(mm_ci)->alignment() == Qt::AlignLeft || + qgraphicsitem_cast(mm_ci)->alignment() == Qt::AlignRight) + wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); + else + wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); + return true; + } + } + } + cur_scl = qSqrt(transform().determinant()); + //return true; + break; + case QEvent::GraphicsSceneMouseMove: + //qDebug() << "move" << (mm_ci != 0 ? mm_ci : 0); + /*if (ghost_) { + ghost_->setPos(quantize(me->scenePos(), grid_step)); + }*/ + if (mm_ci) + if (mm_ci->data(1008).toBool()) + break; + if (mm_cancel) return true; + if (me->buttons().testFlag(Qt::LeftButton)) { + if (!mm_drag) { + if ((screen_point - me->screenPos()).manhattanLength() >= QApplication::startDragDistance()) { + mm_drag = fmm_drag = true; + saveBusesState(); + } + } else { + if (tmp_bus.isVisible()) { + mil = scene_->items(me->scenePos()); + hpin = 0; + foreach (QGraphicsItem * i, mil) + if (i->data(1004) == "pin") { + hpin = qgraphicsitem_cast(i); + break; + } + if (hpin) { + if (hpin->state() == BlockItemPin::Accept) { + unhoverPins(hpin); + hoverAcceptedPin(hpin, true); + } else hpin = 0; + } else unhoverPins(); + if (new_branch) { + matchBus(); + break; + } + trace(trace_from, me->scenePos(), &tmp_bus); + for (int i = 0; i < qMin(tmp_buses.size(), last_multiconnect_pl.size()); ++i) { + QPointF dp = last_multiconnect_pl[i]->scenePos() - trace_from; + //qDebug() << "trace" << i << dp; + trace(trace_from + dp, me->scenePos() + dp, tmp_buses[i], false); + tmp_buses[i]->show(); + } + matchBus(); + } + } + if (tmp_bus.isVisible()) return true; + if (mm_mods.testFlag(Qt::ShiftModifier)) { + if (fmm_drag) { + fmm_drag = false; + if (mm_ci) { + if ((mm_ci->data(1006) == "item")) { + if (!mm_ci->isSelected() && sel_items.isEmpty()) { + clearSelection(); + mm_ci->setSelected(true); + } + } else { + mm_ci = 0; + break; + } + } else { + break; + } + sel_items = scene_->selectedItems(); + deleteCopyTemp(); + QList bi; + foreach (QGraphicsItem * i, sel_items) { + if (i->data(1006) == "item") { + //qDebug() << "copy"; + bi << qgraphicsitem_cast(i); + BlockItem * ti = bi.back()->copy(); + ti->g_main.setPen(QPen(ti->g_main.pen().color(), ti->g_main.pen().widthF(), Qt::DashLine)); + QColor bc = ti->g_main.brush().color(); bc.setAlphaF(bc.alphaF() * 0.5); + ti->g_main.setBrush(bc); + copy_items << ti; + scene_->addItem(ti); + } + } + QList ibi = internalBuses(bi); + foreach (BlockBusItem * i, ibi) { + i = i->copy(); + i->setOpacity(i->opacity() * 0.5); + copy_buses << i; + scene_->addItem(i); + } + mm_copy = true; + setCursor(Qt::DragCopyCursor); + } + } else { + if (smode == BlockView::SingleSelection) { + if (fmm_drag) { + clearSelection(); + if (mm_ci != 0) { + mm_ci->setSelected(true); + } + saveSelState(); + } + } + if (smode == BlockView::MultiSelection) { + sel_rect.setRect(QRectF(scene_point, me->scenePos()).normalized()); + if (fmm_drag) { + if (mm_ci == 0) { + scene_->addItem(&sel_rect); + sel_rect.show(); + if (!mm_mods.testFlag(Qt::ControlModifier)) + clearSelection(); + } else { + if (!mm_mods.testFlag(Qt::ControlModifier) && !mm_ci->isSelected()) { + clearSelection(); + mm_ci->setSelected(true); + } + } + saveSelState(); + if (mm_ci != 0) + if (!sel_items.contains(mm_ci)) + mm_ci = 0; + } + } + } + if (sel_rect.isVisible()) + applySelRect(me); + else { + if (mm_drag && mm_ci != 0) { + mdp = (me->scenePos() - scene_point); + if (grid_snap) + mdp = quantize(mdp, grid_step); + if (!mdp.isNull()) { + scene_point += mdp; + copy_dp += mdp; + } + if (mm_copy) { + if (!mdp.isNull()) + moved = true; + foreach (QGraphicsItem * i, copy_items) + i->setPos(i->pos() + mdp); + foreach (BlockBusItem * i, copy_buses) + i->movePolyline(mdp); + } + if (!mm_mods.testFlag(Qt::ControlModifier) && !mm_mods.testFlag(Qt::ShiftModifier)) { + if (!mdp.isNull()) + moved = true; + foreach (QGraphicsItem * i, sel_items) + if (i->flags().testFlag(QGraphicsItem::ItemIsMovable)) + i->setPos(i->pos() + mdp); + if (!me->modifiers().testFlag(Qt::AltModifier)) + moveBuses(sel_items, mdp); + setCursor(Qt::ClosedHandCursor); + } + return true; + } + } + if (mm_ci) + if (mm_ci->data(1100).toBool()) + return true; + } + if (me->modifiers().testFlag(Qt::ControlModifier) && me->buttons() != 0 && mm_ci == 0) + return true; + //qDebug() << "scene mouse"; + //return true; + break; + case QEvent::GraphicsSceneMouseRelease: + if (me->buttons().testFlag(Qt::LeftButton)) btncnt++; + if (me->buttons().testFlag(Qt::RightButton)) btncnt++; + if (me->buttons().testFlag(Qt::MidButton)) btncnt++; + mm_cancel = btncnt > 0; + if (mm_cancel || (me->button() == Qt::MidButton) || (me->button() == Qt::RightButton)) { + mm_ci = 0; + return true; + } + if (mm_ci) + if (mm_ci->data(1008).toBool()) { + mm_ci = 0; + break; + } + if (mm_copy) { + QList ai; + blockSignals(true); + if (moved) { + QList ci; + QList bi; + foreach (QGraphicsItem * b, sel_items) + if (b->data(1006) == "item") { + ci << qgraphicsitem_cast(b); + ai << qgraphicsitem_cast(b); + } + bi = internalBuses(ci); + if (!ci.isEmpty()) copyBlocks(ci, copy_dp); + if (!bi.isEmpty()) copyBuses(bi, copy_dp); + } + deleteCopyTemp(); + blockSignals(false); + if (moved) { + moved = false; + reconnectAll(); + emitActionEvent(BlockItemBase::BlockCopy, ai); + } + } + if (new_branch) { + hideTmpBuses(false); + } + if (moved && pm_connect) { + QList ci; + foreach (QGraphicsItem * b, sel_items) + if (b->data(1006) == "item") + ci << b; + simplifyBuses(); + emitActionEvent(BlockItemBase::BlockMove, ci); + reconnectAll(); + } + moved = mm_copy = false; + if (tmp_bus.isVisible()) { + if (!tmp_bus.pol.isEmpty()) { + if (match_bus == 0) { + BlockBusItem * nb = new BlockBusItem(tmp_bus); + addItem(nb, tmp_buses.isEmpty()); + foreach (BlockBusItem * b, tmp_buses) { + nb = new BlockBusItem(*b); + addItem(nb, b == tmp_buses.back()); + } + } else { + if (connectTmpToBus(match_bus)) { + emitActionEvent(BlockItemBase::BusAdd, QList() << match_bus); + emit connectionsChanged(); + } + } + } + unmarkPins(); + hideTmpBuses(); + reconnectAll(); + BlockItemPin * pin = getPin(scene_->items(me->scenePos())); + if (pin) + pin->hoverEnterEvent(0); + m_trace_with_buses = prev_tcb; + } + clearBusStates(); + if (!mm_drag) { + switch (smode) { + case SingleSelection: + clearSelection(); + if (mm_ci) + mm_ci->setSelected(true); + break; + case MultiSelection: + if (mm_ci == 0 || !me->modifiers().testFlag(Qt::ControlModifier)) { + clearSelection(); + if (mm_ci) + mm_ci->setSelected(true); + } else { + if (mm_ci != 0) { + if (me->modifiers().testFlag(Qt::ControlModifier)) { + if (mm_ci->data(1006) == "item") + mm_ci->setSelected(!mm_ci->isSelected()); + } else + mm_ci->setSelected(true); + } + } + break; + default: + clearSelection(); + break; + } + } + sel_rect.hide(); + if (sel_rect.scene()) + scene_->removeItem(&sel_rect); + mm_drag = false; + mm_ci = 0; + unsetCursor(); + break; + default: break; + } + } + return QGraphicsView::eventFilter(o, e); +} + + +void BlockView::stopTimer(int & tid) { + if (tid > 0) killTimer(tid); + tid = 0; +} + + +void BlockView::restartTimer(int & tid, int msecs) { + stopTimer(tid); + tid = startTimer(msecs); +} + + +void BlockView::timerEvent(QTimerEvent * e) { + if (e->timerId() == timer_thumb) { + thumbHide(); + stopTimer(timer_thumb); + return; + } + QGraphicsView::timerEvent(e); +} + + +void BlockView::wheelEvent(QWheelEvent * e) { + if (!navigation) return; + if (wheel_zoom || e->modifiers().testFlag(Qt::ControlModifier)) { + double scl = 1. - e->delta() / 500.; + if (!is_nav_anim || (nav_anim.state() != QPropertyAnimation::Running)) + nav_target = _nav(); + QRectF r = nav_target; + double vw = viewport()->width(), vh = viewport()->height(); + double cx = double(e->pos().x()) / vw, cy = double(e->pos().y()) / vh; + double pw = r.width(), ph = r.height(); + r.setWidth(r.width() * scl); + r.setHeight(r.width() * vh / vw); + r.translate(cx * (pw - r.width()), cy * (ph - r.height())); + animateNav(r, scl); + return; + } + if (!wheel_zoom) QGraphicsView::wheelEvent(e); +} + + +void BlockView::mousePressEvent(QMouseEvent * event) { + press_point = event->pos(); + if (event->buttons().testFlag(Qt::MidButton) || event->buttons().testFlag(Qt::RightButton)) { + setCursor(Qt::ClosedHandCursor); + sel_rect.hide(); + if (sel_rect.scene()) + scene_->removeItem(&sel_rect); + } + QGraphicsView::mousePressEvent(event); +} + + +void BlockView::mouseReleaseEvent(QMouseEvent * event) { + unsetCursor(); + QGraphicsView::mouseReleaseEvent(event); +} + + +void BlockView::updateNavRect() { + QPointF t = mapToScene(viewport()->rect().topLeft()); + QPointF b = mapToScene(viewport()->rect().bottomRight()); + nav_rect = QRectF(t, b); +} + + +void BlockView::scrolled() { + updateNavRect(); +} + + +void BlockView::mouseMoveEvent(QMouseEvent * event) { + if (navigation) { + if (event->buttons().testFlag(Qt::MidButton) || event->buttons().testFlag(Qt::RightButton)) { + QPoint dp = (press_point - event->pos()); + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dp.x()); + verticalScrollBar()->setValue(verticalScrollBar()->value() + dp.y()); + press_point = event->pos(); + updateNavRect(); + QRectF nr = nav_anim.endValue().toRectF(); + nr.moveCenter(_nav().center()); + nav_anim.setEndValue(nr); + return; + } + } + QGraphicsView::mouseMoveEvent(event); +} + + +void BlockView::mouseDoubleClickEvent(QMouseEvent * event) { + if (event->buttons().testFlag(Qt::MidButton) || event->buttons().testFlag(Qt::RightButton)) { + fitInView(); + return; + } + QGraphicsView::mouseDoubleClickEvent(event); +} + + +void BlockView::keyPressEvent(QKeyEvent * e) { + BlockItemPin * pin = getPin(items(mapFromGlobal(QCursor::pos()))); + if (pin) { + //qDebug() << "pin" << pin->state(); + if (pin->state() == BlockItemPin::Hover) { + highlightNearPins(pin, e->modifiers()); + } + } + if (tmp_bus.isVisible()) { + bool retrace = false; + if (e->key() == Qt::Key_Shift) { + retrace = true; + switch (wavetrace.preferredDirection()) { + case BlockViewWavetrace::NoTrace: wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); break; + case BlockViewWavetrace::Horizontal: wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); break; + case BlockViewWavetrace::Vertical: wavetrace.setPreferredDirection(BlockViewWavetrace::NoTrace); break; + } + } + if (e->key() == Qt::Key_Alt) { + retrace = true; + m_trace_with_buses = !m_trace_with_buses; + } + if (retrace) { + trace(last_trace_from, trace_to, &tmp_bus); + for (int i = 0; i < qMin(tmp_buses.size(), last_multiconnect_pl.size()); ++i) { + QPointF dp = last_multiconnect_pl[i]->scenePos() - last_trace_from; + //qDebug() << "trace" << i << dp; + trace(last_trace_from + dp, trace_to + dp, tmp_buses[i], false); + tmp_buses[i]->show(); + } + return; + } + } + QGraphicsView::keyPressEvent(e); +} + + +void BlockView::keyReleaseEvent(QKeyEvent * e) { + BlockItemPin * pin = getPin(items(mapFromGlobal(QCursor::pos()))); + if (pin) { + //qDebug() << "pin" << pin->state(); + if (pin->state() == BlockItemPin::Hover) { + highlightNearPins(pin, e->modifiers()); + } + } + QGraphicsView::keyReleaseEvent(e); +} + + +void BlockView::resizeEvent(QResizeEvent * event) { + QGraphicsView::resizeEvent(event); + thick = lineThickness(); + adjustThumb(); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::scrollContentsBy(int dx, int dy) { + QGraphicsView::scrollContentsBy(dx, dy); + if (isHidden()) return; + thumbShow(); + restartTimer(timer_thumb, thumb_hide_delay); + QMetaObject::invokeMethod(&widget_thumb, "update", Qt::QueuedConnection); +} + + +void BlockView::drawBackground(QPainter * painter, const QRectF & rect) { +// QGraphicsView::drawBackground(painter, rect); + if (mm_thumb) return; + float rx, ry, sx = grid_step, sy = grid_step; + double scl = qRound(1. / qSqrt(transform().determinant())); + painter->fillRect(rect, palette().brush(QPalette::Base)); + painter->setRenderHint(QPainter::Antialiasing, false); + if (scl > 0.) { + sx *= scl; + sy *= scl; + } + if (!grid_visible) return; + rx = quantize(rect.left(), sx); + ry = quantize(rect.top(), sy); + bool gp = grid_points > 0.5; + if (gp) { + QPen pp(grid_pen.color(), qMax(grid_points, thick), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + pp.setCosmetic(true); + painter->setPen(pp); + for(int i = 0; i < qCeil(rect.width() / sx) + 1; ++i) + for(int j = 0; j < qCeil(rect.height() / sy) + 1; ++j) + painter->drawPoint(rx + i * sx, ry + j * sy); + } + if (grid_pen.style() == Qt::NoPen) return; + QPen pen = grid_pen; + pen.setWidth(qMax(pen.width(), thick)); + painter->setPen(grid_pen); + for(int i = 0; i < qCeil(rect.width() / sx) + 1; ++i) { + double cx = rx + i * sx; + painter->drawLine(cx, ry, cx, ry + rect.height()); + } + for(int j = 0; j < qCeil(rect.height() / sy) + 1; ++j) { + double cy = ry + j * sy; + painter->drawLine(rx, cy, rx + rect.width(), cy); + } +} + + +void BlockView::scrollFromThumb() { + QRect r(thumb_vr); + double scl(thumb_scl); + horizontalScrollBar()->setValue(r.x() / scl + horizontalScrollBar()->minimum()); + verticalScrollBar()->setValue(r.y() / scl + verticalScrollBar()->minimum()); +} + + +void BlockView::deleteCopyTemp() { + qDeleteAll(copy_items); + copy_items.clear(); + qDeleteAll(copy_buses); + copy_buses.clear(); +} + + +void BlockView::emitActionEvent(BlockItemBase::Action action, QList items) { + if (!ae_enabled) return; + emit schemeAction(action, items); +} + + +void BlockView::setGhost(BlockItem * item) { + clearGhost(); + if (!item) return; + ghost_ = item; + ghost_->setOpacity(0.5); + addItem(item); +} + + +void BlockView::clearGhost() { + if (!ghost_) return; + delete ghost_; + ghost_ = 0; +} + + +void BlockView::drawThumb() { + if (!minimap) return; + QPainter p(&widget_thumb); + QRect wr = widget_thumb.rect().adjusted(0, 0, -1, -1); + QSizeF sr = sceneRect().size(), tr; + if (sr.width() >= sr.height()) + thumb_scl = thumb_size.width() / sr.width(); + else + thumb_scl = thumb_size.height() / sr.height(); + tr = sr * thumb_scl; + cur_scl = qSqrt(transform().determinant()); + thumb_scl /= cur_scl; + QSizeF vs(size().width() - verticalScrollBar()->width(), size().height() - horizontalScrollBar()->height()); + QRectF vr(QPointF(horizontalScrollBar()->value() - horizontalScrollBar()->minimum(), + verticalScrollBar()->value() - verticalScrollBar()->minimum()) * thumb_scl, vs * thumb_scl); + vr.adjust(0, 0, -1, -1); + + p.setBrush(Qt::lightGray); + p.setOpacity(0.5 * _talpha); + p.drawRect(wr); + p.drawImage(0, 0, im_scene); + p.setBrush(Qt::NoBrush); + p.drawRect(wr); + p.setBrush(Qt::white); + p.setRenderHint(QPainter::Antialiasing); + p.drawRect(vr); + thumb_sr = wr; + thumb_vr = vr.toRect(); +} + + +void BlockView::drawSceneThumb() { + if (!minimap) return; + QRect wr = widget_thumb.rect().adjusted(0, 0, -1, -1); + im_scene = QImage(wr.size(), QImage::Format_ARGB32); + im_scene.fill(Qt::transparent); + QPainter p(&im_scene); + p.setRenderHint(QPainter::Antialiasing); + mm_thumb = true; + scene_->render(&p, im_scene.rect()); + mm_thumb = false; +} + + +void BlockView::thumbHide() { + if (!minimap) { + widget_thumb.hide(); + return; + } + if (QApplication::widgetAt(QCursor::pos()) == &widget_thumb) return; + thumb_anim.stop(); + thumb_anim.setDuration(1000); + thumb_anim.setStartValue(_thumb()); + thumb_anim.setEndValue(0.); + thumb_anim.start(); + //qDebug() << "hide" << thumb_anim.startValue() << thumb_anim.endValue(); + //widget_thumb.hide(); +} + + +void BlockView::thumbShow() { + if (!minimap) return; + if (widget_thumb.isHidden() || (_talpha < 0.1)) drawSceneThumb(); + thumb_anim.stop(); + /*thumb_anim.setDuration(100); + thumb_anim.setStartValue(_thumb()); + thumb_anim.setEndValue(1.); + thumb_anim.start();*/ + _setThumb(1.); + //qDebug() << "show" << thumb_anim.startValue() << thumb_anim.endValue(); + //widget_thumb.show(); +} + + +void BlockView::clearSelection() { + sel_items.clear(); + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + i->setSelected(false); +} + + +void BlockView::addItem(QGraphicsItem * item, bool emit_action) { + scene_->addItem(item); + applyGridStep(); + if (item->data(1005) == "connection") { + loadBus(qgraphicsitem_cast(item)); + connect((BlockBusItem*)item, SIGNAL(destroyed(QObject*)), this, SLOT(removedBus(QObject*)), Qt::UniqueConnection); + if (emit_action) emitActionEvent(BlockItemBase::BusAdd, QList() << item); + emit connectionsChanged(); + return; + } + if (item->data(1006) == "item") { + connect((BlockItem*)item, SIGNAL(destroyed(QObject*)), this, SLOT(removedBlock(QObject*)), Qt::UniqueConnection); + connect((BlockItem*)item, SIGNAL(blockHoverEnter(BlockItem*)), this, SIGNAL(blockHoverEnter(BlockItem*)), Qt::UniqueConnection); + connect((BlockItem*)item, SIGNAL(blockHoverLeave(BlockItem*)), this, SIGNAL(blockHoverLeave(BlockItem*)), Qt::UniqueConnection); + if (emit_action) emitActionEvent(BlockItemBase::BlockAdd, QList() << item); + return; + } + item->setData(1009, "decor"); +} + + +QList BlockView::buses() const { + QList ret; + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->data(1005) == "connection") + if (!copy_buses.contains((BlockBusItem*)i)) + ret << qgraphicsitem_cast(i); + return ret; +} + + +QList BlockView::wrongConnectedBuses() const { + QList sl = buses(), ret; + QList bl = blocks(); + foreach (BlockItem * b, bl) { + QVector pins = b->pins(); + foreach (BlockItemPin * p, pins) + if (p->state() == BlockItemPin::Reject) { + QPointF pp = p->scenePos(); + foreach (BlockBusItem * s, sl) + if (s->pol.contains(pp)) + if (!ret.contains(s)) + ret << s; + } + } + return ret; +} + + +QList BlockView::blocks() const { + QList ret; + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->data(1006) == "item") + ret << qgraphicsitem_cast(i); + return ret; +} + + +QList BlockView::decors() const { + QList ret, gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if ((i->data(1009) == "decor") && !i->data(1008).toBool() && (i->parentItem() == 0) + && (i != &sel_rect) && (i != &tmp_bus) && !tmp_buses.contains((BlockBusItem*)i)) + ret << i; + return ret; +} + + +BlockBusItem * BlockView::connectionBus(BlockItem * b0, BlockItem * b1) const { + QList cbl = connectionBuses(b0, b1); + if (cbl.isEmpty()) return 0; + return cbl.front(); +} + + +QList BlockView::connectionBuses(BlockItem * b0, BlockItem * b1) const { + if (!b0 || !b1) return QList(); + QSet bs0 = b0->connectedBuses().toSet(), bs1 = b1->connectedBuses().toSet(); + return (bs0 & bs1).values(); +} + + +bool BlockView::connectPins(BlockItemPin * p0, BlockItemPin * p1) { + if (!p0 || !p1) return false; + if (p0 == p1) return false; + if (p0->busType() != p1->busType()) return false; + QList bl0 = p0->connectedBuses(), bl1 = p1->connectedBuses(); + if (!(bl0.toSet() & bl1.toSet()).isEmpty()) return true; + BlockBusItem * nb = new BlockBusItem(); + nb->setBusType(p0->busType()); + //nb->setEndpointsNumber(3); + loadBus(nb); + if (!bl0.isEmpty() && !bl1.isEmpty()) { // connect two existing buses + } else { + if ((bl0.isEmpty() && !bl1.isEmpty()) || (bl1.isEmpty() && !bl0.isEmpty())) { // connect empty pin to existing bus + BlockItemPin * ep = 0; + BlockBusItem * eb = 0; + if (bl0.isEmpty()) {ep = p0; eb = bl1[0];} + else {ep = p1; eb = bl0[0];} + double md = -1; int mi = -1; + QPointF sp = ep->scenePos(); + if (eb->pol.size() == 2) { + eb->selSegment = 0; + eb->addPoint((eb->pol[0] + eb->pol[1]) / 2.); + eb->selPoint = -1; + mi = 2; + } else { + for (int i = 0; i < eb->pol.size(); ++i) { + if (eb->ends.contains(i)) continue; + double cd = QVector2D(sp - eb->pol[i]).lengthSquared(); + if (md < 0 || md > cd) { + md = cd; + mi = i; + } + } + } + if (mi < 0) { + return false; + } + trace(ep->scenePos(), eb->pol[mi], nb); + if (nb->pol.size() < 2) { + delete nb; + return false; + } + nb->pol.pop_back(); + int lp = eb->pol.size(); + eb->pol << nb->pol; + for (int i = 0; i < nb->pol.size() - 1; ++i) + eb->segments << QPair(lp + i, lp + i + 1); + eb->segments << QPair(mi, lp + nb->pol.size() - 1); + eb->updateGeometry(); + delete nb; + } else { // connect two empty pins + trace(p0->scenePos(), p1->scenePos(), nb); + if (nb->pol.isEmpty()) { + delete nb; + return false; + } + addItem(nb); + } + } + reconnectAll(); + emitActionEvent(BlockItemBase::BusAdd, QList() << nb); + emit connectionsChanged(); + return true; +} + + +void BlockView::setTransform(const QTransform & matrix, bool combine) { + QGraphicsView::setTransform(matrix, combine); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::centerOn(const QPointF & pos) { + QGraphicsView::centerOn(pos); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::centerOn(qreal x, qreal y) { + QGraphicsView::centerOn(x, y); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::centerOn(const QGraphicsItem * item) { + QGraphicsView::centerOn(item); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::fitInView(const QRectF & rect, Qt::AspectRatioMode aspectRatioMode) { + QGraphicsView::fitInView(rect, aspectRatioMode); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::fitInView(qreal x, qreal y, qreal w, qreal h, Qt::AspectRatioMode aspectRatioMode) { + QGraphicsView::fitInView(x, y, w, h, aspectRatioMode); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::fitInView(const QGraphicsItem * item, Qt::AspectRatioMode aspectRatioMode) { + QGraphicsView::fitInView(item, aspectRatioMode); + updateNavRect(); + nav_target = _nav(); +} + + +void BlockView::fitInView() { + QRectF r = _nav(); + QGraphicsView::fitInView(itemsBoundingRect(), Qt::KeepAspectRatio); + updateNavRect(); + QRectF t = _nav(); + QGraphicsView::fitInView(r, Qt::KeepAspectRatio); + updateNavRect(); + animateNav(t); +} + + +QRectF BlockView::itemsBoundingRect() const { + QList gi = scene_->items(); + if (gi.isEmpty()) return QRectF(); + bool f = true; + QRectF ret; + foreach (QGraphicsItem * i, gi) + if (i->isVisible() && (i != &tmp_bus) && !tmp_buses.contains((BlockBusItem*)i)) { + if ((i->data(1007) != "item_selection") && !i->data(1008).toBool()) { + QRectF br = i->mapRectToScene(i->boundingRect()); + if (br.width() <= 1 || br.height() <= 1) continue; + if (f) ret = br; + else ret |= br; + f = false; + } + } + return ret; +} + + +void BlockView::restoreSelState() { + foreach (QGraphicsItem * i, sel_items) { + i->setPos(i->data(1001).toPointF()); + } + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->data(1005) == "connection") { + BlockBusItem * bi = qgraphicsitem_cast(i); + bi->pol = bi->bpol; + bi->prepareGeometryChange(); + } +} + + +void BlockView::saveSelState() { + QList gi = scene_->items(); + sel_items = scene_->selectedItems(); + foreach (QGraphicsItem * i, gi) { + i->setData(1000, i->isSelected()); + i->setData(1001, i->pos()); + if (i->data(1005) == "connection") + qgraphicsitem_cast(i)->bpol = qgraphicsitem_cast(i)->pol; + } +} + + +void BlockView::saveBusesState() { + QList bl = buses(); + foreach (BlockBusItem * b, bl) + b->saveState(); +} + + +void BlockView::restoreBusesState() { + QList bl = buses(); + foreach (BlockBusItem * b, bl) + b->restoreState(); +} + + +void BlockView::applySelRect(QGraphicsSceneMouseEvent * me) { + QList ci = sel_rect.collidingItems(Qt::IntersectsItemBoundingRect); + QList gi = scene_->items(); + bool add = me->modifiers().testFlag(Qt::ControlModifier); + if (!add) clearSelection(); + else { + foreach (QGraphicsItem * i, gi) + i->setSelected(i->data(1000).toBool()); + } + foreach (QGraphicsItem * i, ci) { + i->setSelected(!i->isSelected()); + } +} + + +void BlockView::applyGridStep() { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->type() == QGraphicsItem::UserType + 2) + qgraphicsitem_cast(i)->setGridStep(grid_step); +} + + +void BlockView::trace(QPointF scene_pos_from, QPointF scene_pos_to, BlockBusItem * bus, bool primary) { + if (primary) { + if (hpin) + scene_pos_to = hpin->scenePos(); + last_trace_from = scene_pos_from; + trace_to = scene_pos_to; + } + QRect sr = scene_->sceneRect().toRect(); + int dx = sr.left() / grid_step, dy = sr.top() / grid_step; + //qDebug() << dp; + QPoint dp(-dx, -dy), qpt = quantize(scene_pos_to, grid_step).toPoint() / grid_step + dp; + QTime tm; + tm.restart(); + wavetrace.resize(sr.size() / grid_step); + wavetrace.fill(BlockViewWavetrace::Empty); + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->data(1006) == "item") { + QRect ir = i->mapRectToScene(i->boundingRect()).toRect().normalized(); + wavetrace.fill(QRect(ir.topLeft() / grid_step + dp, ir.bottomRight() / grid_step + dp), BlockViewWavetrace::Blocked); + QVector pins = qgraphicsitem_cast(i)->pins(); + foreach (BlockItemPin * p, pins) { + if (p->busType() == bus->busType()) + wavetrace.fill(quantize(p->scenePos(), grid_step).toPoint() / grid_step + dp, BlockViewWavetrace::Empty); + } + } + if (m_trace_with_buses) { + foreach (QGraphicsItem * i, gi) + if (i->data(1005) == "connection") { + BlockBusItem * b = qgraphicsitem_cast(i); + if (!b) continue; + for (int s = 0; s < b->segments.size(); ++s) { + QPointF p0 = b->pol[b->segments[s].first], p1 = b->pol[b->segments[s].second], cp = p0; + double sx(0.), sy(0.), dx = qAbs(p1.x() - p0.x()), dy = qAbs(p1.y() - p0.y()); + double signx = (p1.x() >= p0.x() ? 1. : -1.), signy = (p1.y() >= p0.y() ? 1. : -1.); + int steps(0); + if ((dx + dy) < grid_step) continue; + if (dx >= dy) { // by x + sx = grid_step; + sy = sx * dy / dx; + steps = qRound(dx / grid_step); + } else { + sy = grid_step; + sx = sy * dx / dy; + steps = qRound(dy / grid_step); + } + sx *= signx; + sy *= signy; + //qDebug() << "fill" << p0 << "->" << p1 << "in" << steps << sx << sy; + for (int j = 0; j < steps; ++j) { + QPoint tp = quantize(cp, grid_step).toPoint() / grid_step + dp; + if (tp != qpt) + wavetrace.fill(tp, BlockViewWavetrace::Blocked); + //qDebug() << " set" << cp; + cp += QPointF(sx, sy); + } + } + } + } + bus->clear(); + if (wavetrace.trace(quantize(scene_pos_from, grid_step).toPoint() / grid_step + dp, qpt)) { + wavetrace.gatherPath(); + foreach (const QPoint & p, wavetrace.path()) { + bus->appendPoint((p - dp) * grid_step); + } + } + //qDebug() << quantize(scene_pos_from, grid_step).toPoint() / grid_step + dp << qpt; + scene_->update(); +} + + +void BlockView::clearBusStates() { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->data(1005) == "connection") { + BlockBusItem * b = qgraphicsitem_cast(i); + b->clearBusState(); +// if (i != bus_from && i != match_bus) + //b->selPoint = b->selSegment = -1; + } +} + + +void BlockView::matchBus() { + match_bus = 0; + bool bv = tmp_bus.isVisible(); + QList gi = scene_->items(); + QList buses; + QList blockl; + int sp = -1, ss = -1; + QPointF point; + iconnect = false; + if (!tmp_bus.pol.isEmpty()) + point = tmp_bus.pol.back(); + foreach (QGraphicsItem * i, gi) { + if (i != bus_from) { + if (i->data(1005) == "connection") + buses << qgraphicsitem_cast(i); + if (i->data(1006) == "item") + blockl << qgraphicsitem_cast(i); + } + } + foreach (BlockBusItem * b, buses) { + b->clearBusState(); + b->selPoint = b->selSegment = -1; + } + if (!bv) return; + BlockBusItem * b(0); + if (m_pin_mc) { + foreach (BlockItem * b_, blockl) + foreach (BlockItemPin * p_, b_->pins()) + if (p_->scenePos() == point) { + + return; + } + } + //qDebug() << "1" << buses.size() << tmp_bus.pol; + for (int i = 0; i < buses.size(); ++i) { + b = buses[i]; + b->testPoint(point, &sp, &ss); + //qDebug() << i << sp << ss; + if (sp >= 0 || ss >= 0) break; + } + if ((sp < 0 && ss < 0) || b == 0) return; + //qDebug("2"); + match_bus = b; + if ((b->busType() != tmp_bus.busType()) || b->connections_.value(sp, 0) != 0) { + b->setBusState(false); + } else { + if (b->max_ep >= 2) { + if (bus_from == 0) { + if (ss >= 0) { + if (b->endpointCount() >= b->max_ep) { + b->setBusState(false); + return; + } + } + if (sp >= 0) { + if (b->endpointCount() + b->pointSegmentsCount(sp) - 2 >= b->max_ep) { + b->setBusState(false); + return; + } + } + } else { + int sep = b->endpointCount() + bus_from->endpointCount(); + if (b->pointSegmentsCount(sp) == 1) sep--; + if (bus_from->selPoint >= 0) + if (bus_from->pointSegmentsCount(bus_from->selPoint) == 1) sep--; + if (sep > b->max_ep) { + b->setBusState(false); + return; + } + } + } + //b->press_point = point; + iconnect = true; + b->setBusState(true); + b->selPoint = sp; + b->selSegment = ss; + } +} + + +bool BlockView::connectTmpToBus(BlockBusItem * bus) { + if (bus == 0) return false; + if (!bus->busState()) return false; + if (tmp_bus.pol.size() < 2) return false; + int np = bus->selPoint; + if (np < 0) + np = bus->addPoint(tmp_bus.pol.back()); + if (np < 0) return false; + tmp_bus.pol.pop_back(); + int lp = bus->pol.size(); + bus->pol << tmp_bus.pol; + for (int i = 0; i < tmp_bus.pol.size() - 1; ++i) + bus->segments << QPair(lp + i, lp + i + 1); + bus->segments << QPair(np, lp + tmp_bus.pol.size() - 1); + bus->updateGeometry(); + tmp_bus.clear(); + return true; +} + + +void BlockView::markPins(int bus_type) { + unhoverPins(); + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) { + if (i->data(1004) == "pin") { + BlockItemPin * p = qgraphicsitem_cast(i); + p->saveState(); + if (m_pin_mc) { + if (p->busType() == bus_type) + p->setState(BlockItemPin::Accept); + } else { + if (p->busType() == bus_type) { + if (p->state() == BlockItemPin::Disconnected) + p->setState(BlockItemPin::Accept); +// else +// p->animAccept(); + } + } + } + } +} + + +void BlockView::unmarkPins(bool to_normal) { + unhoverPins(); + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) { + if (i->data(1004) == "pin") { + qgraphicsitem_cast(i)->restoreState(); + if (to_normal) + while (qgraphicsitem_cast(i)->restoreState()); + } + } +} + + +void BlockView::hoverAcceptedPin(BlockItemPin * pin, bool hover) { + if (!pin) return; + pin->enlargePin(hover); +} + + +void BlockView::unhoverPins(BlockItemPin* excl_pin) { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) { + if (excl_pin == ((BlockItemPin*)i)) continue; + if (i->data(1004) == "pin") { + ((BlockItemPin*)i)->enlargePin(false); + } + } +} + + +void BlockView::simplifyBuses() { + QList bl = buses(); + foreach (BlockBusItem * b, bl) + b->simplify(); +} + + +void BlockView::moveBuses(const QList & items, QPointF dp) { + if (dp.isNull()) return; + QList gi = scene_->items(); + QVector pins; + QList buses; + //qDebug() << "move" << dp; + foreach (QGraphicsItem * i, items) + if (i->data(1006) == "item" && i->flags().testFlag(QGraphicsItem::ItemIsMovable)) + pins << qgraphicsitem_cast(i)->pins(); + foreach (QGraphicsItem * i, gi) + if (i->data(1005) == "connection") + buses << qgraphicsitem_cast(i); + foreach (BlockBusItem * b, buses) { + QList bpins = b->connections_.values(); + if (!bpins.isEmpty()) { + foreach (BlockItemPin * p, pins) + bpins.removeAll(p); + if (bpins.isEmpty()) { + b->movePolyline(dp); + continue; + } + } + foreach (BlockItemPin * p, pins) { + QList ends = b->connections_.keys(p); + for (int i = 0; i < ends.size(); ++i) { + /*int isp = b->segmentPointPair(ends[i]); + QPointF sdp; + if (isp >= 0 && b->pol.size() > 2) { + sdp = b->pol[isp] - b->pol[ends[i]]; + if (!sdp.isNull()) { + if (sdp.x() == 0. && dp.x() != 0.) + b->movePoint(isp, QPointF(dp.x(), 0.)); + if (sdp.y() == 0. && dp.y() != 0.) + b->movePoint(isp, QPointF(0., dp.y())); + } else { + if (p->alignment() == Qt::AlignTop || p->alignment() == Qt::AlignBottom) + b->movePoint(isp, QPointF(dp.x(), 0.)); + if (p->alignment() == Qt::AlignLeft || p->alignment() == Qt::AlignRight) + b->movePoint(isp, QPointF(0., dp.y())); + } + }*/ + QPointF pdp = dp; + double ang = 0.; + switch (p->alignment()) { + case Qt::AlignRight : pdp.setX(0.); ang = 0.; break; + case Qt::AlignTop : pdp.setY(0.); ang = 90.; break; + case Qt::AlignLeft : pdp.setX(0.); ang = 180.; break; + case Qt::AlignBottom: pdp.setY(0.); ang = 270.; break; + default: break; + } + QVector epl = b->endpointLine(ends[i], ang); + foreach (int e, epl) + b->movePoint(e, pdp); + b->movePoint(ends[i], dp); + } + } + } +} + + +QList BlockView::internalBuses(const QList & items) { + QList ret; + if (items.isEmpty()) return ret; + QList sbl = buses(); + QSet sis = QSet::fromList(items); + foreach (BlockBusItem * bi, sbl) { + if (bi->connectedBlocks().isEmpty()) continue; + QSet bis = QSet::fromList(bi->connectedBlocks()); + if ((bis - sis).isEmpty()) + ret << bi; + } + return ret; +} + + +QList BlockView::nearPins(BlockItemPin * pin, Qt::KeyboardModifiers km) { + QList ret; + bool up = km.testFlag(Qt::ShiftModifier); + bool down = km.testFlag(Qt::ControlModifier); + bool ab = km.testFlag(Qt::AltModifier); + //qDebug() << "nearPins" << km; + if (!pin || (!up && !down)) return ret; + const BlockItem * src = pin->parent(); + if (!src) return ret; + bool dirs[2] = {up, down}; + double dy[2] = {-1, 1}; + for (int i = 0; i < 2; ++i) { + if (!dirs[i]) continue; + const BlockItem * cb = src; + BlockItemPin * cpin = pin; + //qDebug() << "dir" << dirs[i]; + while (cpin) { + //qDebug() << "cur" << cpin; + QList il = scene_->items(cpin->scenePos() + QPointF(0., dy[i] * cb->pinsMargin())); + BlockItemPin * np = getPin(il); + if (np == cpin) break; + cpin = np; + if (cpin) { + if (((cb != cpin->parent()) && !ab) || + (cpin->state() != BlockItemPin::Disconnected)) { + break; + } + cb = cpin->parent(); + ret << cpin; + } + } + } + return ret; +} + + +BlockItemPin * BlockView::getPin(const QList & list) const { + foreach (QGraphicsItem * i, list) { + if (i->data(1004) == "pin") + return qgraphicsitem_cast(i); + } + return 0; +} + + +void BlockView::highlightNearPins(BlockItemPin * pin, Qt::KeyboardModifiers km) { + //qDebug() << "restore for" << last_multiconnect_pl.size(); + foreach (BlockItemPin * p, last_multiconnect_pl) + p->restoreState(); + QList pl = nearPins(pin, km); + foreach (BlockItemPin * p, pl) { + p->saveState(); + p->setState(BlockItemPin::Hover); + } + last_multiconnect_pl = pl; +} + + +void BlockView::hideTmpBuses(bool clear) { + tmp_bus.hide(); + if (clear) { + tmp_bus.clear(); + qDeleteAll(tmp_buses); + tmp_buses.clear(); + } +} + + +QList BlockView::selectedBlocks() const { + QList ret; + QList sil = scene()->selectedItems(); + foreach (QGraphicsItem * b, sil) + if (b->data(1006) == "item") + ret << qgraphicsitem_cast(b); + return ret; +} + + +QList BlockView::selectedDecors() const { + QList ret, sil = decors(); + foreach (QGraphicsItem * b, sil) + if (b->isSelected()) + ret << b; + return ret; +} + + +void BlockView::animateNav(QRectF d, double scl) { + nav_target = d; + if (is_nav_anim) { + if (qAbs(scl - 1.) <= 0.1 && (nav_anim.state() != QAbstractAnimation::Running)) { + _setNav(d); + return; + } + if (nav_anim.state() != QAbstractAnimation::Running) { + nav_prev_aa = renderHints().testFlag(QPainter::Antialiasing); + nav_prev_imaa = renderHints().testFlag(QPainter::SmoothPixmapTransform); + nav_prev_grid = isGridVisible(); + setRenderHint(QPainter::Antialiasing, false); + setRenderHint(QPainter::SmoothPixmapTransform, false); + setGridVisible(false); + } + nav_anim.stop(); + nav_anim.setStartValue(_nav()); + nav_anim.setEndValue(d); + nav_anim.start(); + } else + _setNav(d); +} + + +void BlockView::adjustThumb() { + if (!scene()) return; + QSizeF sr = sceneRect().size(), tr; + double scl; + if (sr.width() >= sr.height()) + scl = thumb_size.width() / sr.width(); + else + scl = thumb_size.height() / sr.height(); + tr = sr * scl; + int sx = 0, sy = 0; + if (verticalScrollBar()->isVisible() && (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) + sx = verticalScrollBar()->width(); + if (horizontalScrollBar()->isVisible() && (horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)) + sy = horizontalScrollBar()->height(); + widget_thumb.setGeometry(QRect(QPoint(width() - tr.width() - 10 - sx, + height() - tr.height() - 10 - sy), tr.toSize())); +} + + +void BlockView::newBranch(BlockBusItem * item) { + bus_from = item; + prev_tcb = m_trace_with_buses; + newBusStarted(item->busType()); + markPins(item->busType()); + new_branch = true; + tmp_bus.setBusType(item->busType()); + if (item->selSegment >= 0) { + QPointF ds(item->pol[item->segments[item->selSegment].first] - item->pol[item->segments[item->selSegment].second]); + if (ds.x() == 0.) wavetrace.setPreferredDirection(BlockViewWavetrace::Horizontal); + if (ds.y() == 0.) wavetrace.setPreferredDirection(BlockViewWavetrace::Vertical); + } +} + + +void BlockView::startBusPointMove(int bus_type) { + move_bus_point = true; + prev_tcb = m_trace_with_buses; + newBusStarted(bus_type); + markPins(bus_type); +} + + +void BlockView::endBusPointMove() { + move_bus_point = false; + unmarkPins(); + reconnectAll(); +} + + +void BlockView::pinHoverInOut(BlockItemPin * pin) { + //qDebug() << "pinHoverInOut" << pin << pin->state(); + highlightNearPins(pin, QApplication::keyboardModifiers()); +} + + +void BlockView::checkPaste(bool queued) { + const QMimeData * mime = QApplication::clipboard()->mimeData(); + bool ret = false; + if (mime) + ret = mime->hasFormat(_BlockView_Mime_); + if (queued) QMetaObject::invokeMethod(this, "pasteEnabledChanged", Qt::QueuedConnection, Q_ARG(bool, ret)); + else emit pasteEnabledChanged(ret); +} + + + +void BlockView::newBranchTrace(BlockBusItem * item, QPointF to) { + trace(item->press_pos, to, &tmp_bus); + tmp_bus.show(); +} + + +void BlockView::newBranchAccept(BlockBusItem * item) { + unmarkPins(); + if (!new_branch) return; + new_branch = false; + tmp_bus.hide(); + if (tmp_bus.pol.size() < 2) return; + tmp_bus.pol.pop_front(); + int np = item->addPoint(item->press_pos); + if (np < 0) return; + if (match_bus) { + if (iconnect) tmp_bus.pol.pop_back(); + else return; + } + if (item == match_bus) return; + int snp = np; + int lp = item->pol.size(); + if (!tmp_bus.pol.isEmpty()) { + item->pol << tmp_bus.pol; + item->segments << QPair(np, lp); + for (int i = 0; i < tmp_bus.pol.size() - 1; ++i) + item->segments << QPair(lp + i, lp + i + 1); + } + if (match_bus != 0) { + if (!iconnect) return; + np = match_bus->selPoint; + if (np < 0) + np = match_bus->addPoint(trace_to); + if (np < 0) return; + lp = item->pol.size(); + item->pol << match_bus->pol; + for (int i = 0; i < match_bus->segments.size(); ++i) + item->segments << QPair(match_bus->segments[i].first + lp, match_bus->segments[i].second + lp); + if (tmp_bus.pol.isEmpty()) + item->segments << QPair(lp + np, snp); + else + item->segments << QPair(lp + np, lp - 1); + match_bus->setProperty("_nodelete_", true); + match_bus->deleteLater(); + match_bus = 0; + } + item->updateGeometry(); + emitActionEvent(BlockItemBase::BusAdd, QList() << item); + emit connectionsChanged(); + tmp_bus.clear(); +} + + +void BlockView::newBranchCancel() { + unmarkPins(); + //qDebug() << "cancel"; + new_branch = false; + hideTmpBuses(); +} + + +void BlockView::removedBus(QObject * o) { + mm_ci = 0; + reconnectAll(); + BlockBusItem * bus = (BlockBusItem*)o; + if (bus->property("_nodelete_").toBool()) return; + emitActionEvent(BlockItemBase::BusRemove, QList() << bus); + emit connectionsChanged(); +} + + +void BlockView::removedBlock(QObject * o) { + emit blockRemoved((BlockItem*)o); + emitActionEvent(BlockItemBase::BlockRemove, QList() << qgraphicsitem_cast((BlockItem*)o)); +} + + +void BlockView::removeJunk() { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) { + if (i->data(1005) != "connection") continue; + BlockBusItem * b = qgraphicsitem_cast(i); + if (b->pol.size() <= 1) { + b->deleteLater(); + } + } +} + + +void BlockView::sceneSelectionChanged() { + bool ie = scene()->selectedItems().isEmpty(); + emit copyEnabledChanged(!ie); +} + + +void BlockView::_setThumb(double v) { + _talpha = v; + QMetaObject::invokeMethod(&widget_thumb, "update", Qt::QueuedConnection); + if (_talpha <= 0.01) widget_thumb.hide(); + else widget_thumb.show(); +} + + +void BlockView::_setNav(QRectF v) { + double vw = viewport()->width(), vh = viewport()->height(); + if (vw < 1. || vh < 1. || v.isEmpty()) return; + QTransform matrix; + double scl = qMin(vw / v.width(), vh / v.height()); + double ascl = appScale(this); + if (scl > 0.02 * ascl && scl < 50.0 * ascl) { + matrix.scale(scl, scl); + nav_rect = v; + QGraphicsView::setTransform(matrix); + QGraphicsView::centerOn(v.center()); + } +} + + +QRectF BlockView::_nav() const { + return nav_rect; +} + + +void BlockView::_navFinished() { + setRenderHint(QPainter::Antialiasing, nav_prev_aa); + setRenderHint(QPainter::SmoothPixmapTransform, nav_prev_imaa); + setGridVisible(nav_prev_grid); +} + + +void BlockView::reconnectAll() { + //qDebug() << "reconnect"; + removeJunk(); + QList gi = scene_->items(); + QList pins; + QList buses; + foreach (QGraphicsItem * i, gi) { + if (i->data(1004) == "pin") + pins << qgraphicsitem_cast(i); + if (i->data(1005) == "connection") + buses << qgraphicsitem_cast(i); + } + foreach (BlockItemPin * p, pins) { + p->clearStateStack(); + p->setState(BlockItemPin::Disconnected); + p->buses_.clear(); + } + foreach (BlockBusItem * b, buses) { + b->connections_.clear(); + QVector conns(b->endpoints()); + for (int c = 0; c < conns.size(); ++c) { + QPointF cp = b->pol[conns[c]]; + for (int j = 0; j < pins.size(); ++j) { + if (!pins[j]->isVisible()) continue; + QPointF pp = pins[j]->scenePos(); + if ((cp - pp).manhattanLength() <= (grid_step / 2.)) { + //qDebug() << "found"; + if (b->busType() == pins[j]->busType()) { + b->connections_[conns[c]] = pins[j]; + if (!pins[j]->buses_.contains(b)) + pins[j]->buses_ << b; + pins[j]->setState(BlockItemPin::Connected); + } else + pins[j]->setState(BlockItemPin::Reject); + break; + } + } + } + } + //qDebug() << pins.size() << buses.size(); +} + + +void BlockView::zoom(double factor) { + if (!is_nav_anim || (nav_anim.state() != QPropertyAnimation::Running)) + nav_target = _nav(); + QRectF r = nav_target; + QPoint mp; + if (underMouse()) mp = mapFromGlobal(QCursor::pos()); + else mp = QPoint(viewport()->width() / 2, viewport()->height() / 2); + double cx = double(mp.x()) / viewport()->width(), cy = double(mp.y()) / viewport()->height(); + double pw = r.width(), ph = r.height(); + r.setWidth(r.width() / factor); + r.setHeight(r.height() / factor); + r.translate(cx * (pw - r.width()), cy * (ph - r.height())); + animateNav(r); +} + + +void BlockView::zoomReset() { + cur_scl = qSqrt(transform().determinant()); + zoom(prev_app_scale / cur_scl); +} + + +void BlockView::copyToClipboard() { + QList bll = selectedBlocks(); + QList del = selectedDecors(); + //qDebug() << "copy" << bll.size() << del.size(); + if (bll.isEmpty() && del.isEmpty()) return; + QList bul = internalBuses(bll); + QByteArray ba; + QDataStream s(&ba, QIODevice::ReadWrite); + s << uint(0x89abcdef) << bll << bul << del; + QMimeData * mime = new QMimeData(); + mime->setData(_BlockView_Mime_, ba); + QApplication::clipboard()->setMimeData(mime); +} + + +void BlockView::pasteFromClipboard() { + const QMimeData * mime = QApplication::clipboard()->mimeData(); + if (!mime) return; + if (!mime->hasFormat(_BlockView_Mime_)) return; + QByteArray ba = mime->data(_BlockView_Mime_); + //qDebug() << "paste" << ba.size(); + if (ba.size() < 4) return; + QList bll; + QList bul; + QList del, gl; + uint hdr = 0; + QDataStream s(ba); + s >> hdr; + if (hdr != 0x89abcdef) return; + s >> bll >> bul >> del; + int all = bll.size() + bul.size() + del.size(); + if (all == 0) return; + QRectF br; + foreach (BlockItem * b, bll) { + br |= b->boundingRect().translated(b->pos()); + gl << b; + } + foreach (BlockBusItem * b, bul) + gl << b; + foreach (QGraphicsItem * b, del) + br |= b->boundingRect().translated(b->pos()); + gl << del; + QPointF copy_dp; + if (underMouse()) copy_dp = mapToScene(mapFromGlobal(QCursor::pos())); + else copy_dp = mapToScene(rect().center()); + copy_dp -= br.center(); + copy_dp = quantize(copy_dp, grid_step); + ae_enabled = false; + if (!bll.isEmpty()) copyBlocks(bll, copy_dp); + if (!bul.isEmpty()) copyBuses(bul, copy_dp); + foreach (QGraphicsItem * i, del) + i->setPos(i->pos() + copy_dp); + addItems(del); + ae_enabled = true; + emitActionEvent(BlockItemBase::Paste, gl); +} + + +void BlockView::selectNone() { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + i->setSelected(false); +} + + +void BlockView::selectAll() { + QList gi = scene_->items(); + foreach (QGraphicsItem * i, gi) + if (i->flags().testFlag(QGraphicsItem::ItemIsSelectable)) + i->setSelected(true); +} + + +void BlockView::removeSelected() { + QList gi = scene_->selectedItems(), ai; + blockSignals(true); + QList sbuses = buses(), dbuses, wbuses = wrongConnectedBuses(); + //foreach (BlockBusItem * i, wbuses) + // sbuses.removeOne(i); + foreach (BlockBusItem * i, sbuses) + if (i->connectedBlocks().isEmpty()) + dbuses << i; + foreach (QGraphicsItem * i, gi) { + if (i->data(1006) == "item") + ai << qgraphicsitem_cast(i); + if ((i->data(1006) == "item") || (i->data(1005) == "connection") || (i->data(1009) == "decor")) { + scene_->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); + delete i; + } + } + reconnectAll(); + foreach (BlockBusItem * i, sbuses) + if (i->connectedBlocks().isEmpty()) + if (!dbuses.contains(i)) + delete i; + + blockSignals(false); + foreach (QGraphicsItem * i, ai) + emit blockRemoved((BlockItem*)i); + emitActionEvent(BlockItemBase::BlockRemove, ai); +} + + +void BlockView::removeAll() { + last_multiconnect_pl.clear(); + QList gi = scene_->items(), ai; + blockSignals(true); + foreach (QGraphicsItem * i, gi) { + if (i->data(1006) == "item") + ai << qgraphicsitem_cast(i); + if ((i->data(1006) == "item") || (i->data(1005) == "connection") || (i->data(1009) == "decor")) { + if ((i != &sel_rect) && (i != &tmp_bus) && (i->parentItem() == 0) && !(i->data(1008).toBool())) { + //qDebug() << "delete" << i->data(1004); + scene_->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close)); + delete i; + } + } + } + blockSignals(false); + foreach (QGraphicsItem * i, ai) + emit blockRemoved((BlockItem*)i); +} diff --git a/test/qad/blockview/blockview.h b/test/qad/blockview/blockview.h new file mode 100644 index 0000000..9bfa46f --- /dev/null +++ b/test/qad/blockview/blockview.h @@ -0,0 +1,269 @@ +#ifndef BLOCKVIEW_H +#define BLOCKVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "blockviewwavetrace.h" +#include "blockbusitem.h" + +/// data: +/// 1002 - flag for move parent (true) +/// 1003 - flag for visualize selection (true) +/// 1004 - BlockItemPin ("pin") +/// 1005 - BlockBusItem ("connection") +/// 1006 - BlockItem ("item") +/// 1007 - BlockItem selection ("item_selection") +/// 1008 - item is NOT decor, ignore for function decors() (true) +/// 1009 - item is scene decor ("decor") +/// 1010 - BlockItem decor (src text for QGraphicsSimpleTextItem) +/// 1011 - item is BlockItem decor ("decor") +/// 1100 - flag for correct move (true) + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +Q_DECLARE_METATYPE(BlockItem*) +Q_DECLARE_METATYPE(BlockItemPin*) +Q_DECLARE_METATYPE(BlockBusItem*) + +class BlockView: public QGraphicsView +{ + Q_OBJECT + Q_ENUMS(SelectionMode) + + Q_PROPERTY(bool gridVisible READ isGridVisible WRITE setGridVisible) + Q_PROPERTY(bool snapToGrid READ isSnapToGrid WRITE setSnapToGrid) + Q_PROPERTY(QPen gridPen READ gridPen WRITE setGridPen) + Q_PROPERTY(double gridStep READ gridStep WRITE setGridStep) + Q_PROPERTY(double gridPointsWidth READ gridPointsWidth WRITE setGridPointsWidth) + Q_PROPERTY(SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + Q_PROPERTY(bool postMoveConnect READ isPostMoveConnectEnabled WRITE setPostMoveConnectEnabled) + Q_PROPERTY(bool connectByMouse READ isConnectByMouseEnabled WRITE setConnectByMouseEnabled) + Q_PROPERTY(bool navigationEnabled READ isNavigationEnabled WRITE setNavigationEnabled) + Q_PROPERTY(bool navigateAnimationEnabled READ isNavigateAnimationEnabled WRITE setNavigateAnimationEnabled) + Q_PROPERTY(bool blockAnimationEnabled READ isBlockAnimationEnabled WRITE setBlockAnimationEnabled) + Q_PROPERTY(bool traceConsiderBuses READ isTraceConsiderBuses WRITE setTraceConsiderBuses) + Q_PROPERTY(bool pinMulticonnect READ isPinMulticonnectEnabled WRITE setPinMulticonnectEnabled) + Q_PROPERTY(bool miniMap READ isMiniMapEnabled WRITE setMiniMapEnabled) + Q_PROPERTY(bool zoomWheelOnly READ isZoomWheelOnly WRITE setZoomWheelOnly) + + Q_PROPERTY(double _thumb READ _thumb WRITE _setThumb DESIGNABLE false SCRIPTABLE false) + Q_PROPERTY(QRectF _nav READ _nav WRITE _setNav DESIGNABLE false SCRIPTABLE false) + +public: + BlockView(QWidget * parent = 0); + BlockView(QGraphicsScene * scene, QWidget * parent = 0); + ~BlockView(); + + enum SelectionMode { + NoSelection, + SingleSelection, + MultiSelection + }; + + QPen gridPen() const {return grid_pen;} + bool isGridVisible() const {return grid_visible;} + bool isSnapToGrid() const {return grid_snap;} + bool isPostMoveConnectEnabled() const {return pm_connect;} + bool isNavigationEnabled() const {return navigation;} + bool isNavigateAnimationEnabled() const {return is_nav_anim;} + bool isBlockAnimationEnabled() const {return is_block_anim;} + bool isConnectByMouseEnabled() const {return m_connect;} + bool isTraceConsiderBuses() const {return m_trace_with_buses;} + bool isPinMulticonnectEnabled() const {return m_pin_mc;} + bool isMiniMapEnabled() const {return minimap;} + bool isZoomWheelOnly() const {return wheel_zoom;} + double gridStep() const {return grid_step;} + double gridPointsWidth() const {return grid_points;} + SelectionMode selectionMode() const {return smode;} + void setSelectionMode(SelectionMode mode) {smode = mode;} + + void addItems(QList items) {foreach (QGraphicsItem * i, items) addItem(i);} + QList buses() const; + QList wrongConnectedBuses() const; + QList blocks() const; + QList decors() const; + BlockBusItem * connectionBus(BlockItem * b0, BlockItem * b1) const; + QList connectionBuses(BlockItem * b0, BlockItem * b1) const; + bool connectPins(BlockItemPin * p0, BlockItemPin * p1); + QList selectedBlocks() const; + QList selectedDecors() const; + + void setTransform(const QTransform & matrix, bool combine = false); + void centerOn(const QPointF & pos); + void centerOn(qreal x, qreal y); + void centerOn(const QGraphicsItem * item); + + void fitInView(const QRectF & rect, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio); + void fitInView(qreal x, qreal y, qreal w, qreal h, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio); + void fitInView(const QGraphicsItem * item, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio); + void fitInView(); + + QRectF itemsBoundingRect() const; + +protected: + void _init(); + void _updateBack(); + bool event(QEvent * e); + bool eventFilter(QObject * o, QEvent * e); + void stopTimer(int & tid); + void restartTimer(int & tid, int msecs); + void timerEvent(QTimerEvent * e); + void wheelEvent(QWheelEvent * e); + void mousePressEvent(QMouseEvent * event); + void mouseReleaseEvent(QMouseEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void mouseDoubleClickEvent(QMouseEvent * event); + void keyPressEvent(QKeyEvent * event); + void keyReleaseEvent(QKeyEvent * e); + void resizeEvent(QResizeEvent * event); + void scrollContentsBy(int dx, int dy); + void drawBackground(QPainter * painter, const QRectF & rect); + void drawThumb(); + void drawSceneThumb(); + void thumbHide(); + void thumbShow(); + void restoreSelState(); + void saveSelState(); + void saveBusesState(); + void restoreBusesState(); + void applySelRect(QGraphicsSceneMouseEvent * me); + void applyGridStep(); + void trace(QPointF scene_pos_from, QPointF scene_pos_to, BlockBusItem * bus, bool primary = true); + void clearBusStates(); + void matchBus(); + bool connectTmpToBus(BlockBusItem* bus); + void markPins(int bus_type); + void unmarkPins(bool to_normal = false); + void hoverAcceptedPin(BlockItemPin * pin, bool hover); + void unhoverPins(BlockItemPin * excl_pin = 0); + void simplifyBuses(); + void moveBuses(const QList & items, QPointF dp); + QList internalBuses(const QList & items); + QList nearPins(BlockItemPin * pin, Qt::KeyboardModifiers km); + BlockItemPin * getPin(const QList & list) const; + void highlightNearPins(BlockItemPin * pin, Qt::KeyboardModifiers km); + void hideTmpBuses(bool clear = true); + double _thumb() const {return _talpha;} + QRectF _nav() const; + void animateNav(QRectF d, double scl = 0.); + void scrollFromThumb(); + void deleteCopyTemp(); + void emitActionEvent(BlockItemBase::Action action, QList items); + void setGhost(BlockItem * item); + void clearGhost(); + BlockItem * ghost() const {return ghost_;} + + virtual void loadBus(BlockBusItem * bus) {} + virtual void copyBlocks(QList items, QPointF offset) {} + virtual void copyBuses(QList items, QPointF offset) {} + virtual void newBusStarted(int bus_type) {} + + QGraphicsScene * scene_; + QGraphicsRectItem sel_rect; + QGraphicsItem * mm_ci; + QList sel_items; + QList copy_items; + QList last_multiconnect_pl; + QList copy_buses, tmp_buses; + BlockBusItem tmp_bus, * match_bus, * bus_from; + BlockItemPin * hpin; + BlockItem * ghost_; + BlockViewWavetrace wavetrace; + QPoint press_point, screen_point, thumb_press; + QPointF scene_point, trace_from, last_trace_from, trace_to, copy_dp; + QRect thumb_sr, thumb_vr; + QRectF nav_target, nav_rect; + QSizeF thumb_size; + QPen grid_pen; + SelectionMode smode; + QImage im_scene; + QWidget widget_thumb; + Qt::KeyboardModifiers mm_mods; + QPropertyAnimation thumb_anim, nav_anim; + int timer_thumb, thumb_hide_delay, thick; + bool mm_drag, new_bus, new_branch, moved, mm_cancel, iconnect, mm_copy, mm_thumb, ae_enabled, is_nav_anim, is_block_anim, move_bus_point; + bool grid_visible, grid_snap, pm_connect, navigation, m_connect, m_trace_with_buses, m_pin_mc, minimap, prev_tcb, wheel_zoom; + bool nav_prev_aa, nav_prev_imaa, nav_prev_grid; + double grid_step, grid_points, cur_scl, _talpha, thumb_scl; + double prev_app_scale; + +protected slots: + void getPinMC(bool * v) {if (v) *v = m_pin_mc;} + void adjustThumb(); + void newBranch(BlockBusItem * item); + void newBranchTrace(BlockBusItem * item, QPointF to); + void newBranchAccept(BlockBusItem * item); + void newBranchCancel(); + void removedBus(QObject * o); + void removedBlock(QObject * o); + void removeJunk(); + void sceneSelectionChanged(); + void updateNavRect(); + void scrolled(); + void _setThumb(double v); + void _setNav(QRectF v); + void _navFinished(); + void startBusPointMove(int bus_type); + void endBusPointMove(); + void pinHoverInOut(BlockItemPin * pin); + void checkPaste(bool queued = false); + +public slots: + void setGridPen(const QPen & pen) {grid_pen = pen; _updateBack();} + void setGridVisible(bool yes) {grid_visible = yes; _updateBack();} + void setSnapToGrid(bool yes) {grid_snap = yes;} + void setGridStep(double step) {grid_step = step; applyGridStep(); _updateBack();} + void setGridPointsWidth(double width_) {grid_points = width_; _updateBack();} + void setPostMoveConnectEnabled(bool on) {pm_connect = on;} + void setNavigationEnabled(bool on) {navigation = on;} + void setNavigateAnimationEnabled(bool on) {is_nav_anim = on;} + void setBlockAnimationEnabled(bool on) {is_block_anim = on;} + void setConnectByMouseEnabled(bool on) {m_connect = on;} + void setTraceConsiderBuses(bool on) {m_trace_with_buses = on;} + void setPinMulticonnectEnabled(bool on) {m_pin_mc = on;} + void setMiniMapEnabled(bool on) {minimap = on;} + void setZoomWheelOnly(bool on) {wheel_zoom = on;} + + void zoom(double factor); + void zoomIn() {zoom(1.2);} + void zoomOut() {zoom(1. / 1.2);} + void zoomReset(); + + void copyToClipboard(); + void pasteFromClipboard(); + + void reconnectAll(); + void selectNone(); + void selectAll(); + void removeSelected(); + void removeAll(); + void clearSelection(); + void addItem(QGraphicsItem * item, bool emit_action = true); + +signals: + void blockDoubleClicked(BlockItem * ); + void blockHoverEnter(BlockItem * ); + void blockHoverLeave(BlockItem * ); + void busDoubleClicked(BlockBusItem * ); + void schemeAction(BlockItemBase::Action action, QList items); + void blockRemoved(BlockItem * item); + void connectionsChanged(); + void copyEnabledChanged(bool); + void pasteEnabledChanged(bool); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // BLOCKVIEW_H diff --git a/test/qad/blockview/blockviewwavetrace.cpp b/test/qad/blockview/blockviewwavetrace.cpp new file mode 100644 index 0000000..867631a --- /dev/null +++ b/test/qad/blockview/blockviewwavetrace.cpp @@ -0,0 +1,202 @@ +#include "blockviewwavetrace.h" +#include + + +BlockViewWavetrace::BlockViewWavetrace(int width, int height) { + max_steps = 512; + resize(width, height); + setPreferredDirection(BlockViewWavetrace::Horizontal); +} + + +void BlockViewWavetrace::resize(int width, int height) { + wid = width; + hei = height; + if (field.size() != wid) + field.resize(wid); + for (int i = 0; i < wid; ++i) { + if (field[i].size() != hei) { + field[i].resize(hei); + field[i].fill(-1); + } + } +} + + +void BlockViewWavetrace::fill(short val) { + QTime tm; tm.restart(); + for (int i = 0; i < wid; ++i) { + if (i == 0) + field[i].fill(val); + else + memcpy(field[i].data(), field[0].constData(), hei * sizeof(field[0][0])); + } +} + + +void BlockViewWavetrace::fill(const QRect & rect, short val) { + for (int i = rect.left(); i <= rect.right(); ++i) + for (int j = rect.top(); j <= rect.bottom(); ++j) + field[i][j] = val; +} + + +bool BlockViewWavetrace::trace(const QPoint & start, const QPoint & finish) { + st = start; + fn = finish; + if (dir_ == NoTrace) return true; + //qDebug() << "trace" << start << finish; + //return true; + int cx, cy; + short cl = 0, empty = (short)BlockViewWavetrace::Empty; + QRect frect(0, 0, wid - 1, hei - 1); + QVector cpnts, npnts; + fill(st, cl); + cpnts.push_back(st); + if (field[fn.x()][fn.y()] == (short)BlockViewWavetrace::Blocked) + return false; + while (cpnts.size() > 0) { + npnts.clear(); + cl++; + if (cl >= max_steps) return false; + for (int i = 0; i < cpnts.size(); ++i) { + if (cpnts[i] == fn) { + return true; + } + cx = cpnts[i].x() - 1; + cy = cpnts[i].y(); + if (frect.contains(cx, cy)) { + if (field[cx][cy] == empty) { + npnts.push_back(QPoint(cx, cy)); + fill(cx, cy, cl); + } + } + cx = cpnts[i].x() + 1; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == empty) { + npnts.push_back(QPoint(cx, cy)); + fill(cx, cy, cl); + } + } + cx = cpnts[i].x(); + cy = cpnts[i].y() - 1; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == empty) { + npnts.push_back(QPoint(cx, cy)); + fill(cx, cy, cl); + } + } + cy = cpnts[i].y() + 1; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == empty) { + npnts.push_back(QPoint(cx, cy)); + fill(cx, cy, cl); + } + } + } + cpnts = npnts; + //qDebug() << cl << ": " << cpnts.size(); + } + return false; +} + + +void BlockViewWavetrace::gatherPath() { + path_.clear(); + path_.push_back(fn); + if (dir_ == NoTrace) { + path_.push_front(st); + return; + } + int cx, cy; + int pa = -1, ca = -1; + bool first = true; + short cl = field[fn.x()][fn.y()]; + QRect frect(0, 0, wid, hei); + QPoint cpnt = fn; + //cout << "start from " << cl << endl; + while (cl > 0) { + cl--; + pa = ca; + int dir = 0; + cx = cpnt.x() + dps[dir].x(); + cy = cpnt.y() + dps[dir].y(); + dir++; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == cl) { + ca = QLineF(QPointF(cx, cy), cpnt).angle(); + if (ca != pa && !first) + path_.push_front(cpnt); + cpnt = QPoint(cx, cy); + first = false; + continue; + } + } + cx = cpnt.x() + dps[dir].x(); + cy = cpnt.y() + dps[dir].y(); + dir++; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == cl) { + ca = QLineF(QPointF(cx, cy), cpnt).angle(); + if (ca != pa && !first) + path_.push_front(cpnt); + cpnt = QPoint(cx, cy); + first = false; + continue; + } + } + cx = cpnt.x() + dps[dir].x(); + cy = cpnt.y() + dps[dir].y(); + dir++; + if (frect.contains(cx, cy)) { + if (field[cx][cy] == cl) { + ca = QLineF(QPointF(cx, cy), cpnt).angle(); + if (ca != pa && !first) + path_.push_front(cpnt); + cpnt = QPoint(cx, cy); + first = false; + continue; + } + } + cx = cpnt.x() + dps[dir].x(); + cy = cpnt.y() + dps[dir].y(); + if (frect.contains(cx, cy)) { + if (field[cx][cy] == cl) { + ca = QLineF(QPointF(cx, cy), cpnt).angle(); + if (ca != pa && !first) + path_.push_front(cpnt); + cpnt = QPoint(cx, cy); + first = false; + continue; + } + } + } + path_.push_front(st); + //cout << path_.size() << endl; +} + + +void BlockViewWavetrace::setPreferredDirection(BlockViewWavetrace::Direction dir) { + dir_ = dir; + if (dir == BlockViewWavetrace::Horizontal) { + dps[0] = QPoint(0, -1); + dps[1] = QPoint(0, 1); + dps[2] = QPoint(-1, 0); + dps[3] = QPoint(1, 0); + } + if (dir == BlockViewWavetrace::Vertical) { + dps[2] = QPoint(0, -1); + dps[3] = QPoint(0, 1); + dps[0] = QPoint(-1, 0); + dps[1] = QPoint(1, 0); + } +} + + +const QVector & BlockViewWavetrace::path() const { + /*path_.resize(3); + path_[0] = st; + path_[1] = (st + fn) / 2; + path_[2] = fn;*/ + return path_; +} diff --git a/test/qad/blockview/blockviewwavetrace.h b/test/qad/blockview/blockviewwavetrace.h new file mode 100644 index 0000000..7592f57 --- /dev/null +++ b/test/qad/blockview/blockviewwavetrace.h @@ -0,0 +1,43 @@ +#ifndef BLOCKVIEWWAVETRACE_H +#define BLOCKVIEWWAVETRACE_H + +#include "blockitem.h" + +class BlockViewWavetrace { +public: + BlockViewWavetrace(int width = 1, int height = 1); + + enum CellState {Empty = -1, Blocked = -2}; + enum Direction {NoTrace, Horizontal, Vertical}; + + int width() const {return wid;} + int height() const {return hei;} + void resize(int width, int height); + void resize(const QSize & sz) {resize(sz.width(), sz.height());} + void fill(short val = -1); + void fill(const QRect & rect, short val = -1); + void fill(const QPoint & point, short val = -1) {field[point.x()][point.y()] = val;} + void fill(int px, int py, short val = -1) {field[px][py] = val;} + void fill(BlockViewWavetrace::CellState val = Empty) {fill((short)val);} + void fill(const QRect & rect, BlockViewWavetrace::CellState val = Empty) {fill(rect, (short)val);} + void fill(const QPoint & point, BlockViewWavetrace::CellState val = Empty) {fill(point, (short)val);} + void fill(int px, int py, BlockViewWavetrace::CellState val = Empty) {fill(px, py, (short)val);} + void clear() {fill(-1);} + bool trace(const QPoint & start, const QPoint & finish); + Direction preferredDirection() const {return dir_;} + void setPreferredDirection(Direction dir); + void setMaximumSteps(int steps) {max_steps = steps;} + int maximumSteps() const {return max_steps;} + void gatherPath(); + const QVector & path() const; + +private: + int wid, hei, max_steps; + Direction dir_; + QVector > field; + QVector path_; + QPoint dps[4], st, fn; + +}; + +#endif // BLOCKVIEWWAVETRACE_H diff --git a/test/qad/blockview/drawtools.cpp b/test/qad/blockview/drawtools.cpp new file mode 100644 index 0000000..adbca70 --- /dev/null +++ b/test/qad/blockview/drawtools.cpp @@ -0,0 +1,904 @@ +#include "drawtools.h" +#include "ui_drawtools.h" +#include "alignedtextitem.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +_DTSizeItem::_DTSizeItem(): QGraphicsObject() { + cur_item = 0; + grid = 10.; + in_process = can_drag = false; + setData(1008, true); + for (int i = 0; i < 8; ++i) { + //qDebug() << &(rects[i]); + rects[i].setData(1008, true); + rects[i].setFlag(QGraphicsItem::ItemIgnoresTransformations); + rects[i].setZValue(10.); + rects[i].setAcceptHoverEvents(true); + rects[i].setRect(-5, -5, 10, 10); + rects[i].setPen(QPen(Qt::darkBlue)); + rects[i].setBrush(QBrush(QColor(64, 64, 255, 128))); + //rects[i].setData(1100, true); + } +} + + +_DTSizeItem::~_DTSizeItem() { + assignObject(0); + //qDebug() << "!!!"; +} + + +void _DTSizeItem::assignObject(QGraphicsItem * item) { + if (cur_item) + if (qgraphicsitem_cast(cur_item)) + cur_item->removeSceneEventFilter(this); + cur_item = item; + if (!cur_item) { + for (int i = 0; i < 8; ++i) { + rects[i].hide(); + rects[i].setParentItem(0); + rects[i].removeSceneEventFilter(this); + } + return; + } + if (item) + if (item->scene()) + if (!item->scene()->views().isEmpty()) + grid = ((BlockView*)(item->scene()->views()[0]))->gridStep(); + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + if (irect || iell || iline) { + resizeHandles(); + is_line = qgraphicsitem_cast(item); + for (int i = 0; i < (is_line ? 2 : 8); ++i) { + rects[i].setParentItem(item); + rects[i].installSceneEventFilter(this); + rects[i].show(); + } + } + item->installSceneEventFilter(this); + moveRects(); +} + + +void _DTSizeItem::moveRects() { + if (!cur_item) return; + QRectF rect = itemRect(cur_item); + QPointF tl = rect.topLeft(), tr = rect.topRight(), bl = rect.bottomLeft(), br = rect.bottomRight(); + if (is_line) { + rects[0].setPos(tl); rects[0].setData(2001, int(Qt::SizeAllCursor)); + rects[1].setPos(br); rects[1].setData(2001, int(Qt::SizeAllCursor)); + } else { + rects[0].setPos(tl); rects[0].setData(2001, int(Qt::SizeFDiagCursor)); + rects[1].setPos((tl + tr) / 2.); rects[1].setData(2001, int(Qt::SizeVerCursor)); + rects[2].setPos(tr); rects[2].setData(2001, int(Qt::SizeBDiagCursor)); + rects[3].setPos((tr + br) / 2.); rects[3].setData(2001, int(Qt::SizeHorCursor)); + rects[4].setPos(br); rects[4].setData(2001, int(Qt::SizeFDiagCursor)); + rects[5].setPos((br + bl) / 2.); rects[5].setData(2001, int(Qt::SizeVerCursor)); + rects[6].setPos(bl); rects[6].setData(2001, int(Qt::SizeBDiagCursor)); + rects[7].setPos((bl + tl) / 2.); rects[7].setData(2001, int(Qt::SizeHorCursor)); + } +} + + +void _DTSizeItem::applyRect() { + if (!cur_item) return; + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + if (irect) + irect->setRect(nrect); + if (iell) + iell->setRect(nrect); + if (iline) + iline->setLine(QLineF(nrect.topLeft(), nrect.bottomRight())); +} + + +void _DTSizeItem::doubleClick() { + QGraphicsSimpleTextItem * itext = qgraphicsitem_cast(cur_item); + AlignedTextItem * iatext = qgraphicsitem_cast(cur_item); + QGraphicsPixmapItem * ipixmap = qgraphicsitem_cast(cur_item); + if (itext || iatext) { + QMetaObject::invokeMethod(this, "textEditRequest", Qt::QueuedConnection); + } + if (ipixmap) { + QMetaObject::invokeMethod(this, "pixmapEditRequest", Qt::QueuedConnection); + } +} + + +void _DTSizeItem::resizeHandles() { + double sz = QApplication::fontMetrics().size(0, "0").height() / 3.; + QRectF r(-sz, -sz, sz*2, sz*2); + for (int i = 0; i < 8; ++i) + rects[i].setRect(r); +} + + +QRectF _DTSizeItem::itemRect(const QGraphicsItem * item) const { + if (!cur_item) return QRectF(); + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + if (irect) + return irect->rect(); + if (iell) + return iell->rect(); + if (iline) + return QRectF(iline->line().p1(), iline->line().p2()); + return QRectF(); +} + + +QRectF _DTSizeItem::boundingRect() const { + QRectF ret = rects[0].boundingRect().translated(rects[0].pos()); + for (int i = 1; i < 8; ++i) + ret |= rects[i].boundingRect().translated(rects[i].pos()); + return ret; +} + + +bool _DTSizeItem::sceneEventFilter(QGraphicsItem * watched, QEvent * event) { + QGraphicsSceneMouseEvent * me = (QGraphicsSceneMouseEvent * )event; + if (watched == cur_item) { + if (event->type() == QEvent::Close) { + assignObject(0); + return true; + } + if (event->type() == QEvent::GraphicsSceneMouseDoubleClick) { + doubleClick(); + return true; + } + return QGraphicsItem::sceneEventFilter(watched, event); + } + if (!cur_item) + return QGraphicsItem::sceneEventFilter(watched, event); + view_ = 0; + switch (event->type()) { + case QEvent::GraphicsSceneHoverEnter: + if (watched->scene()) if (!watched->scene()->views().isEmpty()) view_ = watched->scene()->views()[0]; + if (view_) view_->setCursor(Qt::CursorShape(watched->data(2001).toInt())); + break; + case QEvent::GraphicsSceneHoverLeave: + if (watched->scene()) if (!watched->scene()->views().isEmpty()) view_ = watched->scene()->views()[0]; + if (view_) view_->unsetCursor(); + break; + case QEvent::GraphicsSceneMousePress: + can_drag = (me->buttons() == Qt::LeftButton); + if (in_process) { + nrect = cur_item->data(2000).toRectF(); + applyRect(); + moveRects(); + } + in_process = false; + pp = quantize(me->scenePos(), grid); + cur_item->setData(2000, itemRect(cur_item)); + return true; + case QEvent::GraphicsSceneMouseMove: + if (me->buttons().testFlag(Qt::LeftButton)) { + sp = quantize(me->scenePos(), grid); + if (pp != sp && can_drag) { + in_process = true; + nrect = itemRect(cur_item); + if (is_line) { + if (watched == &(rects[0])) nrect.setTopLeft(rects[0].pos() + (sp - pp)); + if (watched == &(rects[1])) nrect.setBottomRight(rects[1].pos() + (sp - pp)); + } else { + if (watched == &(rects[0])) nrect.setTopLeft(rects[0].pos() + (sp - pp)); + if (watched == &(rects[1])) nrect.setTop(rects[1].pos().y() + (sp - pp).y()); + if (watched == &(rects[2])) nrect.setTopRight(rects[2].pos() + (sp - pp)); + if (watched == &(rects[3])) nrect.setRight(rects[3].pos().x() + (sp - pp).x()); + if (watched == &(rects[4])) nrect.setBottomRight(rects[4].pos() + (sp - pp)); + if (watched == &(rects[5])) nrect.setBottom(rects[5].pos().y() + (sp - pp).y()); + if (watched == &(rects[6])) nrect.setBottomLeft(rects[6].pos() + (sp - pp)); + if (watched == &(rects[7])) nrect.setLeft(rects[7].pos().x() + (sp - pp).x()); + nrect = nrect.normalized(); + } + pp = sp; + applyRect(); + moveRects(); + } + } + return true; + case QEvent::GraphicsSceneMouseRelease: + if (in_process) + emit sizeChanged(); + in_process = false; + can_drag = false; + return true; + default: break; + } + return QGraphicsItem::sceneEventFilter(watched, event); +} + + + + +DrawTools::DrawTools(BlockView * parent): QObject(parent), +actions_Z_up(this), actions_Z_top(this), actions_Z_down(this), actions_Z_bottom(this) { + widget_props = new QWidget(); + ui = new Ui::DrawTools(); + ui->setupUi(widget_props); + ui->labelPen->setMinimumSize(preferredIconSize(1.5, widget_props)); + ui->labelPen->setMaximumSize(ui->labelPen->minimumSize()); + ui->labelBrush->setMinimumSize(ui->labelPen->minimumSize()); + ui->labelBrush->setMaximumSize(ui->labelBrush->minimumSize()); + widget_props->setEnabled(false); + int fh = qMax(QApplication::fontMetrics().size(0, "0").height(), 22); + int thick = lineThickness(); + QSize sz(fh * 2.5, fh); + QStringList styles; + styles << tr("NoPen") << tr("Solid") << tr("Dash") + << tr("Dot") << tr("Dash-Dot") << tr("Dash-Dot-Dot"); + ui->comboLineStyle->setIconSize(sz); + for (int i = 0; i < 6; i++) { + QPixmap pix(sz); + pix.fill(); + QPainter p(&pix); + p.setPen(QPen(Qt::black, thick, (Qt::PenStyle)i)); + p.drawLine(0, pix.height() / 2, pix.width(), pix.height() / 2); + p.end(); + ui->comboLineStyle->addItem(QIcon(pix), styles[i]); + } +#ifdef Q_OS_MACOS + setAlignCompact(true); +#else + setAlignCompact(false); +#endif + menu_hor.addActions(QList() << ui->actionLeft << ui->actionHCenter << ui->actionRight); + menu_ver.addActions(QList() << ui->actionTop << ui->actionVCenter << ui->actionBottom); + ui->buttonAlignHor->setMenu(&menu_hor); + ui->buttonAlignVer->setMenu(&menu_ver); + new_type = -1; + new_item = cur_item = 0; + view_ = 0; + resize_enabled = true; + text_dlg.setWindowTitle(trUtf8("Edit text")); + text_dlg.setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); + QDialogButtonBox * bbox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + connect(bbox, SIGNAL(accepted()), &text_dlg, SLOT(accept())); + connect(bbox, SIGNAL(rejected()), &text_dlg, SLOT(reject())); + text_dlg.layout()->addWidget(&text_edit); + text_dlg.layout()->addWidget(bbox); + actions_Z_up.setText(trUtf8("Bring\nforward")); actions_Z_up.setIcon(QIcon(":/icons/z-up.png")); actions_Z_up.setEnabled(false); + actions_Z_top.setText(trUtf8("Bring\nto front")); actions_Z_top.setIcon(QIcon(":/icons/z-top.png")); actions_Z_top.setEnabled(false); + actions_Z_down.setText(trUtf8("Send\nbackward")); actions_Z_down.setIcon(QIcon(":/icons/z-down.png")); actions_Z_down.setEnabled(false); + actions_Z_bottom.setText(trUtf8("Send\nto back")); actions_Z_bottom.setIcon(QIcon(":/icons/z-bottom.png")); actions_Z_bottom.setEnabled(false); + actions_add << newAction(trUtf8("Draw\nRectangle"), QIcon(":/icons/draw-rectangle.png"), 1) + << newAction(trUtf8("Draw\nEllipse"), QIcon(":/icons/draw-ellipse.png"), 2) + << newAction(trUtf8("Draw\nLine"), QIcon(":/icons/draw-line.png"), 4) + << newAction(trUtf8("Draw\nText"), QIcon(":/icons/draw-text.png"), 0) + << newAction(trUtf8("Draw\nImage"), QIcon(":/icons/view-preview.png"), 3); + buttons_align << ui->buttonAlignTL << ui->buttonAlignTC << ui->buttonAlignTR + << ui->buttonAlignCL << ui->buttonAlignCC << ui->buttonAlignCR + << ui->buttonAlignBL << ui->buttonAlignBC << ui->buttonAlignBR; + foreach (QAction * a, actions_add) + connect(a, SIGNAL(toggled(bool)), this, SLOT(toggleNewItem(bool))); + foreach (QToolButton * b, buttons_align) + connect(b, SIGNAL(clicked(bool)), this, SLOT(alignClicked())); + connect(ui->buttonImage, SIGNAL(clicked(bool)), this, SLOT(buttonImage_clicked())); + connect(ui->buttonImagePaste, SIGNAL(clicked(bool)), this, SLOT(buttonImagePaste_clicked())); + connect(ui->buttonFont, SIGNAL(clicked(bool)), this, SLOT(buttonFont_clicked())); + connect(ui->buttonTextEdit, SIGNAL(clicked(bool)), this, SLOT(buttonTextEdit_clicked())); + connect(ui->spinWidth, SIGNAL(valueChanged(double)), this, SLOT(propertyChanged())); + connect(ui->spinWidth, SIGNAL(editingFinished()), this, SLOT(changeFinished())); + connect(ui->spinHeight, SIGNAL(valueChanged(double)), this, SLOT(propertyChanged())); + connect(ui->spinHeight, SIGNAL(editingFinished()), this, SLOT(changeFinished())); + connect(ui->spinThick, SIGNAL(valueChanged(double)), this, SLOT(propertyChanged())); + connect(ui->spinThick, SIGNAL(editingFinished()), this, SLOT(changeFinished())); + connect(ui->spinScale, SIGNAL(valueChanged(double)), this, SLOT(propertyChanged())); + connect(ui->spinScale, SIGNAL(editingFinished()), this, SLOT(changeFinished())); + connect(ui->comboText, SIGNAL(editTextChanged(QString)), this, SLOT(propertyChanged())); + connect(ui->comboText->lineEdit(), SIGNAL(editingFinished()), this, SLOT(changeFinished())); + connect(ui->comboLineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(comboLineStyleChanged())); + connect(ui->colorButtonPen, SIGNAL(colorChanged(QColor)), this, SLOT(propertyChanged())); + connect(ui->colorButtonPen, SIGNAL(colorChanged(QColor)), this, SLOT(changeFinished())); + connect(ui->colorButtonBrush, SIGNAL(colorChanged(QColor)), this, SLOT(propertyChanged())); + connect(ui->colorButtonBrush, SIGNAL(colorChanged(QColor)), this, SLOT(changeFinished())); + connect(ui->actionTop, SIGNAL(triggered(bool)), this, SLOT(actionTop_triggered(bool))); + connect(ui->actionVCenter, SIGNAL(triggered(bool)), this, SLOT(actionVCenter_triggered(bool))); + connect(ui->actionBottom, SIGNAL(triggered(bool)), this, SLOT(actionBottom_triggered(bool))); + connect(ui->actionLeft, SIGNAL(triggered(bool)), this, SLOT(actionLeft_triggered(bool))); + connect(ui->actionHCenter, SIGNAL(triggered(bool)), this, SLOT(actionHCenter_triggered(bool))); + connect(ui->actionRight, SIGNAL(triggered(bool)), this, SLOT(actionRight_triggered(bool))); + connect(&font_dlg, SIGNAL(currentFontChanged(QFont)), this, SLOT(propertyChanged())); + connect(&size_item, SIGNAL(sizeChanged()), this, SLOT(sizeChanged())); + connect(&size_item, SIGNAL(textEditRequest()), this, SLOT(buttonTextEdit_clicked())); + connect(&size_item, SIGNAL(pixmapEditRequest()), this, SLOT(buttonImage_clicked())); + connect(&actions_Z_up, SIGNAL(triggered(bool)), this, SLOT(actionZ_triggered())); + connect(&actions_Z_top, SIGNAL(triggered(bool)), this, SLOT(actionZ_triggered())); + connect(&actions_Z_down, SIGNAL(triggered(bool)), this, SLOT(actionZ_triggered())); + connect(&actions_Z_bottom, SIGNAL(triggered(bool)), this, SLOT(actionZ_triggered())); + setBlockView(parent); +} + + +DrawTools::~DrawTools() { + size_item.assignObject(0); + //delete ui; + //delete widget_props; +} + + +void DrawTools::setBlockView(BlockView * v) { + if (view_) view_->viewport()->removeEventFilter(this); + disconnect(this, SLOT(selectionChanged())); + view_ = v; + if (!view_) return; + view_->addItem(&size_item); + view_->viewport()->installEventFilter(this); + connect(view_->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); +} + + +void DrawTools::setAlignCompact(bool yes) { + ui->widgetAlign2->setVisible(yes); + ui->widgetAlign9->setVisible(!yes); +} + + +bool DrawTools::eventFilter(QObject * o, QEvent * e) { + QMouseEvent * me = (QMouseEvent*)e; + QPointF sp; + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) + ui->labelPen->setMinimumSize(preferredIconSize(1.5, widget_props)); + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseMove) + sp = quantize(view_->mapToScene(me->pos()), view_->gridStep()); + QRectF mr; + switch (e->type()) { + case QEvent::MouseButtonPress: + if (new_type < 0) break; + if (new_item) { + delete new_item; + new_item = 0; + if (!me->modifiers().testFlag(Qt::ControlModifier)) { + foreach (QAction * a, actions_add) + a->setChecked(false); + new_type = -1; + //view_->setCursor(Qt::ArrowCursor); + return true; + } + } + new_item = 0; + pp = sp; + switch (new_type) { + case 0: + new_item = new AlignedTextItem(); + ((AlignedTextItem*)new_item)->setText("Text"); + ((AlignedTextItem*)new_item)->setPos(sp); + break; + case 1: + new_item = new QGraphicsRectItem(); + break; + case 2: + new_item = new QGraphicsEllipseItem(); + break; + case 3: + new_item = new QGraphicsPixmapItem(QPixmap(":/icons/view-preview.png")); + ((QGraphicsPixmapItem*)new_item)->setPos(sp - QPointF(new_item->boundingRect().width() / 2, new_item->boundingRect().height() / 2)); + break; + case 4: + new_item = new QGraphicsLineItem(QLineF(sp, sp)); + break; + }; + if (new_item) { + if (new_type == 1 || new_type == 2) + ((QAbstractGraphicsShapeItem*)new_item)->setBrush(Qt::white); + new_item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); + new_item->setData(1100, true); + emit itemCreated(new_item); + return true; + } + break; + case QEvent::MouseMove: + if (me->buttons().testFlag(Qt::LeftButton)) { + if (new_item) { + mr = new_item->mapRectFromScene(QRectF(pp, sp).normalized()); + //mr = QRectF(pp, sp).normalized(); + switch (new_type) { + case 0: + ((AlignedTextItem*)new_item)->setPos(sp); + break; + case 1: + ((QGraphicsRectItem*)new_item)->setRect(mr); + break; + case 2: + ((QGraphicsEllipseItem*)new_item)->setRect(mr); + break; + case 3: + ((QGraphicsPixmapItem*)new_item)->setPos(sp - QPointF(new_item->boundingRect().width() / 2, new_item->boundingRect().height() / 2)); + break; + case 4: + ((QGraphicsLineItem*)new_item)->setLine(QLineF(pp, sp)); + break; + }; + return true; + } + } + break; + case QEvent::MouseButtonRelease: + if (new_item) { + if (new_item->boundingRect().isEmpty()) + delete new_item; + else { + emit itemAddConfirm(new_item); + if (view_) { + view_->selectNone(); + new_item->setSelected(true); + } + } + new_item = 0; + if (!me->modifiers().testFlag(Qt::ControlModifier)) { + foreach (QAction * a, actions_add) + a->setChecked(false); + new_type = -1; + //view_->setCursor(Qt::ArrowCursor); + } + return true; + } + break; + default: break; + } + return QObject::eventFilter(o, e); +} + + +QComboBox * DrawTools::textEditCombo() const { + return ui->comboText; +} + +/* +void DrawTools::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + return; + } + QObject::changeEvent(e); +} +*/ + +QAction * DrawTools::newAction(const QString & text, const QIcon & icon, int type) { + QAction * ret = new QAction(icon, text, this); + ret->setCheckable(true); + ret->setData(type); + return ret; +} + + +void DrawTools::toggleNewItem(bool on) { + QAction * sa = (QAction * )sender(); + foreach (QAction * a, actions_add) + if (a != sa) + a->setChecked(false); + if (!on) { + new_type = -1; + view_->unsetCursor(); + return; + } + new_type = sa->data().toInt(); + view_->setCursor(Qt::CrossCursor); +} + + +void DrawTools::alignClicked() { + QToolButton * sb = (QToolButton * )sender(); + foreach (QToolButton * b, buttons_align) + if (b != sb) + b->setChecked(false); + sb->setChecked(true); + align = 0; + QString als = sb->objectName().right(2).toLower(); + if (als[0] == 't') align |= Qt::AlignTop; + if (als[0] == 'c') align |= Qt::AlignVCenter; + if (als[0] == 'b') align |= Qt::AlignBottom; + if (als[1] == 'l') align |= Qt::AlignLeft; + if (als[1] == 'c') align |= Qt::AlignHCenter; + if (als[1] == 'r') align |= Qt::AlignRight; + propertyChanged(); +} + + +void DrawTools::setPenBrushEnabled(bool pen, bool brush) { + ui->labelPen->setEnabled(pen); + ui->colorButtonPen->setEnabled(pen); + ui->labelBrush->setEnabled(brush); + ui->colorButtonBrush->setEnabled(brush); +} + + +void DrawTools::blockPropSignals(bool block_) { + ui->spinWidth->blockSignals(block_); + ui->spinHeight->blockSignals(block_); + ui->spinThick->blockSignals(block_); + ui->comboText->blockSignals(block_); + ui->comboLineStyle->blockSignals(block_); + ui->colorButtonPen->blockSignals(block_); + ui->colorButtonBrush->blockSignals(block_); + ui->actionTop->blockSignals(block_); + ui->actionVCenter->blockSignals(block_); + ui->actionBottom->blockSignals(block_); + ui->actionHCenter->blockSignals(block_); + ui->actionLeft->blockSignals(block_); + ui->actionRight->blockSignals(block_); + foreach (QToolButton * b, buttons_align) + b->blockSignals(block_); +} + + +void DrawTools::actionAlignTrigger(bool vert, Qt::AlignmentFlag value) { + blockPropSignals(true); + if (vert) foreach (QAction * a, menu_ver.actions()) a->setChecked(false); + else foreach (QAction * a, menu_hor.actions()) a->setChecked(false); + align = align & (vert ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask); + align |= value; + ((QAction*)sender())->setChecked(true); + blockPropSignals(false); + propertyChanged(); +} + + +void DrawTools::emitZAvailabe(QGraphicsItem * item) { + BlockView * view = 0; + if (item) if (item->scene()) if (!item->scene()->views().isEmpty()) view = qobject_cast(item->scene()->views()[0]); + if (view == 0) { + moveZUpAvailable(false); + moveZDownAvailable(false); + return; + } + QList dl; + if (item->parentItem() == 0) dl = view->decors(); + else if (item->parentItem()->data(1006) == "item") dl = ((BlockItem*)(item->parentItem()))->decors_; + if (dl.size() <= 1) { + moveZUpAvailable(false); + moveZDownAvailable(false); + return; + } + int ind = dl.indexOf(item); + if (ind < 0) { + moveZUpAvailable(false); + moveZDownAvailable(false); + } else { + moveZUpAvailable(ind < dl.size() - 1); + moveZDownAvailable(ind > 0); + } +} + + +void DrawTools::selectionChanged() { + cur_item = 0; + size_item.assignObject(0); + if (!view_) { + emitZAvailabe(); + return; + } + QList sil = view_->scene()->selectedItems(); + if (sil.size() != 1) { + emitZAvailabe(); + widget_props->setEnabled(false); + return; + } + widget_props->setEnabled(true); + cur_item = sil[0]; + if (!cur_item) { + emitZAvailabe(); + return; + } + QGraphicsSimpleTextItem * itext = qgraphicsitem_cast(cur_item); + AlignedTextItem * iatext = qgraphicsitem_cast(cur_item); + QGraphicsPixmapItem * ipixmap = qgraphicsitem_cast(cur_item); + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + blockPropSignals(true); + if (itext) { + ui->stackedProperties->setCurrentIndex(0); + ui->comboText->setEditText(itext->text()); + ui->colorButtonPen->setColor(itext->brush().color()); + font_dlg.blockSignals(true); + font_dlg.setCurrentFont(itext->font()); + font_dlg.blockSignals(false); + setPenBrushEnabled(true, false); + ui->widgetAlign2->setEnabled(false); + ui->widgetAlign9->setEnabled(false); + size_item.assignObject(itext); + } else if (iatext) { + ui->stackedProperties->setCurrentIndex(0); + ui->comboText->setEditText(iatext->text()); + ui->colorButtonPen->setColor(iatext->brush().color()); + font_dlg.blockSignals(true); + font_dlg.setCurrentFont(iatext->font()); + font_dlg.blockSignals(false); + setPenBrushEnabled(true, false); + foreach (QAction * a, menu_hor.actions()) a->setChecked(false); + foreach (QAction * a, menu_ver.actions()) a->setChecked(false); + align = iatext->alignment(); + QString als; + if (align.testFlag(Qt::AlignTop)) {als += "T"; ui->actionTop->setChecked(true);} + if (align.testFlag(Qt::AlignVCenter)) {als += "C"; ui->actionVCenter->setChecked(true);} + if (align.testFlag(Qt::AlignBottom)) {als += "B"; ui->actionBottom->setChecked(true);} + if (align.testFlag(Qt::AlignLeft)) {als += "L"; ui->actionLeft->setChecked(true);} + if (align.testFlag(Qt::AlignHCenter)) {als += "C"; ui->actionHCenter->setChecked(true);} + if (align.testFlag(Qt::AlignRight)) {als += "R"; ui->actionRight->setChecked(true);} + foreach (QToolButton * b, buttons_align) + b->setChecked(false); + foreach (QToolButton * b, buttons_align) + if (b->objectName().endsWith(als)) { + b->setChecked(true); + break; + } + ui->widgetAlign2->setEnabled(true); + ui->widgetAlign9->setEnabled(true); + size_item.assignObject(iatext); + } else if (ipixmap) { + ui->stackedProperties->setCurrentIndex(2); + ui->spinScale->setValue(sqrt(ipixmap->transform().determinant())); + setPenBrushEnabled(false, false); + size_item.assignObject(ipixmap); + } else if (irect || iell) { + ui->stackedProperties->setCurrentIndex(1); + QAbstractGraphicsShapeItem * ishape(0); + if (irect) { + ishape = irect; + ui->spinWidth->setValue(irect->rect().width()); + ui->spinHeight->setValue(irect->rect().height()); + } + if (iell) { + ishape = iell; + ui->spinWidth->setValue(iell->rect().width()); + ui->spinHeight->setValue(iell->rect().height()); + } + if (ishape) { + ui->colorButtonPen->setColor(ishape->pen().color()); + ui->colorButtonBrush->setColor(ishape->brush().color()); + ui->spinThick->setValue(ishape->pen().widthF()); + ui->comboLineStyle->setCurrentIndex(qMin((int)ishape->pen().style(), ui->comboLineStyle->count() - 1)); + setPenBrushEnabled(true, true); + if (resize_enabled) + size_item.assignObject(ishape); + } + } else if (iline) { + ui->stackedProperties->setCurrentIndex(1); + ui->colorButtonPen->setColor(iline->pen().color()); + ui->spinThick->setValue(iline->pen().widthF()); + ui->comboLineStyle->setCurrentIndex(qMin((int)iline->pen().style(), ui->comboLineStyle->count() - 1)); + setPenBrushEnabled(true, false); + if (resize_enabled) + size_item.assignObject(iline); + } else { + ui->stackedProperties->setCurrentIndex(3); + widget_props->setEnabled(false); + } + emitZAvailabe(cur_item); + blockPropSignals(false); +} + + +void DrawTools::sizeChanged() { + blockPropSignals(true); + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + if (irect || iell) { + if (irect) { + ui->spinWidth->setValue(irect->rect().width()); + ui->spinHeight->setValue(irect->rect().height()); + } + if (iell) { + ui->spinWidth->setValue(iell->rect().width()); + ui->spinHeight->setValue(iell->rect().height()); + } + } + blockPropSignals(false); + changeFinished(); +} + + +void DrawTools::propertyChanged() { + if (!cur_item) return; + QGraphicsSimpleTextItem * itext = qgraphicsitem_cast(cur_item); + AlignedTextItem * iatext = qgraphicsitem_cast(cur_item); + QGraphicsPixmapItem * ipixmap = qgraphicsitem_cast(cur_item); + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + if (itext) { + QRectF obr = itext->boundingRect(); + itext->setFont(font_dlg.currentFont()); + itext->setText(ui->comboText->currentText()); + QRectF nbr = itext->boundingRect(); + QSizeF ds = (obr.size() - nbr.size()) / 2.; + itext->setPos(itext->pos() + QPointF(ds.width(), ds.height())); + itext->setBrush(ui->colorButtonPen->color()); + } else if (iatext) { + iatext->setFont(font_dlg.currentFont()); + iatext->setText(ui->comboText->currentText()); + iatext->setBrush(ui->colorButtonPen->color()); + iatext->setAlignment(align); + } else if (ipixmap) { + QTransform t = ipixmap->transform(); + double det = sqrt(t.determinant()); + QSizeF os = ipixmap->boundingRect().size() * det; + if (det != 0.) t.scale(1. / det, 1. / det); + det = ui->spinScale->value(); + t.scale(det, det); + ipixmap->setTransform(t); + QSizeF ns = ipixmap->boundingRect().size() * det; + QSizeF ds = (os - ns) / 2.; + ipixmap->setPos(ipixmap->pos() + QPointF(ds.width(), ds.height())); + } else if (irect || iell) { + QAbstractGraphicsShapeItem * ishape(0); + if (irect) { + ishape = irect; + irect->setRect(QRectF(irect->rect().topLeft(), QSizeF(ui->spinWidth->value(), ui->spinHeight->value()))); + } + if (iell) { + ishape = iell; + iell->setRect(QRectF(iell->rect().topLeft(), QSizeF(ui->spinWidth->value(), ui->spinHeight->value()))); + } + if (ishape) { + ishape->setPen(QPen(ui->colorButtonPen->color(), ui->spinThick->value(), (Qt::PenStyle)ui->comboLineStyle->currentIndex())); + ishape->setBrush(ui->colorButtonBrush->color()); + if (resize_enabled) + size_item.assignObject(ishape); + } + } else if (iline) { + iline->setPen(QPen(ui->colorButtonPen->color(), ui->spinThick->value(), (Qt::PenStyle)ui->comboLineStyle->currentIndex())); + if (resize_enabled) + size_item.assignObject(iline); + } +} + + +void DrawTools::comboLineStyleChanged() { + if (!cur_item) return; + QGraphicsRectItem * irect = qgraphicsitem_cast(cur_item); + QGraphicsEllipseItem * iell = qgraphicsitem_cast(cur_item); + QGraphicsLineItem * iline = qgraphicsitem_cast(cur_item); + if (irect || iell) { + QAbstractGraphicsShapeItem * ishape(0); + if (irect) + ishape = irect; + if (iell) + ishape = iell; + if (ishape) { + QPen p(ishape->pen()); + p.setStyle((Qt::PenStyle)ui->comboLineStyle->currentIndex()); + ishape->setPen(p); + changeFinished(); + } + } else if (iline) { + QPen p(iline->pen()); + p.setStyle((Qt::PenStyle)ui->comboLineStyle->currentIndex()); + iline->setPen(p); + changeFinished(); + } +} + + +void DrawTools::buttonImage_clicked() { + QGraphicsPixmapItem * pi = qgraphicsitem_cast(cur_item); + if (!pi) return; + QList sif(QImageReader::supportedImageFormats()); + QString f; + foreach (const QByteArray & i, sif) { + if (!f.isEmpty()) f += " "; + f += "*."; + f += i; + } + QString ret = QFileDialog::getOpenFileName(0, trUtf8("Select image"), pi->data(1101).toString(), QString("Images(%1)").arg(f)); + if (ret.isEmpty()) return; + QImage im(ret); + if (im.isNull()) return; + pi->setData(1101, ret); + QRectF obr = pi->boundingRect(); + pi->setPixmap(QPixmap::fromImage(im)); + QRectF nbr = pi->boundingRect(); + QSizeF ds = (obr.size() - nbr.size()) / 2.; + pi->setPos(pi->pos() + QPointF(ds.width(), ds.height())); + changeFinished(); +} + + +void DrawTools::buttonImagePaste_clicked() { + QGraphicsPixmapItem * pi = qgraphicsitem_cast(cur_item); + if (!pi) return; + QPixmap pm = QApplication::clipboard()->pixmap(); + if (pm.isNull()) return; + QRectF obr = pi->boundingRect(); + pi->setPixmap(pm); + QRectF nbr = pi->boundingRect(); + QSizeF ds = (obr.size() - nbr.size()) / 2.; + pi->setPos(pi->pos() + QPointF(ds.width(), ds.height())); + changeFinished(); +} + + +void DrawTools::buttonFont_clicked() { + if (!cur_item) return; + QGraphicsSimpleTextItem * ti = qgraphicsitem_cast(cur_item); + AlignedTextItem * ati = qgraphicsitem_cast(cur_item); + if (!ti && !ati) return; + QFont font_prev; + if (ti) font_prev = ti->font(); + if (ati) font_prev = ati->font(); + font_dlg.blockSignals(true); + font_dlg.setCurrentFont(font_prev); + font_dlg.blockSignals(false); + if (font_dlg.exec() == QDialog::Rejected) + font_dlg.setCurrentFont(font_prev); + else + changeFinished(); +} + + +void DrawTools::buttonTextEdit_clicked() { + text_dlg.setWindowIcon(QApplication::activeWindow()->windowIcon()); + text_edit.setPlainText(ui->comboText->lineEdit()->text()); + text_edit.selectAll(); + text_edit.setFocus(); + if (text_dlg.exec() == QDialog::Rejected) + return; + ui->comboText->lineEdit()->setText(text_edit.toPlainText()); + propertyChanged(); + changeFinished(); +} + + +void DrawTools::actionZ_triggered() { + if (!cur_item) return; + if (cur_item->data(1009) == "decor") { + BlockView * view = 0; + if (cur_item) if (cur_item->scene()) if (!cur_item->scene()->views().isEmpty()) + view = qobject_cast(cur_item->scene()->views()[0]); + if (!view) return; + QGraphicsScene * scene = view->scene(); + QList dl = view->decors(); + scene->blockSignals(true); + foreach (QGraphicsItem * d, dl) scene->removeItem(d); + int ind = dl.indexOf(cur_item); + dl.removeAt(ind); + if (sender() == &actions_Z_up) dl.insert(ind + 1, cur_item); + if (sender() == &actions_Z_top) dl.append(cur_item); + if (sender() == &actions_Z_down) dl.insert(ind - 1, cur_item); + if (sender() == &actions_Z_bottom) dl.prepend(cur_item); + foreach (QGraphicsItem * d, dl) scene->addItem(d); + scene->blockSignals(false); + } + if (cur_item->data(1011) == "decor") { + BlockItem * bi = qgraphicsitem_cast(cur_item->parentItem()); + if (!bi) return; + QList dl = bi->decors_; + foreach (QGraphicsItem * d, dl) d->setParentItem(0); + int ind = dl.indexOf(cur_item); + dl.removeAt(ind); + if (sender() == &actions_Z_up) dl.insert(ind + 1, cur_item); + if (sender() == &actions_Z_top) dl.append(cur_item); + if (sender() == &actions_Z_down) dl.insert(ind - 1, cur_item); + if (sender() == &actions_Z_bottom) dl.prepend(cur_item); + bi->decors_ = dl; + foreach (QGraphicsItem * d, dl) d->setParentItem(bi); + } + size_item.assignObject(cur_item); + emitZAvailabe(cur_item); + emit itemZChanged(cur_item); +} + + +void DrawTools::setResizeHandlesEnabled(bool on) { + resize_enabled = on; + if (!on) { + size_item.assignObject(0); + return; + } + if (cur_item && on) + propertyChanged(); +} diff --git a/test/qad/blockview/drawtools.h b/test/qad/blockview/drawtools.h new file mode 100644 index 0000000..b0a8cd3 --- /dev/null +++ b/test/qad/blockview/drawtools.h @@ -0,0 +1,132 @@ +#ifndef DRAWTOOLS_H +#define DRAWTOOLS_H + +#include +#include +#include +#include +#include +#include +#include "blockview.h" + +class QComboBox; + +class _DTSizeItem: public QGraphicsObject +{ + Q_OBJECT +public: + _DTSizeItem(); + ~_DTSizeItem(); + + void assignObject(QGraphicsItem * item); + +protected: + void moveRects(); + void applyRect(); + void doubleClick(); + void resizeHandles(); + QRectF itemRect(const QGraphicsItem * item) const; + QRectF boundingRect() const; + void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0) {} + bool sceneEventFilter(QGraphicsItem * watched, QEvent * event); + + QGraphicsItem * cur_item; + QGraphicsView * view_; + QGraphicsRectItem rects[8]; + QPointF pp, sp; + QRectF nrect; + qreal grid; + bool in_process, can_drag, is_line; + +signals: + void sizeChanged(); + void textEditRequest(); + void pixmapEditRequest(); + +}; + +namespace Ui { + class DrawTools; +} + +class DrawTools: public QObject +{ + Q_OBJECT + Q_PROPERTY(bool resizeHandlesEnabled READ isResizeHandlesEnabled WRITE setResizeHandlesEnabled) +public: + explicit DrawTools(BlockView * parent = 0); + ~DrawTools(); + + void setBlockView(BlockView * v); + void resetSizeTool() {size_item.assignObject(0);} + bool isResizeHandlesEnabled() const {return resize_enabled;} + void setAlignCompact(bool yes); + + QComboBox * textEditCombo() const; + QList actionsForAdd() const {return actions_add;} + QList actionsForZ() const {return QList() << &actions_Z_bottom << &actions_Z_down << &actions_Z_up << &actions_Z_top;} + QWidget * propertyWidget() const {return widget_props;} + +protected: + bool eventFilter(QObject * o, QEvent * e); + + QAction * newAction(const QString & text, const QIcon & icon, int type); + void setPenBrushEnabled(bool pen, bool brush); + void blockPropSignals(bool block_); + void actionAlignTrigger(bool vert, Qt::AlignmentFlag value); + void emitZAvailabe(QGraphicsItem * item = 0); + + QWidget * widget_props; + Ui::DrawTools * ui; + BlockView * view_; + QList actions_add; + mutable QAction actions_Z_up, actions_Z_top, actions_Z_down, actions_Z_bottom; + QList buttons_align; + QGraphicsItem * new_item, * cur_item; + QFontDialog font_dlg; + QPointF pp; + QDialog text_dlg; + QPlainTextEdit text_edit; + QMenu menu_hor, menu_ver; + _DTSizeItem size_item; + Qt::Alignment align; + int new_type; + bool resize_enabled; + +private slots: + void toggleNewItem(bool on); + void alignClicked(); + void selectionChanged(); + void sizeChanged(); + void propertyChanged(); + void comboLineStyleChanged(); + void changeFinished() {if (cur_item) emit itemEdited(cur_item);} + void moveZUpAvailable(bool yes) {actions_Z_up.setEnabled(yes); actions_Z_top.setEnabled(yes);} + void moveZDownAvailable(bool yes) {actions_Z_down.setEnabled(yes); actions_Z_bottom.setEnabled(yes);} + + void buttonImage_clicked(); + void buttonImagePaste_clicked(); + void buttonFont_clicked(); + void buttonTextEdit_clicked(); + + void actionTop_triggered(bool on) {actionAlignTrigger(true, Qt::AlignTop);} + void actionVCenter_triggered(bool on) {actionAlignTrigger(true, Qt::AlignVCenter);} + void actionBottom_triggered(bool on) {actionAlignTrigger(true, Qt::AlignBottom);} + void actionLeft_triggered(bool on) {actionAlignTrigger(false, Qt::AlignLeft);} + void actionHCenter_triggered(bool on) {actionAlignTrigger(false, Qt::AlignHCenter);} + void actionRight_triggered(bool on) {actionAlignTrigger(false, Qt::AlignRight);} + void actionZ_triggered(); + +public slots: + void setResizeHandlesEnabled(bool on); + +signals: + void itemCreated(QGraphicsItem * item); + void itemAddConfirm(QGraphicsItem * item); + void itemEdited(QGraphicsItem * item); + void itemZChanged(QGraphicsItem * item); + +}; + + +#endif // DRAWTOOLS_H diff --git a/test/qad/blockview/drawtools.ui b/test/qad/blockview/drawtools.ui new file mode 100644 index 0000000..61c7f45 --- /dev/null +++ b/test/qad/blockview/drawtools.ui @@ -0,0 +1,821 @@ + + + DrawTools + + + + 0 + 0 + 1197 + 268 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 2 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Font ... + + + + :/icons/draw-text.png:/icons/draw-text.png + + + + + + + Text: + + + + + + + + 0 + 0 + + + + true + + + -1 + + + 32 + + + QComboBox::NoInsert + + + + + + + Edit text ... + + + Edit text ... + + + + :/icons/document-edit.png:/icons/document-edit.png + + + Qt::ToolButtonIconOnly + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Align center left + + + + :/icons/align-hor.png:/icons/align-hor.png + + + QToolButton::InstantPopup + + + + + + + Align center left + + + + :/icons/align-ver.png:/icons/align-ver.png + + + QToolButton::InstantPopup + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + + + + 0 + 0 + + + + Align center + + + + :/icons/align-center-center.png:/icons/align-center-center.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align center right + + + + :/icons/align-center-right.png:/icons/align-center-right.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align center left + + + + :/icons/align-center-left.png:/icons/align-center-left.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align top right + + + + :/icons/align-top-right.png:/icons/align-top-right.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align bottom right + + + + :/icons/align-bottom-right.png:/icons/align-bottom-right.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align bottom center + + + + :/icons/align-bottom-center.png:/icons/align-bottom-center.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align top left + + + + :/icons/align-top-left.png:/icons/align-top-left.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align top center + + + + :/icons/align-top-center.png:/icons/align-top-center.png + + + + 27 + 16 + + + + true + + + + + + + + 0 + 0 + + + + Align bottom left + + + + :/icons/align-bottom-left.png:/icons/align-bottom-left.png + + + + 27 + 16 + + + + true + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + 10.000000000000000 + + + + + + + + 0 + 0 + + + + 0 + + + 1000.000000000000000 + + + 10.000000000000000 + + + + + + + + 0 + 0 + + + + Width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 1 + + + 999.000000000000000 + + + 0.500000000000000 + + + + + + + + 0 + 0 + + + + Thickness: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Height: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + Style: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Load image ... + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + + 0 + 0 + + + + Paste image ... + + + + :/icons/view-preview.png:/icons/view-preview.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 1 + + + + + + + + + 0 + 0 + + + + Scale: + + + + + + + + 0 + 0 + + + + 3 + + + 999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 16 + 20 + + + + + + + + :/icons/format-stroke-color.png + + + true + + + + + + + true + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 16 + 20 + + + + + + + + :/icons/format-fill-color.png + + + true + + + + + + + true + + + true + + + + + + + true + + + Top + + + + + true + + + Center + + + Center + + + + + true + + + Bottom + + + Bottom + + + + + true + + + Center + + + Center + + + + + true + + + Left + + + Left + + + + + true + + + Right + + + Right + + + + + + ColorButton + QPushButton +
colorbutton.h
+
+
+ + + + + +
diff --git a/test/qad/blockview/lang/qad_blockview_ru.ts b/test/qad/blockview/lang/qad_blockview_ru.ts new file mode 100644 index 0000000..5d154a9 --- /dev/null +++ b/test/qad/blockview/lang/qad_blockview_ru.ts @@ -0,0 +1,366 @@ + + + + + BlockBusItem + + + Add point: Ctrl + LeftClick +Remove point\segment: Ctrl + RightClick +New branch: Shift + LeftClick +Remove connection: Shift + RightClick + Добавить точку: Ctrl + LeftClick +Удалить точку\сегмент: Ctrl + RightClick +Новая ветка: Shift + LeftClick +Удалить шину: Shift + RightClick + + + + BlockEditor + + + Block editor + + + + + Block parameters + + + + + Width: + + + + + Heigth: + + + + + Pins margin: + + + + + Color: + + + + + Pins + + + + + Name + + + + + Bus + + + + + Add + + + + + Clone + + + + + Remove selected + + + + + Remove all + + + + + Save + + + + + + Ctrl+S + + + + + Save as ... + + + + + Load ... + + + + + Ctrl+O + + + + + Clear + + + + + Remove items + + + + + Del + + + + + DrawTools + + + Form + + + + + Font ... + Шрифт ... + + + + Text: + Текст: + + + + + Edit text ... + Изменить текст ... + + + + + + Align center left + Выравнивание слева по центру + + + + Align center + Выравнивание по центру + + + + Align center right + Выравнивание справа по центру + + + + Align top right + Выравнивание справа сверху + + + + Align bottom right + Выравнивание справа снизу + + + + Align bottom center + Выравнивание снизу по центру + + + + Align top left + Выравнивание слева сверху + + + + Align top center + Выравнивание сверху по центру + + + + Align bottom left + Выравнивание снизу слева + + + Size: + Размер: + + + + Width: + Ширина: + + + + Thickness: + Толщина: + + + + Height: + Высота: + + + + Style: + Стиль: + + + + Load image ... + Загрузить картинку ... + + + + Scale: + Масштаб: + + + + Top + + + + + + + + Center + + + + + + Bottom + + + + + + Left + + + + + + Right + + + + + NoPen + НетЛинии + + + + Solid + Сплошная + + + + Dash + Штриховая + + + + Dot + Пунктирная + + + + Dash-Dot + ШтрихПунктирная + + + + Dash-Dot-Dot + ШтрихПунктирПунктирная + + + + Edit text + Редактировать текст + + + + Bring +forward + Переместить +выше + + + + Bring +to front + На передний +фон + + + + Send +backward + Переместить +ниже + + + + Send +to back + На задний +фон + + + + Draw +Rectangle + Нарисовать +прямоугольник + + + + Draw +Ellipse + Нарисовать +эллипс + + + + Draw +Line + Нарисовать +линию + + + + Draw +Text + Нарисовать +текст + + + + Draw +Image + Нарисовать +картинку + + + + Select image + Выбрать картинку + + + diff --git a/test/qad/blockview/plugin/CMakeLists.txt b/test/qad/blockview/plugin/CMakeLists.txt new file mode 100644 index 0000000..fce055f --- /dev/null +++ b/test/qad/blockview/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(blockview "Gui;Widgets" "") diff --git a/test/qad/blockview/plugin/blockviewplugin.cpp b/test/qad/blockview/plugin/blockviewplugin.cpp new file mode 100644 index 0000000..4bd2d2a --- /dev/null +++ b/test/qad/blockview/plugin/blockviewplugin.cpp @@ -0,0 +1,69 @@ +#include "blockview.h" +#include "blockviewplugin.h" +#include + + +BlockViewPlugin::BlockViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void BlockViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool BlockViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * BlockViewPlugin::createWidget(QWidget * parent) { + return new BlockView(parent); +} + + +QString BlockViewPlugin::name() const { + return QLatin1String("BlockView"); +} + + +QString BlockViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon BlockViewPlugin::icon() const { + return QIcon(":/icons/blockview.png"); +} + + +QString BlockViewPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString BlockViewPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool BlockViewPlugin::isContainer() const { + return false; +} + + +QString BlockViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString BlockViewPlugin::includeFile() const { + return QLatin1String("blockview.h"); +} + diff --git a/test/qad/blockview/plugin/blockviewplugin.h b/test/qad/blockview/plugin/blockviewplugin.h new file mode 100644 index 0000000..7a90ca3 --- /dev/null +++ b/test/qad/blockview/plugin/blockviewplugin.h @@ -0,0 +1,36 @@ +#ifndef BLOCKVIEWPLUGIN_H +#define BLOCKVIEWPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class BlockViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + BlockViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // BLOCKVIEWPLUGIN_H diff --git a/test/qad/blockview/plugin/qad_blockview.cpp b/test/qad/blockview/plugin/qad_blockview.cpp new file mode 100644 index 0000000..4bd984e --- /dev/null +++ b/test/qad/blockview/plugin/qad_blockview.cpp @@ -0,0 +1,17 @@ +#include "qad_blockview.h" +#include "blockviewplugin.h" + +QADBlockView::QADBlockView(QObject * parent): QObject(parent) +{ + m_widgets.append(new BlockViewPlugin(this)); +} + + +QList QADBlockView::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_graphic_plugin, QADBlockView) +#endif diff --git a/test/qad/blockview/plugin/qad_blockview.h b/test/qad/blockview/plugin/qad_blockview.h new file mode 100644 index 0000000..2645d92 --- /dev/null +++ b/test/qad/blockview/plugin/qad_blockview.h @@ -0,0 +1,23 @@ +#ifndef QAD_BLOCKVIEW_H +#define QAD_BLOCKVIEW_H + +#include +#include + +class QADBlockView: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.blockview") +#endif +public: + explicit QADBlockView(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_BLOCKVIEW_H diff --git a/test/qad/blockview/qad_blockview.qrc b/test/qad/blockview/qad_blockview.qrc new file mode 100644 index 0000000..03b9335 --- /dev/null +++ b/test/qad/blockview/qad_blockview.qrc @@ -0,0 +1,51 @@ + + + ../icons/draw-line.png + ../icons/align-hor.png + ../icons/align-ver.png + ../icons/dialog-close.png + ../icons/edit-clear.png + ../icons/edit-guides.png + ../icons/view-grid.png + ../icons/zoom-fit-best.png + ../icons/configure.png + ../icons/document-save.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/item.png + ../icons/node-add.png + ../icons/node.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/expand_s_x.png + ../icons/expand_s_y.png + ../icons/expand_x.png + ../icons/expand_y.png + ../icons/border-line.png + ../icons/legend.png + ../icons/blockview.png + ../icons/view-fullscreen.png + ../icons/draw-ellipse.png + ../icons/draw-rectangle.png + ../icons/draw-text.png + ../icons/view-preview.png + ../icons/format-fill-color.png + ../icons/format-stroke-color.png + ../icons/document-open.png + ../icons/align-bottom-center.png + ../icons/align-bottom-left.png + ../icons/align-bottom-right.png + ../icons/align-center-center.png + ../icons/align-center-left.png + ../icons/align-center-right.png + ../icons/align-top-center.png + ../icons/align-top-left.png + ../icons/align-top-right.png + ../icons/z-bottom.png + ../icons/z-down.png + ../icons/z-top.png + ../icons/z-up.png + + diff --git a/test/qad/graphic/CMakeLists.txt b/test/qad/graphic/CMakeLists.txt new file mode 100644 index 0000000..6c37c79 --- /dev/null +++ b/test/qad/graphic/CMakeLists.txt @@ -0,0 +1,4 @@ +qad_project(graphic "Gui;Widgets;OpenGL" "qad_widgets;qad_utils;${OPENGL_LIBRARIES}") +if (graphic_UTILS) + add_subdirectory(qpicalculator) +endif() diff --git a/test/qad/graphic/graphic.cpp b/test/qad/graphic/graphic.cpp new file mode 100644 index 0000000..02f7e12 --- /dev/null +++ b/test/qad/graphic/graphic.cpp @@ -0,0 +1,1843 @@ +#include "graphic.h" +#include "qad_types.h" +#include "uglwidget.h" +#include "ui_graphic.h" +#include "ui_graphic_conf.h" +#if QT_VERSION < 0x050000 +# include +# include +# include +#endif +#ifndef Q_OS_ANDROID +# define HAS_GL +#endif +#ifdef HAS_GL +# ifndef GL_MULTISAMPLE +# define GL_MULTISAMPLE 0x809D +# endif +#endif + + +__GraphicRegistrator__ __graphic_registrator__; + + +Graphic::Graphic(QWidget * parent): QFrame(parent), canvas(0), line_x_min(this), line_x_max(this), line_y_min(this), line_y_max(this) { + QTranslator * trans = new QTranslator(); + trans->load(":/lang/qad_graphic_" + QLocale::system().name().left(2)); + if (trans->isEmpty()) + trans->load("lang/qad_graphic_" + QLocale::system().name().left(2)); +#if QT_VERSION >= 0x050000 + if (!qApp->installTranslator(trans)) + delete trans; +#else + qApp->installTranslator(trans); +#endif + gesture_angle = 45.; + leg_update = true; + visible_update = fullscr = need_mouse_pan = false; + gestures = +#ifdef Q_OS_ANDROID + true; +#else + false; +#endif + ui = new Ui::Graphic(); + ui->setupUi(this); + /*line_x_min.resize(70, 22); + line_x_max.resize(70, 22); + line_y_min.resize(70, 22); + line_y_max.resize(70, 22);*/ + line_x_min.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + line_x_max.setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + ((QBoxLayout * )ui->widgetLY->layout())->insertWidget(0, &line_y_min); + ((QBoxLayout * )ui->widgetLY->layout())->addWidget(&line_y_max); + ((QBoxLayout * )ui->widgetLX->layout())->insertWidget(0, &line_x_min); + ((QBoxLayout * )ui->widgetLX->layout())->addWidget(&line_x_max); + tm.restart(); + grid_numbers_x = grid_numbers_y = 1; + LN2 = qLn(2.); + LN5 = qLn(5.); + LN10 = qLn(10.); + line_x_min.setClearButtonVisible(true); + line_x_max.setClearButtonVisible(true); + line_y_min.setClearButtonVisible(true); + line_y_max.setClearButtonVisible(true); + connect(&line_x_min, SIGNAL(valueChanged(double)), this, SLOT(lineXMinChanged(double))); + connect(&line_x_max, SIGNAL(valueChanged(double)), this, SLOT(lineXMaxChanged(double))); + connect(&line_y_min, SIGNAL(valueChanged(double)), this, SLOT(lineYMinChanged(double))); + connect(&line_y_max, SIGNAL(valueChanged(double)), this, SLOT(lineYMaxChanged(double))); + connect(ui->canvas_raster, SIGNAL(paintEvent(QPaintEvent * )), this, SLOT(canvasPaintEvent())); + prepareCanvas(ui->canvas_raster); +#ifdef HAS_GL + canvas_gl = new UGLWidget(); + ui->layoutCanvas->addWidget(canvas_gl); + connect(canvas_gl, SIGNAL(paintSignal()), this, SLOT(canvasPaintEvent())); + prepareCanvas(canvas_gl); +#endif + icon_exp_x = QIcon(":/icons/expand_x.png"); + icon_exp_y = QIcon(":/icons/expand_y.png"); + icon_exp_sx = QIcon(":/icons/expand_s_x.png"); + icon_exp_sy = QIcon(":/icons/expand_s_y.png"); + icon_pause_b = QImage(":/icons/pause-back.png"); + icon_pause_f = QImage(":/icons/pause-front.png"); + aupdate = grid = isFit = isEmpty = navigation = true; + aalias = mupdate = bufferActive = isOGL = cancel = isPrinting = guides = hasLblX = hasLblY = isHover = false; + pause_ = only_expand_x = only_expand_y = false; + //qDebug() << -DBL_MAX/2. << DBL_MAX/2. << DBL_MIN; + limit_.setCoords(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); + eminx = eminy = DBL_MAX; + emaxx = emaxy = DBL_MIN; + grad_x = grad_y = Auto; + axis_type_x = Numeric; + min_repaint_int = 25; + inc_x = 1.; + legy = 0; + buffer = 0; + gridx = gridy = 1.; + history = 5.; + min_int = 1; + max_int = 200; + mdm = 10.; + visible_time = -1.; + thick = lineThickness(); + pause_phase = 0.; + def_rect.setRect(0., 0., 1., 1.); + selrect = def_rect; + margins_.setRect(4, 4, 4, 4); + curaction = gaMove; + selbrush.setStyle(Qt::SolidPattern); + selbrush.setColor(QColor(60, 175, 255, 100)); + text_color = palette().color(QPalette::WindowText); + grid_pen = QPen(palette().color(QPalette::Disabled, QPalette::WindowText), 0., Qt::DotLine); + //graph_pen = QPen(Qt::red); + //graph_pen.setCosmetic(true); + graphics.append(GraphicType()); + curGraphic = 0; + selpen = palette().color(QPalette::WindowText); + selpen.setStyle(Qt::DashLine); + back_color = palette().color(QPalette::Base); + buttons_ = AllButtons; + setOpenGL(false); + setButtonsPosition(Graphic::Left); + setAntialiasing(false); + setCaption(""); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui->layoutButtons->update(); + updateLegend(); + setRectToLines(); + conf = new GraphicConf(graphics, this); +} + + +Graphic::~Graphic() { + delete conf; + if (buffer != 0) delete buffer; +} + + +void Graphic::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + return; + } + QFrame::changeEvent(e); +} + + +void Graphic::resizeEvent(QResizeEvent *) { + if (leg_update) updateLegend(false); +} + + +void Graphic::timerEvent(QTimerEvent * ) { + pause_phase += 0.02; + if (pause_phase > 1.) + pause_phase -= 1.; + update(); +} + + +bool Graphic::eventFilter(QObject * o, QEvent * e) { + //qDebug() << "event" << o << e; + if (o == canvas) { + switch (e->type()) { + case QEvent::Gesture: + if (!navigation || !gestures) break; + foreach (QGesture * g, ((QGestureEvent*)e)->gestures()) + procGesture(g); + break; + case QEvent::KeyPress: + if (((QKeyEvent*)e)->key() == Qt::Key_Back) + leaveFullscreen(); + break; + case QEvent::TouchBegin: + if (!navigation || !gestures) break; + need_mouse_pan = true; + break; + case QEvent::TouchUpdate: { + if (!navigation || !gestures) break; + QList tpl = ((QTouchEvent*)e)->touchPoints(); + if (tpl.size() == 2) { + need_mouse_pan = false; + QPointF dp = tpl[0].scenePos() - tpl[1].scenePos(); + gesture_angle = rad2deg_qpie * qAtan2(qAbs(dp.y()), qAbs(dp.x())); + } + } break; + default: break; + } + } + return QFrame::eventFilter(o, e); +} + + +void Graphic::prepareCanvas(QWidget * w) { + connect(w, SIGNAL(mouseMoveEvent(QMouseEvent * )), this, SLOT(canvasMouseMoveEvent(QMouseEvent * ))); + connect(w, SIGNAL(mousePressEvent(QMouseEvent * )), this, SLOT(canvasMousePressEvent(QMouseEvent * ))); + connect(w, SIGNAL(mouseReleaseEvent(QMouseEvent * )), this, SLOT(canvasMouseReleaseEvent(QMouseEvent * ))); + connect(w, SIGNAL(mouseDoubleClickEvent(QMouseEvent*)), this, SLOT(canvasMouseDoubleClickEvent(QMouseEvent * ))); + connect(w, SIGNAL(wheelEvent(QWheelEvent * )), this, SLOT(canvasWheelEvent(QWheelEvent * ))); + connect(w, SIGNAL(leaveEvent(QEvent * )), this, SLOT(canvasLeaveEvent(QEvent * ))); + connect(w, SIGNAL(keyPressEvent(QKeyEvent * )), this, SLOT(canvasKeyPressEvent(QKeyEvent * ))); + //w->grabGesture(Qt::TapGesture); + w->grabGesture(Qt::TapAndHoldGesture); + w->grabGesture(Qt::PanGesture); + w->grabGesture(Qt::PinchGesture); + w->setMouseTracking(true); + w->installEventFilter(this); +} + + +void Graphic::procGesture(QGesture * g) { + if (!g) return; + switch (g->gestureType()) { + case Qt::PanGesture: { + if (need_mouse_pan) break; + QPanGesture * pg = (QPanGesture*)g; + QPointF dp = -pg->delta(); + dp.rx() /= getScaleX(); + dp.ry() /= getScaleY(); + selrect.translate(dp); + totalUpdate(); + } break; + case Qt::PinchGesture: { + QPinchGesture * pg = (QPinchGesture*)g; + //qDebug() << pg->totalRotationAngle(); + Qt::KeyboardModifiers km = Qt::NoModifier; + if (gesture_angle <= 20.) km = Qt::ControlModifier; + if (gesture_angle >= 70.) km = Qt::ShiftModifier; + QPoint cp = pg->centerPoint().toPoint(); + if (!fullscr) cp = mapFromGlobal(cp); + procZoom(cp, (pg->scaleFactor() - 1.) * 500., km); + totalUpdate(); + } break; + case Qt::TapAndHoldGesture: { + QTapAndHoldGesture * pg = (QTapAndHoldGesture*)g; + if (pg->state() == Qt::GestureStarted) + fullscreen(); + } break; + default: + qDebug() << g; + break; + } + //qDebug() << g; +} + + +void Graphic::procZoom(QPointF view_center, double dzoom, Qt::KeyboardModifiers km) { + double scl, wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top(); + double px = view_center.x() - gridborder.x() - margins_.left(), py = hei - view_center.y() + margins_.height(); + px = px / wid * selrect.width() + selrect.x(); + py = py / hei * selrect.height() + selrect.y(); + scl = 1. - dzoom / 500.; + if (km == Qt::NoModifier) + selrect.setRect(px - (px - selrect.x()) * scl, py - (py - selrect.y()) * scl, selrect.width() * scl, selrect.height() * scl); + else { + if (km == Qt::ControlModifier) + selrect.setRect(px - (px - selrect.x()) * scl, selrect.y(), selrect.width() * scl, selrect.height()); + if (km == Qt::ShiftModifier) + selrect.setRect(selrect.x(), py - (py - selrect.y()) * scl, selrect.width(), selrect.height() * scl); + } +} + + +void Graphic::totalUpdate() { + isFit = false; + emit visualRectChanged(); + update(true); + setRectToLines(); +} + + +void Graphic::canvasPaintEvent() { + if (is_lines_update) return; + //QMutexLocker ml(&mutex_); + //static int pwid = 0, phei = 0; + int wid = canvas->width(), hei = canvas->height(); + lastw = wid; + lasth = hei; + font_sz = fontMetrics().size(0, "0"); + font_sz.setHeight(font_sz.height() * 1.); + font_sz.setWidth(font_sz.width() * 8); + thick = lineThickness(); + if (buffer != 0) if (buffer->width() != wid || buffer->height() != hei) {delete buffer; buffer = 0;} + if (buffer == 0) buffer = new QImage(wid, hei, QImage::Format_RGB32); + if (bufferActive) { + QPainter p(canvas); + p.drawImage(0, 0, *buffer); + painter = &p; + fp_size.clear(); + if (curpos != startpos) drawAction(); + drawGuides(); + return; + } + //if (!aupdate && !mupdate && pwid == wid && phei == hei) return; + /*if (pwid != wid || phei != hei) { + line_x_min.move(0, hei - 35); + line_x_max.move(0, 0); + line_y_min.move(70, hei - line_x_min.height()); + line_y_max.move(wid - line_y_max.width(), hei - line_x_min.height()); + } + line_x_min.setVisible(grid); + line_x_max.setVisible(grid); + line_y_min.setVisible(grid); + line_y_max.setVisible(grid);*/ + //pwid = wid; + //phei = hei; + QPainter p; +#ifdef HAS_GL + if (isOGL) { + p.fillRect(canvas->rect(), Qt::black); + glClearColor(0.f, 0.f, 0.f, 0.f); + p.begin(canvas); + } else +#endif + p.begin(buffer); + p.fillRect(canvas->rect(), back_color); + painter = &p; + p.setFont(font()); + gridborder = QPoint(5, 5); + if (grid) { + gridborder += QPoint(font_sz.width(), font_sz.height()); + if (hasLblY) gridborder += QPoint(font_sz.height(), 0); + if (hasLblX) gridborder += QPoint(0, font_sz.height()); + } + painter->setClipping(true); + painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); + emit beforeGraphicPaintEvent(painter); + painter->setClipping(false); + if (grid) + drawGrid(); + p.setRenderHint(QPainter::Antialiasing, aalias); +#ifndef ANDROID + if (isOGL) { + if (aalias) glEnable(GL_MULTISAMPLE); + else glDisable(GL_MULTISAMPLE); + } +#endif + //p.setRenderHint(QPainter::HighQualityAntialiasing, aalias); + fp_size.clear(); + if (!aalias) p.translate(-0.5, -0.5); + drawGraphics(); + drawGuides(); + if (pause_) drawPause(); + emit graphicPaintEvent(painter); + p.end(); + if (isOGL) return; + p.begin(canvas); + p.drawImage(0, 0, *buffer); + p.end(); +} + + +void Graphic::canvasMouseMoveEvent(QMouseEvent * e) { + isHover = true; + curpos = e->pos(); + curpos_r = canvas2real(curpos); + QPointF dp; + QString cursorstr = tr("Cursor: ") + pointCoords(curpos_r); + emit graphicMouseMoveEvent(curpos_r, e->buttons()); + if (e->buttons() == Qt::NoButton) { + ui->status->setText(cursorstr); + if (guides) update(); + return; + } + if (!navigation) return; + if (gestures) { + if (!need_mouse_pan) return; + curaction = gaMove; + } else + if (curaction != gaMove && (e->buttons() & Qt::RightButton) == Qt::RightButton) return; + switch (curaction) { + case gaZoomInRect: + ui->status->setText(tr("Selection") + ": " + pointCoords(startpos_r) + " -> " + + pointCoords(curpos_r) + ", " + tr("Size") + ": " + pointCoords(absPoint(curpos_r - startpos_r))); + repaintCanvas(true); + break; + case gaZoomRangeX: + ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.x(), 'f', 3) + + " -> " + QString::number(curpos_r.x(), 'f', 3) + ", " + tr("Length") + ": " + + QString::number(qAbs(curpos_r.x() - startpos_r.x()), 'f', 3)); + repaintCanvas(true); + break; + case gaZoomRangeY: + ui->status->setText(tr("Range") + ": " + QString::number(startpos_r.y(), 'f', 3) + + " -> " + QString::number(curpos_r.y(), 'f', 3) + ", " + tr("Length") + ": " + + QString::number(qAbs(curpos_r.y() - startpos_r.y()), 'f', 3)); + repaintCanvas(true); + break; + case gaMove: + dp = e->pos() - prevpos; + dp.rx() *= selrect.width() / double(gridborder.x() + 5 - lastw); + dp.ry() *= selrect.height() / double(lasth - legy - gridborder.y() - 5); + if (e->modifiers() == Qt::ControlModifier) + dp.setY(0.); + if (e->modifiers() == Qt::ShiftModifier) + dp.setX(0.); + selrect.translate(dp); + totalUpdate(); + break; + default: break; + } + prevpos = e->pos(); +} + + +void Graphic::canvasMousePressEvent(QMouseEvent * e) { + emit graphicMousePressEvent(canvas2real(QPointF(e->pos())), e->buttons()); + if (!navigation) return; + if (gestures && !need_mouse_pan) return; +#ifdef HAS_GL + canvas_gl->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor); +#endif + ui->canvas_raster->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor); + prevpos = e->pos(); + startpos = prevpos; + startpos_r = canvas2real(startpos); + if (cancel || gestures) return; + if (e->button() == Qt::MidButton) curaction = gaMove; + if (e->button() == Qt::RightButton) { + if (bufferActive) { + curpos = startpos; + curpos_r = canvas2real(curpos); + repaintCanvas(true); + swapToNormal(); + cancel = true; + return; + } else { + prevaction = curaction; + curaction = gaMove; + return; + } + } + if (e->button() == Qt::LeftButton) { + if (e->modifiers() == Qt::ControlModifier) curaction = gaZoomRangeX; + else if (e->modifiers() == Qt::ShiftModifier) curaction = gaZoomRangeY; + else curaction = gaZoomInRect; + switch (curaction) { + case gaZoomInRect: + case gaZoomRangeX: + case gaZoomRangeY: + swapToBuffer(); + break; + default: break; + } + } + setCurrentAction(curaction); +} + + +void Graphic::canvasMouseReleaseEvent(QMouseEvent * e) { + emit graphicMouseReleaseEvent(canvas2real(QPointF(e->pos())), e->buttons()); + if (gestures) return; + need_mouse_pan = false; + if (!navigation) return; +#ifdef HAS_GL + canvas_gl->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor); +#endif + ui->canvas_raster->setCursor(guides ? Qt::BlankCursor : Qt::ArrowCursor); + QPointF tlp, brp; + QRect sr; + sr = QRect(startpos, curpos).normalized(); + if (cancel) { + if (e->buttons() == Qt::NoButton) cancel = false; + return; + } + if (e->button() == Qt::RightButton && curaction == gaMove) { + curaction = prevaction; + return; + } + if (e->button() == Qt::LeftButton && (e->buttons() & Qt::RightButton) != Qt::RightButton) { + if (curpos != startpos) { + tlp = canvas2real(sr.topLeft()); + brp = canvas2real(sr.bottomRight()); + isFit = false; + switch (curaction) { + case gaZoomInRect: + if (sr.width() <= 1 || sr.height() <= 1) break; + selrect.setCoords(tlp.x(), brp.y(), brp.x(), tlp.y()); + setRectToLines(); + break; + case gaZoomRangeX: + if (sr.width() <= 1) break; + findGraphicsRect(tlp.x(), brp.x()); + break; + case gaZoomRangeY: + if (sr.height() <= 1) break; + findGraphicsRect(0., 0., brp.y(), tlp.y()); + break; + default: return; + } + } + swapToNormal(); + update(true); + } + QPointF rp = canvas2real(QPointF(e->pos())); + ui->status->setText(tr("Cursor") + ": " + pointCoords(rp)); + emit visualRectChanged(); +} + + +void Graphic::canvasMouseDoubleClickEvent(QMouseEvent * ) { + autofit(); +} + + +void Graphic::canvasWheelEvent(QWheelEvent * e) { + //if (curaction != gaMove) return; + emit graphicWheelEvent(canvas2real(QPointF(e->pos())), e->delta()); + if (gestures) return; + if (!navigation) return; + procZoom(e->pos(), e->delta(), e->modifiers()); + totalUpdate(); +} + + +void Graphic::zoom(float factor) { + double wid = canvas->width() - gridborder.x() - margins_.width() - margins_.left(), hei = canvas->height() - gridborder.y() - margins_.height() - margins_.top(); + double px = wid / 2, py = hei / 2; + px = px / wid * selrect.width() + selrect.x(); + py = py / hei * selrect.height() + selrect.y(); + selrect.setRect(px - (px - selrect.x()) * factor, py - (py - selrect.y()) * factor, selrect.width() * factor, selrect.height() * factor); + isFit = false; + update(true); + setRectToLines(); +} + + +void Graphic::fullscreen() { + if (fullscr) leaveFullscreen(); + else enterFullscreen(); +} + + +void Graphic::canvasLeaveEvent(QEvent * ) { + isHover = false; + if (guides) update(true); + ui->status->setText(tr("Cursor") + ": ( ; )"); + leaveFullscreen(); +} + + +void Graphic::canvasKeyPressEvent(QKeyEvent * e) { + switch (e->key()) { + case Qt::Key_Escape: leaveFullscreen(); + default: break; + }; +} + + +void Graphic::clear() { + //cout << "clear" << endl; + for (int i = 0; i < graphics.size(); ++i) { + graphics[i].polyline.clear(); + graphics[i].polyline_pause.clear(); + graphics[i].max_x = 0.; + graphics[i].cvrect = QRectF(); + } + if (isFit) on_buttonAutofit_clicked(); +} + + +void Graphic::setAntialiasing(bool enabled) { + if (aalias == enabled) return; + aalias = enabled; + /*QGLFormat f = canvas_gl->format(); + f.setSampleBuffers(enabled); + canvas_gl->setFormat(f);*/ + update(); +} + + +void Graphic::setPaused(bool yes) { + pause_ = yes; + ui->checkPause->blockSignals(true); + ui->checkPause->setChecked(yes); + ui->checkPause->blockSignals(false); + for (int i = 0; i < graphics.size(); ++i) + graphics[i].cvrect = QRectF(); + if (!pause_) { + killTimer(timer_pause); + timer_pause = 0; + update(true); + return; + } + for (int i = 0; i < graphics.size(); ++i) { + graphics[i].polyline_pause = graphics[i].polyline; + graphics[i].polyline_pause.detach(); + graphics[i].max_x_pause = graphics[i].max_x; + } + timer_pause = startTimer(40); +} + + +void Graphic::setHistorySize(double val) { + history = val; + double x; + for (int i = 0; i < graphics.size(); ++i) { + QPolygonF & pol(graphics[i].polyline); + if (pol.isEmpty() || history <= 0.) continue; + graphics[i].cvrect = QRectF(); + x = pol.back().x() - history; + for (int j = pol.size() - 2; j >= 0 ; --j) + if (pol[j].x() < x) { + //qDebug() << pol.size() << j; + pol.erase(pol.begin(), pol.begin() + j); + break; + } + } +} + + +void Graphic::setOnlyExpandY(bool yes) { + only_expand_y = yes; + ui->checkExpandY->blockSignals(true); + ui->checkExpandY->setCheckable(yes); + ui->checkExpandY->blockSignals(false); +} + + +void Graphic::setOnlyExpandX(bool yes) { + only_expand_x = yes; + ui->checkExpandX->blockSignals(true); + ui->checkExpandX->setCheckable(yes); + ui->checkExpandX->blockSignals(false); +} + + +void Graphic::setGesturesNavigation(bool yes) { + gestures = yes; +} + + +Graphic::GraphicsData Graphic::graphicsData() const { + GraphicsData ret; + ret.resize(graphics.size()); + for (int i = 0; i < graphics.size(); ++i) + ret[i] = graphics[i].polyline; + return ret; +} + + +QByteArray Graphic::graphicsDataRaw() const { + QByteArray ret; + QDataStream s(&ret, QIODevice::WriteOnly); + s << graphicsData(); + return ret; +} + + +void Graphic::setGraphicsData(const Graphic::GraphicsData & gd) { + setGraphicsCount(gd.size()); + for (int i = 0; i < gd.size(); ++i) + setGraphicData(gd[i], i, false); + updateGraphics(); +} + + +void Graphic::setGraphicsDataRaw(const QByteArray & ba) { + if (ba.isEmpty()) { + clear(); + return; + } + Graphic::GraphicsData gd; + QDataStream s(ba); + s >> gd; + setGraphicsData(gd); +} + + +void Graphic::setButtons(Graphic::Buttons b) { + buttons_ = b; + ui->buttonAutofit->setVisible(b.testFlag(Autofit)); + ui->checkGrid->setVisible(b.testFlag(Grid)); + ui->checkGuides->setVisible(b.testFlag(CursorAxis)); + ui->checkExpandY->setVisible(b.testFlag(OnlyExpandY)); + ui->checkExpandX->setVisible(b.testFlag(OnlyExpandX)); + ui->buttonFullscreen->setVisible(b.testFlag(Fullscreen)); + ui->checkBorderInputs->setVisible(b.testFlag(BorderInputs)); + ui->checkLegend->setVisible(b.testFlag(Legend)); + ui->buttonClear->setVisible(b.testFlag(Clear)); + ui->buttonConfigure->setVisible(b.testFlag(Configure)); + ui->buttonSave->setVisible(b.testFlag(Save)); + ui->buttonClose->setVisible(b.testFlag(Close)); + ui->checkPause->setVisible(b.testFlag(Pause)); + if (ui->buttonAutofit->isVisible() || ui->checkGrid->isVisible() || ui->checkGuides->isVisible() || + ui->buttonConfigure->isVisible() || ui->buttonSave->isVisible() || ui->checkPause->isVisible()) + ui->verticalSpacer->changeSize(0, 30, QSizePolicy::Preferred, QSizePolicy::Preferred); + else + ui->verticalSpacer->changeSize(0, 0, QSizePolicy::Preferred, QSizePolicy::Preferred); + ui->layoutButtons->update(); +} + + +void Graphic::setButtonsPosition(Graphic::Alignment a) { + align = a; + ui->widgetLeft->hide(); + ui->widgetRight->hide(); + switch (a) { + case Graphic::Left: + ui->widgetLeft->setLayout(ui->layoutButtons); + ui->widgetLeft->show(); + break; + case Graphic::Right: + ui->widgetRight->setLayout(ui->layoutButtons); + ui->widgetRight->show(); + break; + } +} + + +void Graphic::addPoint(const QPointF & p, int graphic, bool update_) { + if (graphic >= graphics.size() || graphic < 0) return; + GraphicType & t(graphics[graphic]); + if (!t.cvrect.isNull() && !pause_) { +// if (graphics[graphic].cvrect.contains(p)) +// graphics[graphic].cvrect = QRectF(); + if (t.cvrect.top() < p.y()) t.cvrect.setTop(p.y()); + if (t.cvrect.bottom() > p.y()) t.cvrect.setBottom(p.y()); + if (t.cvrect.right() < p.x()) t.cvrect.setRight(p.x()); + if (t.cvrect.left() > p.x()) t.cvrect.setLeft(p.x()); + } + if (t.polyline.size() == 0) t.max_x = p.x(); + t.polyline << p; + if (t.max_x < p.x()) t.max_x = p.x(); + tick(graphic, true, update_); +} + + +void Graphic::setGraphicData(const QVector & g, int graphic, bool update_) { + if (graphic >= graphics.size() || graphic < 0) return; + GraphicType & t(graphics[graphic]); + t.cvrect = QRectF(); + t.polyline.clear(); + t.polyline = g; + if (t.polyline.size() == 0) { + t.max_x = 0.; + tick(graphic, false, update_); + return; + } + t.max_x = t.polyline[0].x(); + for (int i = 1; i < t.polyline.size(); ++i) + if (t.max_x < t.polyline[i].x()) + t.max_x = t.polyline[i].x(); + tick(graphic, false, update_); +} + + +void Graphic::setGraphicProperties(int graphic, const QString & name, const QColor& color, Qt::PenStyle style, double width, bool visible) { + if (graphic < 0 || graphic >= graphics.size()) return; + graphics[graphic].name = name; + graphics[graphic].pen.setColor(color); + graphics[graphic].pen.setStyle(style); + graphics[graphic].pen.setWidth(width); + graphics[graphic].visible = visible; + updateLegend(); +} + + +void Graphic::addGraphic(const QString & name, const QColor & color, Qt::PenStyle style, double width, bool visible) { + graphics << GraphicType(name, color, style, width, visible); + updateLegend(); +} + + +void Graphic::setVisualRect(const QRectF & rect) { + selrect = rect; + isFit = false; + update(); +} + + +void Graphic::setDefaultRect(const QRectF & rect) { + def_rect = rect; + if (isFit) autofit(); +} + + +void Graphic::saveImage() { + QString str = QFileDialog::getSaveFileName(this, tr("Save Image"), ppath, "PNG(*.png);;JPEG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tiff *.tif);;PPM(*.ppm)"); + if (str == "") return; + ppath = str; + QPixmap im(canvas->size()); + mupdate = true; + canvas->render(&im); + mupdate = false; + im.save(ppath); + update(true); +} + + +void Graphic::setOpenGL(bool on) { +#ifdef HAS_GL + isOGL = on; + if (on) { + ui->canvas_raster->hide(); + canvas_gl->show(); + canvas = canvas_gl; + } else { + canvas_gl->hide(); + ui->canvas_raster->show(); + canvas = ui->canvas_raster; + } +#else + isOGL = false; + ui->canvas_raster->show(); + canvas = ui->canvas_raster; +#endif + /*line_x_min.setParent(canvas); + line_x_max.setParent(canvas); + line_y_min.setParent(canvas); + line_y_max.setParent(canvas); + line_x_min.show(); + line_x_max.show(); + line_y_min.show(); + line_y_max.show();*/ + update(); +} + + +void Graphic::update(bool force) { + mupdate = true; + repaintCanvas(force); + mupdate = false; +} + + +void Graphic::setGraphicsCount(int arg, bool update) { + if (arg < 0) return; + while (graphics.size() < arg) + graphics.append(GraphicType(tr("y(x)"), QColor::fromHsv((graphics.size() * 55) % 360, 255, 255 - qrand() % 115))); + while (graphics.size() > arg) { + delete graphics.back().pb; + graphics.pop_back(); + } + if (update) updateLegend(); +} + + +void Graphic::removeGraphic(int arg, bool update) { + if (arg < 0 || arg >= graphics.size()) return; + delete graphics[arg].pb; + graphics.remove(arg); + if (update) updateLegend(); +} + + +/*void Graphic::setHistogramData(const QVector & g, int graphic) { + graphics[graphic].polyline.clear(); + if (g.isEmpty()) { + return; + } + QVector data = g; + QVector hist; + int ic = max_int, ci; + double md, cd, min, max, range, cx; + qSort(data); + md = DBL_MAX; + min = max = data[0]; + for (int i = 1; i < data.size(); ++i) { + if (min > data[i]) min = data[i]; + if (max < data[i]) max = data[i]; + cd = qAbs(data[i] - data[i - 1]); + if (md > cd && cd != 0.) md = cd; + } + range = max - min; + md = mdm; + //qDebug() << md << range << ic; + if (md != 0.) + ic = qRound(qMax(qMin(double(ic), range / md), double(min_int))); + md = range / ic; + hist.resize(ic); + foreach (const float & i, data) { + ci = qRound((i - min) / range * double(ic - 1)); + //if (ci < 0) ci = 0; + //if (ci >= ic) ci = ic - 1; + hist[ci]++; + } + QPolygonF & cpol(graphics[graphic].polyline); + if (hist.size() == 1 || range == 0.) { + cpol << QPointF(min - 0.5, 0.) << QPointF(min - 0.25, 0.); + cpol << QPointF(min - 0.25, hist[0]) << QPointF(min + 0.25, hist[0]); + cpol << QPointF(min + 0.25, 0.) << QPointF(min + 0.5, 0.); + } else { + cpol << QPointF(min, 0.); + for (int i = 0; i < hist.size(); ++i) { + cx = i * range / ic + min; + cpol << QPointF(cx, hist[i]) << QPointF(cx + md, hist[i]); + } + cpol << QPointF(range + min, 0.); + } + updateGraphics(); +}*/ + + +void Graphic::findGraphicsRect(double start_x, double end_x, double start_y, double end_y) { + double cx, cy, maxX, minX, maxY, minY, vx; + bool isRangeX = (start_x != end_x), isRangeY = (start_y != end_y); +// bool isEmpty = true; + //bool fast = true; + bool can_fast = (start_x == 0 && end_x == 0 && start_y == 0 && end_y == 0); + bool anyVisible = false, isTimeLimit = (visible_time > 0.) && !(isRangeX || isRangeY); + bool force_find = (visible_time > 0.) && (history > 0.) && (visible_time < history); + if (force_find) can_fast = false; +// foreach (const GraphicType & t, graphics) { +// const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline); +// if (!pol.isEmpty()) { +// isEmpty = false; +// break; +// } +// } +// if (isEmpty) { +// grect = def_rect; +// setRectToLines(); +// return; +// } +// can_fast = false; + vx = -DBL_MAX; + minY = minX = DBL_MAX; + maxY = maxX = -DBL_MAX; + foreach (const GraphicType & t, graphics) { + if (!t.visible) continue; + if (vx < (pause_ ? t.max_x_pause : t.max_x)) vx = (pause_ ? t.max_x_pause : t.max_x); + } + vx -= visible_time; + //qDebug() << "[Graphic]" << "can_fast" << can_fast; + for (int g = 0; g < graphics.size(); g++) { + GraphicType & t(graphics[g]); + if (!t.visible) continue; + const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline); + if (pol.isEmpty()) continue; + bool f = true; + //qDebug() << "[Graphic]" << "cvrect:" << t.cvrect << t.cvrect.isNull(); + if (t.cvrect.isNull() || !can_fast) { + for (int i = 0; i < pol.size(); i++) { + cx = pol[i].x(); + cy = pol[i].y(); + if ((start_x > cx || end_x < cx) && isRangeX) continue; + if ((start_y > cy || end_y < cy) && isRangeY) continue; + if ((cx < vx) && isTimeLimit) continue; + if (f) { + t.cvrect.setRect(cx, cy, 0, 0); + f = false; + } else { + if (t.cvrect.top() < cy) t.cvrect.setTop(cy); + if (t.cvrect.bottom() > cy) t.cvrect.setBottom(cy); + if (t.cvrect.right() < cx) t.cvrect.setRight(cx); + if (t.cvrect.left() > cx) t.cvrect.setLeft(cx); + } + //fast = false; + } + if (f) continue; + //qDebug() << "[Graphic]" << "2 cvrect:" << t.cvrect; + } + anyVisible = true; + if (maxY < t.cvrect.top()) maxY = t.cvrect.top(); + if (minY > t.cvrect.bottom()) minY = t.cvrect.bottom(); + if (maxX < t.cvrect.right()) maxX = t.cvrect.right(); + if (minX > t.cvrect.left()) minX = t.cvrect.left(); + if (!can_fast) t.cvrect = QRectF(); + } + if (!anyVisible) { + //qDebug() << "[Graphic]" << "empty autofit"; + grect = def_rect; + setRectToLines(); + return; + } +// if (fast) qDebug() << "[Graphic]" << "FAST autofit"; +// else qDebug() << "[Graphic]" << "autofit"; + if (maxX > limit_.right()) maxX = limit_.right(); + if (minX > limit_.right()) minX = limit_.right(); + if (minX < limit_.left()) minX = limit_.left(); + if (maxX < limit_.left()) maxX = limit_.left(); + if (maxY > limit_.bottom()) maxY = limit_.bottom(); + if (minY > limit_.bottom()) minY = limit_.bottom(); + if (minY < limit_.top()) minY = limit_.top(); + if (maxY < limit_.top()) maxY = limit_.top(); + if (minX > maxX) qSwap(minX, maxX); + if (minY > maxY) qSwap(minY, maxY); + if (qAbs(minX - maxX) < 1E-60) {minX -= defaultRect().width()/2; maxX += defaultRect().width()/2;} + if (qAbs(minY - maxY) < 1E-60) {minY -= defaultRect().height()/2; maxY += defaultRect().height()/2;} + if (only_expand_x) { + if (minX > eminx) minX = eminx; + if (maxX < emaxx) maxX = emaxx; + } + if (only_expand_y) { + if (minY > eminy) minY = eminy; + if (maxY < emaxy) maxY = emaxy; + } + eminx = minX; emaxx = maxX; + eminy = minY; emaxy = maxY; + if (isRangeX) selrect.setRect(start_x, minY, end_x - start_x, maxY - minY); + else if (isRangeY) selrect.setRect(minX, start_y, maxX - minX, end_y - start_y); + else grect.setRect(minX, minY, maxX - minX, maxY - minY); + grect = grect.normalized(); + if (isFit)/* || isRangeX || isRangeY)*/ selrect = grect; + setRectToLines(); +} + + +void Graphic::drawAction() { + //qDebug() << "draw action"; + int wid = canvas->width(), hei = canvas->height() - gridborder.y(), sx = startpos.x(), sy = startpos.y(), cx = curpos.x(), cy = curpos.y(); + painter->setPen(selpen); + painter->setBrush(selbrush); + switch (curaction) { + case gaZoomInRect: { + QSizeF rsz = QRectF(startpos_r, curpos_r).normalized().size(); + painter->drawRect(QRect(startpos, curpos)); + fp_size = " x " + pointCoords(QPointF(rsz.width(), rsz.height())); + } break; + case gaZoomRangeX: + painter->drawLine(sx, hei, sx, 0); + painter->drawLine(cx, hei, cx, 0); + painter->fillRect(sx, 0, cx - sx, hei, selbrush); + fp_size = " x " + pointCoords(QPointF(qAbs(startpos_r.x() - curpos_r.x()), 0.), true, false); + break; + case gaZoomRangeY: + painter->drawLine(gridborder.x(), sy, wid, sy); + painter->drawLine(gridborder.x(), cy, wid, cy); + painter->fillRect(gridborder.x(), sy, wid - gridborder.x(), cy - sy, selbrush); + fp_size = " x " + pointCoords(QPointF(0., qAbs(startpos_r.y() - curpos_r.y())), false, true); + break; + default: break; + } +} + + +void Graphic::drawGrid() { + int gbx = gridborder.x(), gby = gridborder.y(), cwid = canvas->width(), chei = canvas->height() - legy; + double px, py, range, step, start; + int wid = cwid - gbx - 5, hei = chei - gby - 5, cx, cy, cnt; + QRect rect; + QPair str; + + range = selrect.bottom() - selrect.top(); + if (grad_y == Graphic::Auto) step = splitRange(range, hei / gridy / font_sz.height() / 1.4); + else step = gridy;//range / hei * gridy; + start = roundTo(canvas2realY(-hei), step) - step; + py = start + step; + cy = 0; + cx = gbx - 5; + grid_pen.setWidth(qMax(qMax(qRound(thick / 1.4), 1), grid_pen.width())); + QFont sf = font(); + QFont nf = sf; + sf.setPointSizeF(qMax(sf.pointSizeF() / 1.6, 7.)); + QFontMetrics fm(nf), sfm(sf); + if (step > 0.) { + cnt = 1000; + while (cnt-- > 0) { + py -= step; + if (fabs(py) < step * .5) py = 0.; + cy = real2canvasY(py); + if (cy < 0) continue; + if (cy > hei + 5) break; + painter->setPen(grid_pen); + painter->drawLine(gbx, cy, cwid, cy); + str = gridMark(py * grid_numbers_y); + painter->setPen(text_color); + cy += font_sz.height() / 4.; + int dx = font_sz.height() / 8.; + if (!str.second.isEmpty()) { + rect = sfm.boundingRect(str.second); + painter->setFont(sf); + painter->drawText(cx - rect.width() - dx, cy - font_sz.height() / 2.5, str.second); + dx += rect.width() + font_sz.height() / 6.; + } + rect = fm.boundingRect(str.first); + painter->setFont(nf); + painter->drawText(cx - rect.width() - dx, cy, str.first); + } + } + cy = real2canvasY(0.); + if (cy >= 0 && cy <= (hei + 5)) { + QPen _p(grid_pen); + _p.setStyle(Qt::SolidLine); + painter->setPen(_p); + painter->drawLine(gbx, cy, cwid, cy); + } + if (hasLblY) { + painter->setPen(text_color); + painter->save(); + painter->translate(5, hei); + painter->rotate(-90.); + painter->drawText(0, 0, hei, font_sz.height(), Qt::AlignCenter, label_y); + painter->restore(); + } + + cy = chei - font_sz.height() / 4; + if (hasLblX) cy -= font_sz.height(); + range = selrect.right() - selrect.left(); + QString df; + if (axis_type_x == Graphic::Numeric) { + if (grad_x == Graphic::Auto) step = splitRange(range, wid / gridx / font_sz.width() * 1.4); + else step = gridx;//range / wid * gridx; + start = roundTo(canvas2realX(wid), step) + step; + px = start + step; + if (step > 0.) { + cnt = 1000; + while (cnt-- > 0) { + px -= step; + if (fabs(px) < step * .5) px = 0.; + cx = real2canvasX(px); + if (cx > cwid) continue; + if (cx < gbx) break; + painter->setPen(grid_pen); + painter->drawLine(cx, hei + 5, cx, 0); + painter->setPen(text_color); + int dx = -font_sz.height() / 4.; + painter->setFont(nf); + str = gridMark(px * grid_numbers_x); + rect = fm.boundingRect(str.first); + painter->drawText(cx + dx, cy, str.first); + dx += rect.width() + font_sz.height() / 6.; + if (!str.second.isEmpty()) { + rect = sfm.boundingRect(str.second); + painter->setFont(sf); + painter->drawText(cx + dx, cy - font_sz.height() / 4., str.second); + } + } + } + cx = real2canvasX(0.); + if (cx <= cwid && cx >= gbx) { + QPen _p(grid_pen); + _p.setStyle(Qt::SolidLine); + painter->setPen(_p); + painter->drawLine(cx, hei + 5, cx, 0); + } + } else { + int cur_scl[7] = {0,0,0,0,0,0,0}; + step = splitRangeDate(range, wid / gridx / font_sz.width() * 1.4, &df, cur_scl); + start = roundTo(canvas2realX(wid), step) + step; + px = start + step; + QDateTime cd = QDateTime::fromMSecsSinceEpoch(px * grid_numbers_x); + //qDebug() << "*** start" << cd << step; + roundDateTime(cd, cur_scl); + //qDebug() << "*** round" << cd; + addDateTime(cd, cur_scl); + //qDebug() << "*** add" << cd; + //qDebug() << "*** cur" << cur_scl[0] << cur_scl[1] << cur_scl[2] << cur_scl[3] << cur_scl[4] << cur_scl[5] << cur_scl[6]; + if (step > 0.) { + cnt = 1000; + while (cnt-- > 0) { + addDateTime(cd, cur_scl, -1); + //roundDateTime(cd, cur_scl); + //qDebug() << "next" << cd; + cx = real2canvasX(cd.toMSecsSinceEpoch() / grid_numbers_x); + if (cx > cwid) continue; + if (cx < gbx) {/*qDebug() << cx << "<" << gbx;*/ break;} + painter->setPen(grid_pen); + painter->drawLine(cx, hei + 5, cx, 0); + painter->setPen(text_color); + int dx = -font_sz.height() / 4.; + painter->setFont(nf); + str.first = cd.toString(df); + painter->drawText(cx + dx, cy, str.first); + } + } + } + painter->setPen(text_color); + painter->setFont(nf); + if (hasLblX) { + painter->setPen(text_color); + painter->drawText(gbx, chei - font_sz.height(), wid, font_sz.height(), Qt::AlignCenter, label_x); + } + + painter->setPen(QPen(grid_pen.color(), qMax(thick, grid_pen.width()))); + painter->drawRect(gbx, -1, wid + 6, hei + 6); +} + + +QPair Graphic::gridMark(double v) const { + QPair ret; + if ((qAbs(v) >= 1E+4 || qAbs(v) <= 1E-4) && v != 0.) { + int p = qFloor(qLn(qAbs(v)) / LN10); + v /= qPow(10., p); + if (v == 10.) { + v = 1.; + p += 1; + } + ret.first = QString::fromUtf8("%1·10").arg(v); + ret.second = QString::number(p); + } else + ret.first = QString::number(v); + return ret; +} + + +void Graphic::drawGraphics() { + if (isHover) + ui->status->setText(tr("Cursor: ") + pointCoords(canvas2real(QPointF(curpos)))); + QPointF srp = -selrect.topLeft(); + double sclx, scly, wid = canvas->width(), hei = canvas->height() - legy; + sclx = (wid - gridborder.x() - margins_.left() - margins_.width()) / selrect.width(); + scly = (hei - gridborder.y() - margins_.top() - margins_.height()) / selrect.height(); + painter->setClipping(true); + painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); + painter->translate(gridborder.x() + margins_.left(), hei - gridborder.y() - margins_.top()); + //if (isOGL && aalias) pen.setWidthF(1.5f); + painter->scale(sclx, -scly); + painter->translate(srp); + QTransform mat = painter->transform(); + painter->resetTransform(); + painter->setWorldMatrixEnabled(false); + QPolygonF cpol; + QPen pen; + for (int i = 0; i < graphics.size(); ++i) { + GraphicType & t(graphics[i]); + QPolygonF & rpol(pause_ ? t.polyline_pause : t.polyline); + if (t.visible && !rpol.isEmpty()) { + pen = t.pen; + if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick); + else pen.setWidthF(pen.widthF()*thick); + pen.setCosmetic(true); + if (t.lines) { + painter->setPen(pen); + if (t.fill) { + cpol = rpol; + painter->setBrush(t.fill_color); + //cpol.push_front(QPointF(cpol.front().x(), 0.)); + //cpol.push_back(QPointF(cpol.back().x(), 0.)); + painter->drawPolygon(mat.map(cpol)); + } else + painter->drawPolyline(mat.map(rpol)); + } + if (t.points) { + if (qRound(t.pointWidth) == t.pointWidth) pen.setWidth(qRound(t.pointWidth*thick)); + else pen.setWidthF(t.pointWidth*thick); + painter->setPen(pen); + painter->drawPoints(mat.map(rpol)); + } + } + } + painter->setWorldMatrixEnabled(true); +} + + +QString Graphic::pointCoords(QPointF point, bool x, bool y) { + QString ret = "("; + if (x) { + if (axis_type_x == Numeric) + ret += QString::number(point.x(), 'f', 3); + else + ret += QDateTime::fromMSecsSinceEpoch(point.x()).toString(); + } + if (y) { + if (ret.size() > 1) ret += " ; "; + ret += QString::number(point.y(), 'f', 3); + } + ret += ")"; + return ret; +} + + +void Graphic::drawGuides() { + if (!guides || !isHover) return; + int wid = canvas->width(), hei = canvas->height(); + painter->setRenderHint(QPainter::Antialiasing, false); + painter->setPen(QPen(grid_pen.color(), qMax(qRound(thick / 1.4), 1))); + painter->resetTransform(); + painter->setClipping(true); + painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y())); + painter->drawLine(0, curpos.y(), wid, curpos.y()); + painter->drawLine(curpos.x(), 0, curpos.x(), hei); + QString str = pointCoords(canvas2real(curpos)) + fp_size; + QFontMetrics fm(font()); + QRect r = fm.boundingRect(str); + QPoint p = curpos + QPoint(font_sz.height() / 4., -font_sz.height() / 4.); + if (r.width() + curpos.x() > wid - font_sz.height() / 2.) p.setX(curpos.x() - r.width() - font_sz.height() / 4.); + if (curpos.y() - r.height() < font_sz.height() / 8.) p.setY(curpos.y() + r.height() - font_sz.height() / 8.); + painter->setPen(text_color); + painter->drawText(p, str); +} + + +void Graphic::drawPause() { + painter->setClipping(false); + painter->save(); + painter->resetMatrix(); + painter->translate(canvas->width() - icon_pause_b.width() - 6, 6); + double o = (0.5 - pause_phase) * 2; + painter->setOpacity(o*o); + painter->drawImage(0, 0, icon_pause_b); + painter->setOpacity(1.); + painter->drawImage(0, 0, icon_pause_f); + painter->restore(); + painter->setClipping(true); +} + + +double Graphic::splitRange(double range, int count) { + double digits, step, tln; + range = qAbs(range); + tln = qFloor(qLn(range) / LN10); + for (int i = 0; i <= 5; ++i) { + digits = qPow(10., tln - i); + step = qRound(range / count / digits); + if (step > 0.) { + digits = qPow(10., tln - i - 1); + step = qRound(range / count / digits); + break; + } + } + double step5 = qRound(step / 5.) * 5., step10 = qRound(step / 10.) * 10.; + double err5 = qAbs(step - step5), err10 = qAbs(step - step10); + step = (err5 < err10 ? step5 : step10) * digits; + return step; +} + + +double Graphic::splitRangeDate(double range, int count, QString * format, int step[7]) { + double ret = splitRange(range, count); + //qDebug() << "ret =" << ret << getScaleX(); + if (ret < 1000. * 1) {*format = "ss.zzz"; step[0] = ret;} + else if (ret < 1000. * 60) {*format = "h:m:ss"; step[1] = qRound(ret / 1000);} + else if (ret < 1000. * 60 * 60) {*format = "h:mm"; step[2] = qRound(ret / 1000 / 60);} + else if (ret < 1000. * 60 * 60 * 24) {*format = "dd(ddd) hh"; step[3] = qRound(ret / 1000 / 60 / 60);} + else if (ret < 1000. * 60 * 60 * 24 * 30) {*format = "MMM dd"; step[4] = qRound(ret / 1000 / 60 / 60 / 24);} + else if (ret < 1000. * 60 * 60 * 24 * 30 * 12) {*format = "yyyy MMM"; step[5] = qRound(ret / 1000 / 60 / 60 / 24 / 30);} + else {*format = "yyyy"; step[6] = qRound(ret / 1000 / 60 / 60 / 24 / 30 / 12);} + return ret; +} + + +double Graphic::roundTo(double value, double round_to) { + if (round_to == 0.) return value; + return qRound(value / round_to) * round_to; +} + + +void Graphic::roundDateTime(QDateTime & dt, int c[7]) { + QDate d(dt.date()); QTime t(dt.time()); + //if (c[0] != 0) t.setHMS(t.hour(), t.minute(), t.second(), 0); + if (c[1] != 0) t.setHMS(t.hour(), t.minute(), t.second()); + if (c[2] != 0) t.setHMS(t.hour(), t.minute(), 0); + if (c[3] != 0) t.setHMS(t.hour(), 0, 0); + if (c[4] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), d.day());} + if (c[5] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), 1);} + if (c[6] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), 1, 1);} + dt = QDateTime(d, t); +} + + +void Graphic::addDateTime(QDateTime & dt, int c[7], int mul) { + if (c[0] != 0) dt = dt.addMSecs(mul * c[0]); + if (c[1] != 0) dt = dt.addSecs(mul * c[1]); + if (c[2] != 0) dt = dt.addSecs(mul * c[2] * 60); + if (c[3] != 0) dt = dt.addSecs(mul * c[3] * 60 * 60); + if (c[4] != 0) dt = dt.addDays(mul * c[4]); + if (c[5] != 0) dt = dt.addMonths(mul * c[5]); + if (c[6] != 0) dt = dt.addYears(mul * c[6]); +} + + +double Graphic::canvas2realX(double px) const { + int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width(); + double cx = px - gbx, sclx = selrect.width() / (double)wid; + return cx * sclx + selrect.x(); +} + + +double Graphic::canvas2realY(double py) const { + int gby = gridborder.y() + margins_.top(), chei = lasth - legy, hei = chei - gby - margins_.height(); + double cy = chei - py - gby, scly = selrect.height() / (double)hei; + return cy * scly + selrect.y(); +} + + +double Graphic::real2canvasX(double px) const { + int gbx = gridborder.x() + margins_.left(), cwid = lastw, wid = cwid - gbx - margins_.width(); + double sclx = selrect.width() / (double)wid; + return (px - selrect.x()) / sclx + gbx; +} + + +double Graphic::real2canvasY(double py) const { + int gby = gridborder.y() + margins_.top(), chei = lasth - legy, hei = chei - gby - margins_.height(); + double scly = selrect.height() / (double)hei; + return chei - gby - (py - selrect.y()) / scly; +} + + +QPolygonF Graphic::real2canvas(const QPolygonF & real_polygon) const { + QPolygonF ret; + for (int i=0; icanvas_raster->setCursor(cursor); +#ifdef HAS_GL + canvas_gl->setCursor(cursor); +#endif +} + + +void Graphic::swapToBuffer() { + QImage timg; + //qDebug() << "render start"; +#ifdef HAS_GL + if (isOGL) { + timg = canvas_gl->grabFrameBuffer(); + QPainter p(buffer); + p.drawImage(0, 0, timg); + p.end(); + } +#endif + //qDebug() << "render finish"; + bufferActive = true; +} + + +void Graphic::setRectToLines() { + is_lines_update = true; + //line_x_min.is_auto = line_x_max.is_auto = line_y_min.is_auto = line_y_max.is_auto = true; + //qDebug() << "set to lines" << selrect; + //line_x_min.is_reset = line_x_max.is_reset = line_y_min.is_reset = line_y_max.is_reset = isFit; + if (line_x_min.isVisible() && line_x_max.isVisible() && line_y_min.isVisible() && line_y_max.isVisible()) { + line_x_min.blockSignals(true); line_x_max.blockSignals(true); line_y_min.blockSignals(true); line_y_max.blockSignals(true); + if (!line_x_min.hasFocus()) { + if (isFit) line_x_min.setValue(grect.left()); + else line_x_min.setValue(selrect.left()); + } + if (!line_x_max.hasFocus()) { + if(isFit) line_x_max.setValue(grect.right()); + else line_x_max.setValue(selrect.right()); + } + if (!line_y_min.hasFocus()) { + if(isFit) line_y_min.setValue(grect.bottom()); + else line_y_min.setValue(selrect.bottom()); + } + if (!line_y_max.hasFocus()) { + if(isFit) line_y_max.setValue(grect.top()); + else line_y_max.setValue(selrect.top()); + } + line_x_min.setDefaultText(QString::number(grect.left()).toUpper()); + line_x_max.setDefaultText(QString::number(grect.right()).toUpper()); + line_y_min.setDefaultText(QString::number(grect.bottom()).toUpper()); + line_y_max.setDefaultText(QString::number(grect.top()).toUpper()); + line_x_min.blockSignals(false); line_x_max.blockSignals(false); line_y_min.blockSignals(false); line_y_max.blockSignals(false); +// if(isFit) { +// line_y_min.setValue(grect.left()); +// line_y_max.setValue(grect.left()); +// } + } + //line_x_min.is_auto = line_x_max.is_auto = line_y_min.is_auto = line_y_max.is_auto = false; + is_lines_update = false; +} + + +void Graphic::checkLines() { + isFit = (line_x_min.isCleared() && line_x_max.isCleared() && line_y_min.isCleared() && line_y_max.isCleared()); + update(true); +} + + +void Graphic::tick(int index, bool slide, bool update_) { + if (slide) { + ///mutex.lock(); + GraphicType & t(graphics[index]); + if (history > 0.) + while (t.polyline.size() > 1) { + if (fabs(t.polyline.back().x() - t.polyline.front().x()) <= history) break; + /// TODO: [Graphic] fast autofit while addPoint(double y, ...) + if (!t.cvrect.isNull()) { + QPointF fp(t.polyline.first()); + if (qFuzzyCompare(t.cvrect.left(), fp.x()) || + qFuzzyCompare(t.cvrect.right(), fp.x()) || + qFuzzyCompare(t.cvrect.top(), fp.y()) || + qFuzzyCompare(t.cvrect.bottom(), fp.y())) { + t.cvrect = QRectF(); + } + } + t.polyline.pop_front(); + } + } + if (!update_) { + if (isFit) findGraphicsRect(); + ///mutex.unlock(); + return; + } + //polyline.push_back(QPointF(brick->time_, brick->output(port))); + //cout << polyline.size() << endl; + if (isFit) findGraphicsRect(); + if (!slide) { + if (aupdate) update(); + return; + } + ///mutex.unlock(); + if (aupdate) update(); +} + + +void Graphic::on_buttonAutofit_clicked() { + isFit = true; + bool isEmpty = true; + foreach (const GraphicType & t, graphics) { + const QPolygonF & pol(pause_ ? t.polyline_pause : t.polyline); + if (!pol.isEmpty()) { + isEmpty = false; + break; + } + } + if (isEmpty) grect = def_rect; + selrect = grect; + findGraphicsRect(); + update(); +} + + +void Graphic::on_buttonConfigure_clicked() { + conf->graphicItems.clear(); + for (int i = 0; i < graphics.size(); i++) { + GraphicConf::GraphicItem item; + item.icon = graphics[i].icon; + item.name = graphics[i].name; + conf->graphicItems.append(item); + } + conf->ui->colorGrid->setColor(grid_pen.color()); + conf->ui->comboStyleGrid->setCurrentIndex((int)grid_pen.style()); + conf->ui->spinWidthGrid->setValue(grid_pen.widthF()); + conf->ui->checkOGL->setChecked(isOGL); + conf->ui->checkAAlias->setChecked(aalias); + conf->ui->checkInputs->setChecked(borderInputsVisible()); + conf->ui->checkStatus->setChecked(statusVisible()); + conf->ui->checkLegend->setChecked(legendVisible()); + conf->ui->checkGridAutoX->setChecked(grad_x == Auto); + conf->ui->checkGridAutoY->setChecked(grad_y == Auto); + conf->ui->colorBackground->setColor(back_color); + conf->ui->colorText->setColor(text_color); + conf->ui->spinGridStepX->setValue(gridx); + conf->ui->spinGridStepY->setValue(gridy); + conf->ui->spinMarginL->setValue(margins_.left()); + conf->ui->spinMarginT->setValue(margins_.height()); + conf->ui->spinMarginR->setValue(margins_.width()); + conf->ui->spinMarginB->setValue(margins_.top()); + conf->readParams(); + if (conf->exec() == QDialog::Rejected) return; + grid_pen = QPen(conf->ui->colorGrid->color(), conf->ui->spinWidthGrid->value(), (Qt::PenStyle)conf->ui->comboStyleGrid->currentIndex()); + back_color = conf->ui->colorBackground->color(); + text_color = conf->ui->colorText->color(); + grad_x = conf->ui->checkGridAutoX->isChecked() ? Auto : Fixed; + grad_y = conf->ui->checkGridAutoY->isChecked() ? Auto : Fixed; + gridx = conf->ui->spinGridStepX->value(); + gridy = conf->ui->spinGridStepY->value(); + setOpenGL(conf->ui->checkOGL->isChecked()); + setAntialiasing(conf->ui->checkAAlias->isChecked()); + setBorderInputsVisible(conf->ui->checkInputs->isChecked()); + setStatusVisible(conf->ui->checkStatus->isChecked()); + setLegendVisible(conf->ui->checkLegend->isChecked()); + setMargins(conf->ui->spinMarginL->value(), conf->ui->spinMarginR->value(), conf->ui->spinMarginT->value(), conf->ui->spinMarginB->value()); + updateLegend(); + update(); +} + + +void Graphic::on_checkGuides_toggled(bool checked) { + guides = checked; + if (guides) setCanvasCursor(Qt::BlankCursor); + else setCanvasCursor(Qt::ArrowCursor); + update(); +} + + +void Graphic::updateLegend(bool es) { + QPixmap pix(60, 22); + for (int i = 0; i < graphics.size(); i++) { + pix.fill(back_color); + QPainter p(&pix); + QPen pen = graphics[i].pen; + if (qRound(pen.widthF()) == pen.widthF()) pen.setWidth(pen.width()*thick); + else pen.setWidthF(pen.widthF()*thick); + p.setPen(pen); + p.drawLine(0, pix.height() / 2, pix.width(), pix.height() / 2); + p.end(); + graphics[i].icon = QIcon(pix); + } + if (!ui->widgetLegend->isVisibleTo(this)) { + if (es) emit graphicSettingsChanged(); +// qDebug() << "skip updateLegend"; + return; + } +// qDebug() << "updateLegend" << graphics.size(); + leg_update = false; + int ps = 100; + for (int r = 0; r < ui->layoutLegend->rowCount(); ++r) + for (int c = 0; c < ui->layoutLegend->columnCount(); ++c) { + QLayoutItem * li = ui->layoutLegend->itemAtPosition(r, c); + if (!li) continue; + if (!li->widget()) continue; + while (li->widget()->actions().isEmpty()) + li->widget()->removeAction(li->widget()->actions()[0]); + delete li->widget(); + } + ui->layoutLegend->invalidate(); + for (int i = 0; i < graphics.size(); i++) { + graphics[i].pb = new QCheckBox(graphics[i].name); + graphics[i].pb->setIconSize(pix.size()); + //graphics[i].pb->setFlat(true); + graphics[i].pb->setIcon(graphics[i].icon); + graphics[i].pb->setChecked(graphics[i].visible); + graphics[i].pb->setProperty("graphic_num", i); + graphics[i].pb->setContextMenuPolicy(Qt::ActionsContextMenu); + //qDebug() << graphics[i].pb->actions(); + QAction * act = new QAction(trUtf8("Check all"), 0); + act->setCheckable(true); + act->setChecked(true); + graphics[i].pb->addAction(act); + connect(act, SIGNAL(triggered(bool)), this, SLOT(graphicAllVisibleChange(bool))); + connect(graphics[i].pb, SIGNAL(toggled(bool)), this, SLOT(graphicVisibleChange(bool))); + int cps = graphics[i].pb->sizeHint().width() + 4; + if (cps > ps) ps = cps; + } + int maxcol = qMax(ui->widgetLegend->width() / ps - 1, 1); + int row = 0, col = 0; + bool lv = ui->widgetLegend->isVisibleTo(this); + ui->widgetLegend->hide(); + for (int i = 0; i < graphics.size(); i++) { + ui->layoutLegend->addWidget(graphics[i].pb,row,col); + graphics[i].pb->show(); + col++; + if (col > maxcol) {col = 0; row++;} + } + ui->widgetLegend->setVisible(lv); + leg_update = true; + if (es) emit graphicSettingsChanged(); +} + + +void Graphic::updateLegendChecks() { + for (int i = 0; i < graphics.size(); i++) { + if (!graphics[i].pb) continue; + bool pbs = graphics[i].pb->blockSignals(true); + graphics[i].pb->setChecked(graphics[i].visible); + graphics[i].pb->blockSignals(pbs); + } + emit graphicSettingsChanged(); +} + + +void Graphic::graphicVisibleChange(bool checked) { + if (visible_update) return; + QCheckBox * cb = qobject_cast(sender()); + int i = cb->property("graphic_num").toInt(); + graphics[i].visible = checked; + if (isFit) on_buttonAutofit_clicked(); + else update(); + emit graphicSettingsChanged(); + // update(); +} + + +void Graphic::graphicAllVisibleChange(bool checked) { + visible_update = true; + for (int i=0; isetChecked(checked); + } + visible_update = false; + if (isFit) on_buttonAutofit_clicked(); + else update(); + emit graphicSettingsChanged(); +} + + +void Graphic::enterFullscreen() { + if (fullscr) return; + ui->layoutCanvas->removeWidget(canvas); + canvas->setParent(0); + canvas->showFullScreen(); + canvas->setFocus(); + canvas->raise(); + fullscr = true; +} + + +void Graphic::leaveFullscreen() { + if (!fullscr) return; + canvas->setWindowFlags(canvas->windowFlags() & ~Qt::WindowFullScreen); + ui->layoutCanvas->addWidget(canvas); + canvas->show(); + fullscr = false; +} + + +QString Graphic::caption() const { + return ui->labelCaption->text(); +} + + +bool Graphic::borderInputsVisible() const { + return ui->widgetLX->isVisible(); +} + + +bool Graphic::statusVisible() const { + return ui->status->isVisible(); +} + + +bool Graphic::legendVisible() const { + return ui->widgetLegend->isVisible(); +} + + +QByteArray Graphic::save() { +// QByteArray ba; +// QDataStream s(&ba, QIODevice::ReadWrite); +// s << openGL() << antialiasing() << borderInputsVisible() << statusVisible() << legendVisible(); +// s << graphics; +// return ba; + +// version '2': + ChunkStream cs; + cs.add(1, antialiasing()).add(2, openGL()).add(3, borderInputsVisible()).add(4, statusVisible()).add(5, legendVisible()); + cs.add(6, backgroundColor()).add(7, textColor()).add(8, margins()); + cs.add(9, gridPen()).add(10, graduationX()).add(11, graduationY()).add(12, graduationStepX()).add(13, graduationStepY()); + cs.add(14, graphics); + cs.add(15, isFit).add(16, visualRect()); + return cs.data().prepend('2'); +} + + +void Graphic::load(QByteArray ba) { + if (ba.isEmpty()) return; + char ver = ba[0]; + //qDebug() << "load" << (int)ver; + switch(ver) { + case '2': {// version '2': + ba.remove(0, 1); + QRectF vrect; + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: setAntialiasing(cs.getData()); break; + case 2: setOpenGL(cs.getData()); break; + case 3: setBorderInputsVisible(cs.getData()); break; + case 4: setStatusVisible(cs.getData()); break; + case 5: setLegendVisible(cs.getData()); break; + case 6: setBackgroundColor(cs.getData()); break; + case 7: setTextColor(cs.getData()); break; + case 8: setMargins(cs.getData()); break; + case 9: setGridPen(cs.getData()); break; + case 10: setGraduationX(cs.getData()); break; + case 11: setGraduationY(cs.getData()); break; + case 12: setGraduationStepX(cs.getData()); break; + case 13: setGraduationStepY(cs.getData()); break; + case 14: graphics = cs.getData >(); break; + case 15: isFit = cs.getData(); break; + case 16: vrect = cs.getData(); break; + default: break; + } + } + if (!isFit) setVisualRect(vrect); + } break; + default: {// old version 0: + QDataStream s(ba); + bool a; + s >> a; setOpenGL(a); + s >> a; setAntialiasing(a); + s >> a; setBorderInputsVisible(a); + s >> a; setStatusVisible(a); + s >> a; + s >> graphics; + setLegendVisible(a); + } break; + } +} + + +void Graphic::setCaption(const QString & str) { + ui->labelCaption->setText(str); + ui->labelCaption->setVisible(str.length() > 0); + if (aupdate) update(); +} + + +void Graphic::setGridEnabled(bool enabled) { + ui->checkGrid->setChecked(enabled); +} + + +void Graphic::setBorderInputsVisible(bool visible) { + ui->widgetLX->setVisible(visible); + ui->widgetLY->setVisible(visible); + ui->checkBorderInputs->setChecked(visible); + if (visible) setRectToLines(); +} + + +void Graphic::setStatusVisible(bool visible) { + ui->status->setVisible(visible); +} + + +void Graphic::setLegendVisible(bool visible) { + ui->widgetLegend->setVisible(visible); + ui->checkLegend->setChecked(visible); + updateLegend(); +} + + +void Graphic::on_checkExpandY_toggled(bool checked) { + only_expand_y = checked; + ui->checkExpandY->setIcon(checked ? icon_exp_y : icon_exp_sy); +} + + +void Graphic::on_checkExpandX_toggled(bool checked) { + only_expand_x = checked; + ui->checkExpandX->setIcon(checked ? icon_exp_x : icon_exp_sx); +} diff --git a/test/qad/graphic/graphic.h b/test/qad/graphic/graphic.h new file mode 100644 index 0000000..c2412dc --- /dev/null +++ b/test/qad/graphic/graphic.h @@ -0,0 +1,455 @@ +#ifndef GRAPHIC_H +#define GRAPHIC_H + +#include +#include +#include +#include +#include +///#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "graphic_conf.h" +#include "evalspinbox.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +namespace Ui { + class Graphic; +} + +class UGLWidget; + +class Graphic: public QFrame +{ + Q_OBJECT + Q_FLAGS(Buttons) + Q_ENUMS(Alignment Graduation AxisType) + + Q_PROPERTY(QString caption READ caption WRITE setCaption) + Q_PROPERTY(QString labelX READ labelX WRITE setLabelX) + Q_PROPERTY(QString labelY READ labelY WRITE setLabelY) + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) + Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) + + Q_PROPERTY(int currentGraphic READ currentGraphic WRITE setCurrentGraphic) + Q_PROPERTY(int graphicsCount READ graphicsCount WRITE setGraphicsCount) + Q_PROPERTY(QString graphicName READ graphicName WRITE setGraphicName) + Q_PROPERTY(QPen graphicPen READ graphicPen WRITE setGraphicPen) + Q_PROPERTY(QColor graphicColor READ graphicColor WRITE setGraphicColor) + Q_PROPERTY(Qt::PenStyle graphicStyle READ graphicStyle WRITE setGraphicStyle) + Q_PROPERTY(double graphicLineWidth READ graphicLineWidth WRITE setGraphicLineWidth) + Q_PROPERTY(double graphicPointWidth READ graphicPointWidth WRITE setGraphicPointWidth) + Q_PROPERTY(QColor graphicFillColor READ graphicFillColor WRITE setGraphicFillColor) + Q_PROPERTY(bool graphicLinesEnabled READ graphicLinesEnabled WRITE setGraphicLinesEnabled) + Q_PROPERTY(bool graphicPointsEnabled READ graphicPointsEnabled WRITE setGraphicPointsEnabled) + Q_PROPERTY(bool graphicFillEnabled READ graphicFillEnabled WRITE setGraphicFillEnabled) + + Q_PROPERTY(bool gridEnabled READ gridEnabled WRITE setGridEnabled) + Q_PROPERTY(QPen gridPen READ gridPen WRITE setGridPen) + Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor) + Q_PROPERTY(Qt::PenStyle gridStyle READ gridStyle WRITE setGridStyle) + + Q_PROPERTY(QPen selectionPen READ selectionPen WRITE setSelectionPen) + Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor) + Q_PROPERTY(Qt::PenStyle selectionStyle READ selectionStyle WRITE setSelectionStyle) + Q_PROPERTY(QBrush selectionBrush READ selectionBrush WRITE setSelectionBrush) + + Q_PROPERTY(Alignment buttonsPosition READ buttonsPosition WRITE setButtonsPosition) + Q_PROPERTY(Buttons buttons READ buttons WRITE setButtons) + Q_PROPERTY(bool navigationEnabled READ navigationEnabled WRITE setNavigationEnabled) + Q_PROPERTY(bool openGL READ openGL WRITE setOpenGL) + Q_PROPERTY(bool antialiasing READ antialiasing WRITE setAntialiasing) + Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate) + Q_PROPERTY(bool borderInputsVisible READ borderInputsVisible WRITE setBorderInputsVisible) + Q_PROPERTY(bool statusVisible READ statusVisible WRITE setStatusVisible) + Q_PROPERTY(bool legendVisible READ legendVisible WRITE setLegendVisible) + Q_PROPERTY(bool paused READ paused WRITE setPaused) + Q_PROPERTY(bool onlyExpandY READ onlyExpandY WRITE setOnlyExpandY) + Q_PROPERTY(bool onlyExpandX READ onlyExpandX WRITE setOnlyExpandX) + Q_PROPERTY(bool gesturesNavigation READ gesturesNavigation WRITE setGesturesNavigation) + Q_PROPERTY(double historySize READ historySize WRITE setHistorySize) + Q_PROPERTY(double maxVisibleTime READ maxVisibleTime WRITE setMaxVisibleTime) + Q_PROPERTY(double autoXIncrement READ autoXIncrement WRITE setAutoXIncrement) + Q_PROPERTY(QRectF limit READ limit WRITE setLimit) + Q_PROPERTY(QRect margins READ margins WRITE setMargins) + Q_PROPERTY(QRectF visualRect READ visualRect WRITE setVisualRect) + Q_PROPERTY(QRectF defaultRect READ defaultRect WRITE setDefaultRect) + Q_PROPERTY(int minimumRepaintInterval READ minimumRepaintInterval WRITE setMinimumRepaintInterval) + + Q_PROPERTY(double gridNumbersMultiplierX READ gridNumbersMultiplierX WRITE setGridNumbersMultiplierX) + Q_PROPERTY(double gridNumbersMultiplierY READ gridNumbersMultiplierY WRITE setGridNumbersMultiplierY) + Q_PROPERTY(Graduation graduationX READ graduationX WRITE setGraduationX) + Q_PROPERTY(Graduation graduationY READ graduationY WRITE setGraduationY) + Q_PROPERTY(double graduationStepX READ graduationStepX WRITE setGraduationStepX) + Q_PROPERTY(double graduationStepY READ graduationStepY WRITE setGraduationStepY) + Q_PROPERTY(AxisType axisType READ axisType WRITE setAxisType) + + Q_PROPERTY(int histogramMinIntervals READ histogramMinIntervals WRITE setHistogramMinIntervals) + Q_PROPERTY(int histogramMaxIntervals READ histogramMaxIntervals WRITE setHistogramMaxIntervals) + Q_PROPERTY(double histogramMinDeltaMultiplier READ histogramMinDeltaMultiplier WRITE setHistogramMinDeltaMultiplier) + + Q_PROPERTY(Graphic::GraphicsData graphicsData READ graphicsData WRITE setGraphicsData) + Q_PROPERTY(QByteArray graphicsDataRaw READ graphicsDataRaw WRITE setGraphicsDataRaw) + +public: + Graphic(QWidget * parent = 0); + ~Graphic(); + + typedef QVector > GraphicsData; + enum GraphicAction {gaNone, gaZoomInRect, gaZoomRangeX, gaZoomRangeY, gaMove}; + enum Button {NoButtons = 0x0, + AllButtons = 0xFFFFFFFF, + Autofit = 0x01, + Grid = 0x02, + CursorAxis = 0x04, + OnlyExpandY = 0x08, + OnlyExpandX = 0x10, + Fullscreen = 0x20, + BorderInputs = 0x40, + Legend = 0x80, + Configure = 0x100, + Save = 0x200, + Clear = 0x800, + Close = 0x1000, + Pause = 0x2000, + StandartButtons = 0x2FFF + }; + enum Alignment {Left, Right}; + enum Graduation {Auto, Fixed}; + enum AxisType {Numeric, DateTime}; + Q_DECLARE_FLAGS(Buttons, Button) + + QString caption() const; + QString labelX() const {return label_x;} + QString labelY() const {return label_y;} + QString graphicName() const {return graphics[curGraphic].name;} + QColor backgroundColor() const {return back_color;} + QColor textColor() const {return text_color;} + QColor graphicColor() const {return graphics[curGraphic].pen.color();} + QColor gridColor() const {return grid_pen.color();} + QColor selectionColor() const {return selpen.color();} + Qt::PenStyle graphicStyle() const {return graphics[curGraphic].pen.style();} + Qt::PenStyle gridStyle() const {return grid_pen.style();} + Qt::PenStyle selectionStyle() const {return selpen.style();} + double graphicLineWidth() const {return graphics[curGraphic].pen.widthF();} + double graphicPointWidth() const {return graphics[curGraphic].pointWidth;} + QColor graphicFillColor() const {return graphics[curGraphic].fill_color;} + bool graphicVisible() const {return graphics[curGraphic].visible;} + bool graphicLinesEnabled() const {return graphics[curGraphic].lines;} + bool graphicPointsEnabled() const {return graphics[curGraphic].points;} + bool graphicFillEnabled() const {return graphics[curGraphic].fill;} + QPen graphicPen() const {return graphics[curGraphic].pen;} + QPen gridPen() const {return grid_pen;} + QPen selectionPen() const {return selpen;} + QBrush selectionBrush() const {return selbrush;} + bool navigationEnabled() const {return navigation;} + bool openGL() const {return isOGL;} + bool antialiasing() const {return aalias;} + bool autoUpdate() const {return aupdate;} + bool gridEnabled() const {return grid;} + bool borderInputsVisible() const; + bool statusVisible() const; + bool legendVisible() const; + bool paused() const {return pause_;} + bool onlyExpandY() const {return only_expand_y;} + bool onlyExpandX() const {return only_expand_x;} + bool gesturesNavigation() const {return gestures;} + bool isAutofitted() const {return isFit;} + int currentGraphic() const {return curGraphic;} + int graphicsCount() const {return graphics.size();} + Graphic::Buttons buttons() const {return buttons_;} + Graphic::Alignment buttonsPosition() const {return align;} + double historySize() const {return history;} + double maxVisibleTime() const {return visible_time;} + double autoXIncrement() const {return inc_x;} + QRectF visualRect() const {return selrect;} + QRectF defaultRect() const {return def_rect;} + QRectF limit() const {return limit_;} + QRect margins() const {return margins_;} + int minimumRepaintInterval() const {return min_repaint_int;} + int histogramMinIntervals() const {return min_int;} + int histogramMaxIntervals() const {return max_int;} + double histogramMinDeltaMultiplier() const {return mdm;} + double gridNumbersMultiplierX() const {return grid_numbers_x;} + double gridNumbersMultiplierY() const {return grid_numbers_y;} + Graduation graduationX() const {return grad_x;} + Graduation graduationY() const {return grad_y;} + double graduationStepX() const {return gridx;} + double graduationStepY() const {return gridy;} + AxisType axisType() const {return axis_type_x;} + QVector graphicData(const int index = 0) const {return graphics[index].polyline;} + GraphicsData graphicsData() const; + QByteArray graphicsDataRaw() const; + QWidget * viewport() const {return canvas;} + QByteArray save(); + void load(QByteArray ba); + ///void lock() {mutex_.lock();} + ///void unlock() {mutex_.unlock();} + + ///void reset() {mutex.lock(); clear(); mutex.unlock();} + void reset() {clear();} + + GraphicType graphic(int arg) {if (arg < 0 || arg >= graphics.size()) return GraphicType(); return graphics[arg];} + const QVector & allGraphics() const {return graphics;} + void setAllGraphics(const QVector & g, bool update = true) {graphics = g; if (update) updateLegend();} +// void setHistogramData(const QVector & g, int graphic); +// void setHistogramData(const QVector & g) {setHistogramData(g, curGraphic);} + + double canvas2realX(double px) const; + double canvas2realY(double py) const; + double real2canvasX(double px) const; + double real2canvasY(double py) const; + QPointF canvas2real(QPointF canvas_point) const {return QPointF(canvas2realX(canvas_point.x()), canvas2realY(canvas_point.y()));} + QPointF real2canvas(QPointF real_point) const {return QPointF(real2canvasX(real_point.x()), real2canvasY(real_point.y()));} + QPolygonF real2canvas(const QPolygonF & real_polygon) const; + QPolygonF canvas2real(const QPolygonF & canvas_polygon) const; + double getScaleX() const {return real2canvasX(1.) - real2canvasX(0.);} + double getScaleY() const {return real2canvasY(1.) - real2canvasY(0.);} + QPointF getScale() const {return QPointF(getScaleX(), getScaleY());} + +public slots: + void setCaption(const QString & str); + void setLabelX(const QString & str) {label_x = str; hasLblX = (str.length() > 0); if (aupdate) update();} + void setLabelY(const QString & str) {label_y = str; hasLblY = (str.length() > 0); if (aupdate) update();} + void setGraphicName(const QString & str, int index) {graphics[index].name = str; updateLegend(); if (aupdate) update();} + void setGraphicName(const QString & str) {graphics[curGraphic].name = str; updateLegend(); if (aupdate) update();} + void setBackgroundColor(const QColor & color) {back_color = color; if (aupdate) update(); updateLegend();} + void setTextColor(const QColor & color) {text_color = color; if (aupdate) update();} + void setGraphicColor(const QColor & color, int index) {graphics[index].pen.setColor(color); updateLegend(); if (aupdate) update();} + void setGraphicColor(const QColor & color) {setGraphicColor(color, curGraphic);} + void setGridColor(const QColor & color) {grid_pen.setColor(color); if (aupdate) update();} + void setSelectionColor(const QColor & color) {selpen.setColor(color);} + void setGraphicStyle(const Qt::PenStyle & style) {graphics[curGraphic].pen.setStyle(style); updateLegend(); if (aupdate) update();} + void setGridStyle(const Qt::PenStyle & style) {grid_pen.setStyle(style); if (aupdate) update();} + void setSelectionStyle(const Qt::PenStyle & style) {selpen.setStyle(style);} + void setGraphicVisible(bool visible, int index) {graphics[index].visible = visible; updateLegendChecks(); if (aupdate) update();} + void setGraphicVisible(bool visible) {setGraphicVisible(visible, curGraphic);} + void setGraphicLineWidth(double w, int index) {if (qRound(w) == w) graphics[index].pen.setWidth(qRound(w)); else graphics[index].pen.setWidthF(w); updateLegend(); if (aupdate) update();} + void setGraphicLineWidth(double w) {setGraphicLineWidth(w, curGraphic);} + void setGraphicPointWidth(double w, int index) {graphics[index].pointWidth = w; updateLegend(); if (aupdate) update();} + void setGraphicPointWidth(double w) {setGraphicPointWidth(w, curGraphic);} + void setGraphicFillColor(const QColor & w, int index) {graphics[index].fill_color = w; updateLegend(); if (aupdate) update();} + void setGraphicFillColor(const QColor & w) {setGraphicFillColor(w, curGraphic);} + void setGraphicLinesEnabled(bool w, int index) {graphics[index].lines = w; updateLegend(); if (aupdate) update();} + void setGraphicLinesEnabled(bool w) {setGraphicLinesEnabled(w, curGraphic);} + void setGraphicPointsEnabled(bool w, int index) {graphics[index].points = w; updateLegend(); if (aupdate) update();} + void setGraphicPointsEnabled(bool w) {setGraphicPointsEnabled(w, curGraphic);} + void setGraphicFillEnabled(bool w, int index) {graphics[index].fill = w; updateLegend(); if (aupdate) update();} + void setGraphicFillEnabled(bool w) {setGraphicFillEnabled(w, curGraphic);} + void setGraphicPen(const QPen & pen, int index) {graphics[index].pen = pen; updateLegend(); if (aupdate) update();} + void setGraphicPen(const QPen & pen) {setGraphicPen(pen, curGraphic);} + void setGridPen(const QPen & pen) {grid_pen = pen; if (aupdate) update();} + void setSelectionPen(const QPen & pen) {selpen = pen;} + void setSelectionBrush(const QBrush & brush) {selbrush = brush;} + void setNavigationEnabled(bool on) {navigation = on;} + void setOpenGL(bool on); + void setAntialiasing(bool enabled); + void setAutoUpdate(bool enabled) {aupdate = enabled;} + void setGridEnabled(bool enabled); + void setBorderInputsVisible(bool visible); + void setStatusVisible(bool visible); + void setLegendVisible(bool visible); + void setPaused(bool yes); + void setButtons(Graphic::Buttons b); + void setButtonsPosition(Graphic::Alignment a); + void setHistorySize(double val); + void setMaxVisibleTime(double val) {visible_time = val;} + void setAutoXIncrement(double val) {inc_x = val;} + void setLimit(const QRectF & val) {limit_ = val;} + void setMargins(const QRect & val) {margins_ = val; update();} + void setMargins(int left_, int right_, int top_, int bottom_) {setMargins(QRect(left_, bottom_, right_, top_));} + void setLeftMargin(int value) {margins_.moveLeft(value); setMargins(margins_);} + void setRightMargin(int value) {margins_.setWidth(value); setMargins(margins_);} + void setTopMargin(int value) {margins_.setHeight(value); setMargins(margins_);} + void setBottomMargin(int value) {margins_.moveTop(value); setMargins(margins_);} + void setMinimumRepaintInterval(const int & val) {min_repaint_int = val;} + void setOnlyExpandY(bool yes); + void setOnlyExpandX(bool yes); + void setGesturesNavigation(bool yes); + void setHistogramMinIntervals(int value) {min_int = value; updateGraphics();} + void setHistogramMaxIntervals(int value) {max_int = value; updateGraphics();} + void setHistogramMinDeltaMultiplier(double value) {mdm = value; updateGraphics();} + void setGraphicsData(const GraphicsData & gd); + void setGraphicsDataRaw(const QByteArray & ba); + + void setGridNumbersMultiplierX(double value) {grid_numbers_x = value; updateGraphics();} + void setGridNumbersMultiplierY(double value) {grid_numbers_y = value; updateGraphics();} + void setGraduationX(Graduation value) {grad_x = value; if (aupdate) update();;} + void setGraduationY(Graduation value) {grad_y = value; if (aupdate) update();;} + void setGraduationStepX(double sx) {gridx = sx; if (aupdate) update();} + void setGraduationStepY(double sy) {gridy = sy; if (aupdate) update();} + void setGraduationSteps(double sx, double sy) {gridx = sx; gridy = sy; if (aupdate) update();} + void setAxisType(AxisType t) {axis_type_x = t; if (aupdate) update();} + + void addPoint(const QPointF & p, int graphic, bool update_ = true); + void addPoint(const QPointF & p, bool update = true) {addPoint(p, curGraphic, update);} + void addPoint(double x, double y, int graphic, bool update = true) {addPoint(QPointF(x, y), graphic, update);} + void addPoint(double x, double y, bool update = true) {addPoint(QPointF(x, y), update);} + void addPoint(double y, int graphic, bool update = true) {if (graphics[graphic].polyline.isEmpty()) addPoint(QPointF(0.0, y), graphic, update); else addPoint(QPointF(graphics[graphic].max_x + inc_x, y), graphic, update);} + void addPoint(double y, bool update = true) {if (graphics[curGraphic].polyline.isEmpty()) addPoint(QPointF(0.0, y), update); else addPoint(QPointF(graphics[curGraphic].max_x + inc_x, y), update);} + void setGraphicData(const QVector & g, int graphic, bool update_ = true); + void setGraphicData(const QVector & g) {setGraphicData(g, curGraphic);} + void setGraphicProperties(const QString & name, const QColor & color = Qt::darkRed, Qt::PenStyle style = Qt::SolidLine, double width = 0., bool visible = true) {setGraphicProperties(curGraphic, name, color, style, width, visible);} + void setGraphicProperties(int graphic, const QString & name, const QColor & color = Qt::darkRed, Qt::PenStyle style = Qt::SolidLine, double width = 0., bool visible = true); + void addGraphic(const QString & name, const QColor & color = Qt::darkRed, Qt::PenStyle style = Qt::SolidLine, double width = 0., bool visible = true); + void addGraphic(const GraphicType & gd, bool update = true) {graphics << gd; if (update) updateLegend();} + void setVisualRect(const QRectF & rect); + void setDefaultRect(const QRectF & rect); + void autofit() {on_buttonAutofit_clicked();} + void saveImage(); + void clear(); + void update(bool force = false); + void updateGraphics() {findGraphicsRect(); update();} + void setCurrentGraphic(int arg) {if (arg < 0 || arg >= graphics.size()) return; curGraphic = arg;} + void setGraphicsCount(int arg, bool update = true); + void removeGraphic(int arg, bool update = true); + + void zoom(float factor); + void zoomIn() {zoom(1. / 1.2);} + void zoomOut() {zoom(1.2);} + void fullscreen(); + +protected: + virtual void changeEvent(QEvent * e); + virtual void resizeEvent(QResizeEvent * ); + virtual QSize sizeHint() const {return QSize(400, 300);} + virtual void timerEvent(QTimerEvent * ); + virtual bool eventFilter(QObject * o, QEvent * e); + + void prepareCanvas(QWidget * w); + void procGesture(QGesture * g); + void procZoom(QPointF view_center, double dzoom, Qt::KeyboardModifiers km = Qt::NoModifier); + void totalUpdate(); + void setCurrentAction(GraphicAction action); + void findGraphicsRect(double start_x = 0., double end_x = 0., double start_y = 0., double end_y = 0.); + void tick(int index, bool slide = true, bool update = true); + void repaintCanvas(bool force = false) {if (tm.elapsed() < min_repaint_int && !force) return; tm.restart(); canvas->update();} + void drawGraphics(); + void drawGrid(); + void drawGuides(); + void drawPause(); + void drawAction(); + void updateLegend(bool es = true); + void updateLegendChecks(); + void setCanvasCursor(QCursor cursor); + void swapToBuffer(); + void swapToNormal() {bufferActive = false;} + void setRectToLines(); + void checkLines(); + double splitRange(double range, int count = 1); + double splitRangeDate(double range, int count = 1, QString * format = 0, int step[7] = 0); + double roundTo(double value, double round_to); + void roundDateTime(QDateTime & dt, int c[7]); + void addDateTime(QDateTime & dt, int c[7], int mul = 1); + QPointF absPoint(QPointF point) {return QPointF(qAbs(point.x()), qAbs(point.y()));} + QString pointCoords(QPointF point, bool x = true, bool y = true); + QPair gridMark(double v) const; + + Ui::Graphic * ui; + UGLWidget * canvas_gl; + ///QMutex mutex, mutex_; + QWidget * canvas; + QImage * buffer; + QPainter * painter; + QBrush selbrush; + QPen grid_pen, selpen; + QColor back_color, text_color; + QVector graphics; + int curGraphic; + GraphicAction curaction, prevaction; + QRectF grect, rrect, selrect, limit_, def_rect; + QRect margins_; + QSize font_sz; + QPoint startpos, curpos, prevpos, gridborder; + QPointF startpos_r, curpos_r; + QString label_x, label_y, ppath, fp_size; + Graphic::Buttons buttons_; + Graphic::Alignment align; + GraphicConf * conf; + EvalSpinBox line_x_min, line_x_max, line_y_min, line_y_max; + QTime tm; + QIcon icon_exp_x, icon_exp_y, icon_exp_sx, icon_exp_sy; + QImage icon_pause_b, icon_pause_f; + Graduation grad_x, grad_y; + AxisType axis_type_x; + double gridx, gridy, history, visible_time, inc_x, mdm, grid_numbers_x, grid_numbers_y, LN2, LN5, LN10; + double eminx, eminy, emaxx, emaxy, pause_phase, gesture_angle; + int legy, lastw, lasth, min_repaint_int, min_int, max_int, timer_pause, thick; + bool aalias, aupdate, mupdate, grid, guides, isFit, isEmpty, isOGL, isHover, bufferActive, cancel, pause_, isPrinting, gestures; + bool hasLblX, hasLblY, navigation, only_expand_y, only_expand_x, is_lines_update, leg_update, visible_update, fullscr, need_mouse_pan; + +protected slots: + void canvasPaintEvent(); + void canvasMouseMoveEvent(QMouseEvent * ); + void canvasMousePressEvent(QMouseEvent * ); + void canvasMouseReleaseEvent(QMouseEvent * ); + void canvasMouseDoubleClickEvent(QMouseEvent * ); + void canvasWheelEvent(QWheelEvent * ); + void canvasLeaveEvent(QEvent * ); + void canvasKeyPressEvent(QKeyEvent * ); + void graphicVisibleChange(bool checked); + void graphicAllVisibleChange(bool checked); + void lineXMinChanged(double value) {selrect.setLeft(value); checkLines();} + void lineXMaxChanged(double value) {selrect.setRight(value); checkLines();} + void lineYMinChanged(double value) {selrect.setBottom(value); checkLines();} + void lineYMaxChanged(double value) {selrect.setTop(value); checkLines();} + void on_buttonClose_clicked() {emit closeRequest(this);} + void on_buttonClear_clicked() {reset(); emit cleared();} + void on_buttonAutofit_clicked(); + void on_buttonConfigure_clicked(); + void on_buttonFullscreen_clicked() {fullscreen();} + void on_buttonSave_clicked() {saveImage();} + void on_checkGrid_toggled(bool checked) {grid = checked; update();} + void on_checkGuides_toggled(bool checked); + void on_checkExpandY_toggled(bool checked); + void on_checkExpandX_toggled(bool checked); + void on_checkBorderInputs_toggled(bool checked) {setBorderInputsVisible(checked);} + void on_checkLegend_toggled(bool checked) {setLegendVisible(checked);} + void on_checkPause_toggled(bool checked) {setPaused(checked);} + void enterFullscreen(); + void leaveFullscreen(); + +signals: + void beforeGraphicPaintEvent(QPainter * ); + void graphicPaintEvent(QPainter * ); + void graphicMouseMoveEvent(QPointF point, int buttons); + void graphicMousePressEvent(QPointF point, int buttons); + void graphicMouseReleaseEvent(QPointF point, int buttons); + void graphicWheelEvent(QPointF point, int delta); + void closeRequest(QWidget * ); + void cleared(); + void visualRectChanged(); + void graphicSettingsChanged(); + +}; + +Q_DECLARE_METATYPE(Graphic::GraphicsData) + +Q_DECLARE_OPERATORS_FOR_FLAGS(Graphic::Buttons) + +inline QDataStream & operator <<(QDataStream & s, const Graphic::Graduation & v) {s << (int)v; return s;} +inline QDataStream & operator >>(QDataStream & s, Graphic::Graduation & v) {s >> *((int*)(&v)); return s;} + +class __GraphicRegistrator__ { +public: + __GraphicRegistrator__() { + qRegisterMetaType("Graphic::GraphicsData"); + qRegisterMetaTypeStreamOperators("Graphic::GraphicsData"); + } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // GRAPHIC_H diff --git a/test/qad/graphic/graphic.ui b/test/qad/graphic/graphic.ui new file mode 100644 index 0000000..f096c8a --- /dev/null +++ b/test/qad/graphic/graphic.ui @@ -0,0 +1,443 @@ + + + Graphic + + + + 0 + 0 + 564 + 433 + + + + + 150 + 150 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + + + + + -10 + 0 + 33 + 420 + + + + + 2 + + + + + Autofit + + + + :/icons/view-autofit.png:/icons/view-autofit.png + + + + + + + Grid + + + + :/icons/view-grid.png:/icons/view-grid.png + + + true + + + true + + + + + + + Cursor axis + + + + :/icons/edit-guides.png:/icons/edit-guides.png + + + true + + + + + + + Only expand Y + + + + :/icons/expand_s_y.png:/icons/expand_s_y.png + + + true + + + + + + + Only expand X + + + + :/icons/expand_s_x.png:/icons/expand_s_x.png + + + true + + + + + + + Fullscreen + + + + :/icons/view-fullscreen.png:/icons/view-fullscreen.png + + + + + + + Border inputs + + + + :/icons/border-line.png:/icons/border-line.png + + + true + + + true + + + + + + + Legend + + + + :/icons/legend.png:/icons/legend.png + + + true + + + false + + + + + + + Pause + + + + :/icons/media-playback-pause.png:/icons/media-playback-pause.png + + + true + + + + + + + Configure ... + + + + :/icons/configure.png:/icons/configure.png + + + + + + + Save image ... + + + + :/icons/document-save.png:/icons/document-save.png + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 0 + 30 + + + + + + + + Clear + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + Close + + + + :/icons/dialog-close.png:/icons/dialog-close.png + + + + + + + Qt::Vertical + + + + 0 + 79 + + + + + + + + + + + + Qt::RichText + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + QFrame::Box + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + + + true + + + + + + + + + + 0 + 0 + + + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Cursor: ( ; ) + + + + + + + + 0 + 0 + + + + + 1 + + + 1 + + + 1 + + + 1 + + + 4 + + + + + + + + + UWidget + QWidget +
uwidget.h
+
+
+ + + + + + + +
diff --git a/test/qad/graphic/graphic_conf.cpp b/test/qad/graphic/graphic_conf.cpp new file mode 100644 index 0000000..265e64f --- /dev/null +++ b/test/qad/graphic/graphic_conf.cpp @@ -0,0 +1,107 @@ +#include "graphic_conf.h" +#include "qad_types.h" +#include "ui_graphic_conf.h" + + +GraphicConf::GraphicConf(QVector & graphics_, QWidget * parent): QDialog(parent), graphics(graphics_) { + ui = new Ui::GraphicConf(); + ui->setupUi(this); + QStringList styles; + int fh = qMax(fontMetrics().size(0, "0").height(), 22); + int thick = lineThickness(); + QSize sz(fh * 2.5, fh); + styles << tr("NoPen") << tr("Solid") << tr("Dash") + << tr("Dot") << tr("Dash-Dot") << tr("Dash-Dot-Dot"); + ui->comboStyleGrid->setIconSize(sz); + ui->comboStyleGraphic->setIconSize(sz); + ui->cbGraphicNames->setIconSize(sz); + for (int i = 0; i < 6; i++) { + QPixmap pix(sz); + pix.fill(); + QPainter p(&pix); + p.setPen(QPen(Qt::black, thick, (Qt::PenStyle)i)); + p.drawLine(0, pix.height() / 2, pix.width(), pix.height() / 2); + p.end(); + ui->comboStyleGraphic->addItem(QIcon(pix), styles[i]); + ui->comboStyleGrid->addItem(QIcon(pix), styles[i]); + } +} + + +void GraphicConf::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + return; + } + QDialog::changeEvent(e); +} + + +void GraphicConf::readParams() { + ui->cbGraphicNames->clear(); + for (int i = 0; i < graphicItems.size(); i++) + ui->cbGraphicNames->addItem(graphicItems[i].icon, graphicItems[i].name); +} + + +void GraphicConf::on_cbGraphicNames_currentIndexChanged(int i) { + if (i < 0) return; + if (graphicItems.isEmpty()) return; + ui->comboStyleGraphic->setCurrentIndex((int)graphics[i].pen.style()); + ui->colorGraphic->setColor(graphics[i].pen.color()); + ui->colorFill->setColor(graphics[i].fill_color); + ui->spinLineWidthGraphic->setValue(graphics[i].pen.widthF()); + ui->spinPointWidthGraphic->setValue(graphics[i].pointWidth); + ui->checkLines->setChecked(graphics[i].lines); + ui->checkPoints->setChecked(graphics[i].points); + ui->checkFill->setChecked(graphics[i].fill); +} + + +void GraphicConf::on_colorGraphic_colorChanged(const QColor & c) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].pen.setColor(c); +} + + +void GraphicConf::on_comboStyleGraphic_currentIndexChanged(int index) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].pen.setStyle((Qt::PenStyle)index); +} + + +void GraphicConf::on_spinLineWidthGraphic_valueChanged(double value) { + if (graphicItems.isEmpty()) return; + if (qRound(value) == value) graphics[ui->cbGraphicNames->currentIndex()].pen.setWidth(qRound(value)); + else graphics[ui->cbGraphicNames->currentIndex()].pen.setWidthF(value); +} + + +void GraphicConf::on_spinPointWidthGraphic_valueChanged(double value) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].pointWidth = value; +} + + +void GraphicConf::on_checkLines_toggled(bool on) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].lines = on; +} + + +void GraphicConf::on_checkPoints_toggled(bool on) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].points = on; +} + + +void GraphicConf::on_checkFill_toggled(bool on) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].fill = on; +} + + +void GraphicConf::on_colorFill_colorChanged(const QColor & color) { + if (graphicItems.isEmpty()) return; + graphics[ui->cbGraphicNames->currentIndex()].fill_color = color; +} diff --git a/test/qad/graphic/graphic_conf.h b/test/qad/graphic/graphic_conf.h new file mode 100644 index 0000000..dd5305c --- /dev/null +++ b/test/qad/graphic/graphic_conf.h @@ -0,0 +1,91 @@ +#ifndef GRAPHIC_CONF_H +#define GRAPHIC_CONF_H + +#include +#include +#include +#include + + +namespace Ui { + class GraphicConf; +}; + + +struct GraphicType { + GraphicType(QString name_ = "y(x)", QColor color = Qt::red, Qt::PenStyle style = Qt::SolidLine, double width = 0., bool visible_ = true) { + pen.setColor(color); + pen.setStyle(style); + lines = true; + points = false; + fill = false; + fill_color = Qt::yellow; + if (qRound(width) == width) pen.setWidth(qRound(width)); + else pen.setWidthF(width); + pen.setWidth(1); + pen.setCosmetic(true); + max_x = 0.; + name = name_; + visible = visible_; + pointWidth = 2.; + pb = new QCheckBox(name); + } + //~GraphicType() {delete pb;} + QString name; + QPolygonF polyline; + QPolygonF polyline_pause; + QPen pen; + QColor fill_color; + bool lines; + bool points; + bool fill; + double pointWidth; + double max_x; + double max_x_pause; + QCheckBox * pb; + QIcon icon; + bool visible; + QRectF cvrect; +}; + + +inline QDataStream & operator <<(QDataStream & s, const GraphicType & v) {s << v.name << v.pen << v.fill_color << v.lines << v.points << v.fill << v.pointWidth << v.visible; return s;} +inline QDataStream & operator >>(QDataStream & s, GraphicType & v) {s >> v.name >> v.pen >> v.fill_color >> v.lines >> v.points >> v.fill >> v.pointWidth >> v.visible; return s;} + + +class GraphicConf: public QDialog +{ + Q_OBJECT + friend class Graphic; +public: + explicit GraphicConf(QVector & graphics_, QWidget * parent = 0); + + struct GraphicItem { + QString name; + QIcon icon; + }; + + void readParams(); + + QVector & graphics; + QVector graphicItems; + +protected: + void changeEvent(QEvent * e); + + Ui::GraphicConf * ui; + +private slots: + void on_cbGraphicNames_currentIndexChanged(int index); + void on_colorGraphic_colorChanged(const QColor &); + void on_colorFill_colorChanged(const QColor &); + void on_comboStyleGraphic_currentIndexChanged(int index); + void on_spinLineWidthGraphic_valueChanged(double value); + void on_spinPointWidthGraphic_valueChanged(double value); + void on_checkLines_toggled(bool on); + void on_checkPoints_toggled(bool on); + void on_checkFill_toggled(bool on); + +}; + +#endif // GRAPHIC_CONF_H diff --git a/test/qad/graphic/graphic_conf.ui b/test/qad/graphic/graphic_conf.ui new file mode 100644 index 0000000..7b26a2a --- /dev/null +++ b/test/qad/graphic/graphic_conf.ui @@ -0,0 +1,644 @@ + + + GraphicConf + + + Qt::WindowModal + + + + 0 + 0 + 500 + 583 + + + + Graphic parameters + + + true + + + + + + Appearance + + + + + + + + Border inputs + + + + + + + Antialiasing + + + + + + + Status bar + + + + + + + OpenGL + + + + + + + Legend + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + 0 + 0 + + + + Background color: + + + + + + + true + + + + + + + + 0 + 0 + + + + Text color: + + + + + + + true + + + + + + + + + + + + Graphics + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + 0 + 0 + + + + + + + + Color: + + + + + + + true + + + + + + + Style: + + + + + + + + + + Lines width: + + + true + + + + + + + 2 + + + 1.000000000000000 + + + + + + + Points width: + + + true + + + + + + + 2 + + + 999.990000000000009 + + + 1.000000000000000 + + + + + + + Fill: + + + true + + + + + + + true + + + + + + + + + + Grid + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Color: + + + + + + + true + + + + + + + Style: + + + + + + + + + + Width: + + + + + + + 2 + + + + + + + Step X: + + + + + + + Step Y: + + + + + + + Auto X + + + true + + + + + + + Auto Y + + + true + + + + + + + 30 + + + 30 + + + true + + + + + + + 50 + + + 50 + + + true + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Margins + + + + + + px + + + 100 + + + + + + + px + + + 100 + + + + + + + All: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + px + + + 100 + + + + + + + Right: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Left: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Bottom: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + px + + + 100 + + + + + + + Top: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + px + + + 100 + + + + + + + + + + + ColorButton + QPushButton +
colorbutton.h
+ + colorChanged(QColor) + +
+ + EvalSpinBox + QWidget +
evalspinbox.h
+
+
+ + checkAAlias + checkOGL + colorBackground + colorText + colorGrid + comboStyleGrid + spinWidthGrid + cbGraphicNames + colorGraphic + comboStyleGraphic + checkLines + spinLineWidthGraphic + checkPoints + spinPointWidthGraphic + buttonBox + + + + + buttonBox + rejected() + GraphicConf + reject() + + + 255 + 641 + + + 245 + 207 + + + + + checkLines + toggled(bool) + spinLineWidthGraphic + setEnabled(bool) + + + 322 + 410 + + + 415 + 411 + + + + + checkPoints + toggled(bool) + spinPointWidthGraphic + setEnabled(bool) + + + 322 + 434 + + + 415 + 435 + + + + + buttonBox + accepted() + GraphicConf + accept() + + + 294 + 641 + + + 288 + 268 + + + + + checkFill + toggled(bool) + colorFill + setEnabled(bool) + + + 322 + 458 + + + 415 + 460 + + + + + spinMarginT_2 + valueChanged(int) + spinMarginT + setValue(int) + + + 259 + 221 + + + 249 + 191 + + + + + spinMarginT_2 + valueChanged(int) + spinMarginR + setValue(int) + + + 268 + 220 + + + 371 + 220 + + + + + spinMarginT_2 + valueChanged(int) + spinMarginB + setValue(int) + + + 233 + 230 + + + 234 + 252 + + + + + spinMarginT_2 + valueChanged(int) + spinMarginL + setValue(int) + + + 213 + 230 + + + 133 + 229 + + + + +
diff --git a/test/qad/graphic/lang/qad_graphic_ru.ts b/test/qad/graphic/lang/qad_graphic_ru.ts new file mode 100644 index 0000000..2cf267f --- /dev/null +++ b/test/qad/graphic/lang/qad_graphic_ru.ts @@ -0,0 +1,351 @@ + + + + + Graphic + + + + Autofit + Автомасштаб + + + + + Grid + Сетка + + + + + Cursor axis + Плавающие оси + + + + + Only expand Y + Только расширять Y + + + + + Only expand X + Только расширять X + + + + + Fullscreen + Во весь экран + + + + + Border inputs + Граничные поля ввода + + + + + Legend + Легенда + + + + + Configure ... + Настроить ... + + + + + Save image ... + Сохранить изображение ... + + + + + Clear + Очистить + + + + + Close + Закрыть + + + + + Cursor: ( ; ) + Курсор: ( ; ) + + + + + Cursor: + Курсор: + + + + Selection + Выделение + + + + Size + Размер + + + + + Range + Диапазон + + + + + Length + Длина + + + + + Cursor + Курсор + + + + Save Image + Сохранить изображение + + + + y(x) + + + + + Check all + Выбрать все + + + + GraphicConf + + + + Graphic parameters + Параметры графика + + + + + Appearance + Внешний вид + + + + + Border inputs + Граничные поля ввода + + + + + Antialiasing + Сглаживание + + + + + Status bar + Панель статуса + + + + + OpenGL + + + + + + Legend + Легенда + + + + + Background color: + Цвет фона: + + + + + Text color: + Цвет текста: + + + + + Graphics + Графики + + + + + + + Color: + Цвет: + + + + + + + Style: + Стиль: + + + + + Lines width: + Толщина линий: + + + + + Points width: + Толщина точек: + + + + + Fill: + Заливка: + + + + + Grid + Сетка + + + + + Width: + Толщина: + + + + + Step X: + Шаг X: + + + + + Step Y: + Шаг Y: + + + + + Auto X + Авто X + + + + + Auto Y + Авто Y + + + Auto step + Автоматический шаг + + + + + Margins + Поля + + + + + + + + + + + + + px + пикс + + + + + All: + Все: + + + + + Right: + Правое: + + + + + Left: + Левое: + + + + + Bottom: + Нижнее: + + + + + Top: + Верхнее: + + + + NoPen + НетЛинии + + + + Solid + Сплошная + + + + Dash + Штриховая + + + + Dot + Пунктирная + + + + Dash-Dot + ШтрихПунктирная + + + + Dash-Dot-Dot + ШтрихПунктирПунктирная + + + diff --git a/test/qad/graphic/plugin/CMakeLists.txt b/test/qad/graphic/plugin/CMakeLists.txt new file mode 100644 index 0000000..fea0294 --- /dev/null +++ b/test/qad/graphic/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(graphic "Gui;Widgets;OpenGL" "") diff --git a/test/qad/graphic/plugin/graphicplugin.cpp b/test/qad/graphic/plugin/graphicplugin.cpp new file mode 100644 index 0000000..c4ff495 --- /dev/null +++ b/test/qad/graphic/plugin/graphicplugin.cpp @@ -0,0 +1,69 @@ +#include "graphic.h" +#include "graphicplugin.h" +#include + + +GraphicPlugin::GraphicPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void GraphicPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool GraphicPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * GraphicPlugin::createWidget(QWidget * parent) { + return new Graphic(parent); +} + + +QString GraphicPlugin::name() const { + return QLatin1String("Graphic"); +} + + +QString GraphicPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon GraphicPlugin::icon() const { + return QIcon(":/icons/graphic.png"); +} + + +QString GraphicPlugin::toolTip() const { + return QLatin1String("");//QLatin1String("Widget for display any math graphics with grid and navigation"); +} + + +QString GraphicPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool GraphicPlugin::isContainer() const { + return false; +} + + +QString GraphicPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString GraphicPlugin::includeFile() const { + return QLatin1String("graphic.h"); +} + diff --git a/test/qad/graphic/plugin/graphicplugin.h b/test/qad/graphic/plugin/graphicplugin.h new file mode 100644 index 0000000..4112d65 --- /dev/null +++ b/test/qad/graphic/plugin/graphicplugin.h @@ -0,0 +1,36 @@ +#ifndef GRAPHICPLUGIN_H +#define GRAPHICPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class GraphicPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + GraphicPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/graphic/plugin/qad_graphic.cpp b/test/qad/graphic/plugin/qad_graphic.cpp new file mode 100644 index 0000000..4dd23cd --- /dev/null +++ b/test/qad/graphic/plugin/qad_graphic.cpp @@ -0,0 +1,17 @@ +#include "qad_graphic.h" +#include "graphicplugin.h" + +QADGraphic::QADGraphic(QObject * parent): QObject(parent) +{ + m_widgets.append(new GraphicPlugin(this)); +} + + +QList QADGraphic::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_graphic_plugin, QADGraphic) +#endif diff --git a/test/qad/graphic/plugin/qad_graphic.h b/test/qad/graphic/plugin/qad_graphic.h new file mode 100644 index 0000000..07098fa --- /dev/null +++ b/test/qad/graphic/plugin/qad_graphic.h @@ -0,0 +1,23 @@ +#ifndef QAD_GRAPHIC_H +#define QAD_GRAPHIC_H + +#include +#include + +class QADGraphic: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.graphic") +#endif +public: + explicit QADGraphic(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_GRAPHIC_H diff --git a/test/qad/graphic/qad_graphic.qrc b/test/qad/graphic/qad_graphic.qrc new file mode 100644 index 0000000..3ca1b8e --- /dev/null +++ b/test/qad/graphic/qad_graphic.qrc @@ -0,0 +1,28 @@ + + + ../icons/media-playback-pause.png + ../icons/dialog-close.png + ../icons/edit-clear.png + ../icons/edit-guides.png + ../icons/view-grid.png + ../icons/view-autofit.png + ../icons/configure.png + ../icons/document-save.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/expand_s_x.png + ../icons/expand_s_y.png + ../icons/expand_x.png + ../icons/expand_y.png + ../icons/border-line.png + ../icons/legend.png + ../icons/graphic.png + ../icons/view-fullscreen.png + ../icons/pause-back.png + ../icons/pause-front.png + + diff --git a/test/qad/graphic/qpicalculator/CMakeLists.txt b/test/qad/graphic/qpicalculator/CMakeLists.txt new file mode 100644 index 0000000..6cd2148 --- /dev/null +++ b/test/qad/graphic/qpicalculator/CMakeLists.txt @@ -0,0 +1,27 @@ +project(qpicalc) +find_qt(${QtVersions} Core Gui Widgets) +qt_sources(SRC) +qt_wrap(${SRC} CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets qad_graphic) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() + diff --git a/test/qad/graphic/qpicalculator/CMakeLists.txt.user b/test/qad/graphic/qpicalculator/CMakeLists.txt.user new file mode 100644 index 0000000..6d91a76 --- /dev/null +++ b/test/qad/graphic/qpicalculator/CMakeLists.txt.user @@ -0,0 +1,188 @@ + + + + + + EnvironmentId + {948faa78-0b50-402e-a285-1bca3b08de64} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + false + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + DesktopBuild + DesktopBuild + {3c749452-9483-442d-b011-933a1b5dac10} + 0 + 0 + 0 + + false + D:/work/qpicalculator-build + + + + + false + true + Сборка + + CMakeProjectManager.MakeStep + + 1 + Сборка + + ProjectExplorer.BuildSteps.Build + + + + clean + + true + true + Сборка + + CMakeProjectManager.MakeStep + + 1 + Очистка + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + all + + CMakeProjectManager.CMakeBuildConfiguration + + 1 + + + 0 + Установка + + ProjectExplorer.BuildSteps.Deploy + + 1 + Локальная установка + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 3 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + qpicalculator + + false + + 2 + + qpicalculator + + CMakeProjectManager.CMakeRunConfiguration.qpicalculator + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 16 + + + Version + 16 + + diff --git a/test/qad/graphic/qpicalculator/icons/Apps-accessories-calculator-icon.png b/test/qad/graphic/qpicalculator/icons/Apps-accessories-calculator-icon.png new file mode 100644 index 0000000..37a3035 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/Apps-accessories-calculator-icon.png differ diff --git a/test/qad/graphic/qpicalculator/icons/application-exit.png b/test/qad/graphic/qpicalculator/icons/application-exit.png new file mode 100644 index 0000000..4839c61 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/application-exit.png differ diff --git a/test/qad/graphic/qpicalculator/icons/arrow-left.png b/test/qad/graphic/qpicalculator/icons/arrow-left.png new file mode 100644 index 0000000..0a74ecf Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/arrow-left.png differ diff --git a/test/qad/graphic/qpicalculator/icons/arrow-right.png b/test/qad/graphic/qpicalculator/icons/arrow-right.png new file mode 100644 index 0000000..0a8a69e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/arrow-right.png differ diff --git a/test/qad/graphic/qpicalculator/icons/axes.png b/test/qad/graphic/qpicalculator/icons/axes.png new file mode 100644 index 0000000..11a059d Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/axes.png differ diff --git a/test/qad/graphic/qpicalculator/icons/axes_1.png b/test/qad/graphic/qpicalculator/icons/axes_1.png new file mode 100644 index 0000000..cb54382 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/axes_1.png differ diff --git a/test/qad/graphic/qpicalculator/icons/character-set.png b/test/qad/graphic/qpicalculator/icons/character-set.png new file mode 100644 index 0000000..9be1a10 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/character-set.png differ diff --git a/test/qad/graphic/qpicalculator/icons/configure.png b/test/qad/graphic/qpicalculator/icons/configure.png new file mode 100644 index 0000000..5ce478b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/configure.png differ diff --git a/test/qad/graphic/qpicalculator/icons/dialog-close.png b/test/qad/graphic/qpicalculator/icons/dialog-close.png new file mode 100644 index 0000000..2c2f99e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/dialog-close.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-close.png b/test/qad/graphic/qpicalculator/icons/document-close.png new file mode 100644 index 0000000..d3937b9 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-close.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-edit.png b/test/qad/graphic/qpicalculator/icons/document-edit.png new file mode 100644 index 0000000..84e345d Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-edit.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-open.png b/test/qad/graphic/qpicalculator/icons/document-open.png new file mode 100644 index 0000000..b225e9a Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-open.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-revert.png b/test/qad/graphic/qpicalculator/icons/document-revert.png new file mode 100644 index 0000000..b224e05 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-revert.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-save-as.png b/test/qad/graphic/qpicalculator/icons/document-save-as.png new file mode 100644 index 0000000..821ca12 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-save-as.png differ diff --git a/test/qad/graphic/qpicalculator/icons/document-save.png b/test/qad/graphic/qpicalculator/icons/document-save.png new file mode 100644 index 0000000..8072aea Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/document-save.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-clear-locationbar-rtl.png b/test/qad/graphic/qpicalculator/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..6c4b83b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-clear-locationbar-rtl.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-clear.png b/test/qad/graphic/qpicalculator/icons/edit-clear.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-clear.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-copy.png b/test/qad/graphic/qpicalculator/icons/edit-copy.png new file mode 100644 index 0000000..5cdeb5f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-copy.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-cut.png b/test/qad/graphic/qpicalculator/icons/edit-cut.png new file mode 100644 index 0000000..f4a55e3 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-cut.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-delete.png b/test/qad/graphic/qpicalculator/icons/edit-delete.png new file mode 100644 index 0000000..87cd0b0 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-delete.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-find.png b/test/qad/graphic/qpicalculator/icons/edit-find.png new file mode 100644 index 0000000..9a462c0 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-find.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-guides.png b/test/qad/graphic/qpicalculator/icons/edit-guides.png new file mode 100644 index 0000000..d264839 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-guides.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-paste.png b/test/qad/graphic/qpicalculator/icons/edit-paste.png new file mode 100644 index 0000000..a4e0a02 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-paste.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-redo.png b/test/qad/graphic/qpicalculator/icons/edit-redo.png new file mode 100644 index 0000000..9bfee2d Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-redo.png differ diff --git a/test/qad/graphic/qpicalculator/icons/edit-undo.png b/test/qad/graphic/qpicalculator/icons/edit-undo.png new file mode 100644 index 0000000..83e41dc Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/edit-undo.png differ diff --git a/test/qad/graphic/qpicalculator/icons/empty_brick.png b/test/qad/graphic/qpicalculator/icons/empty_brick.png new file mode 100644 index 0000000..b09680b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/empty_brick.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-fill-color.png b/test/qad/graphic/qpicalculator/icons/format-fill-color.png new file mode 100644 index 0000000..6601f53 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-fill-color.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-bold.png b/test/qad/graphic/qpicalculator/icons/format-text-bold.png new file mode 100644 index 0000000..3abb164 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-bold.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-color.png b/test/qad/graphic/qpicalculator/icons/format-text-color.png new file mode 100644 index 0000000..9e86afc Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-color.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-italic.png b/test/qad/graphic/qpicalculator/icons/format-text-italic.png new file mode 100644 index 0000000..93ec082 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-italic.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-overline.png b/test/qad/graphic/qpicalculator/icons/format-text-overline.png new file mode 100644 index 0000000..e49a11c Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-overline.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-strikethrough.png b/test/qad/graphic/qpicalculator/icons/format-text-strikethrough.png new file mode 100644 index 0000000..f248ce0 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-strikethrough.png differ diff --git a/test/qad/graphic/qpicalculator/icons/format-text-underline.png b/test/qad/graphic/qpicalculator/icons/format-text-underline.png new file mode 100644 index 0000000..7e47fb8 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/format-text-underline.png differ diff --git a/test/qad/graphic/qpicalculator/icons/go-home.png b/test/qad/graphic/qpicalculator/icons/go-home.png new file mode 100644 index 0000000..aab6a88 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/go-home.png differ diff --git a/test/qad/graphic/qpicalculator/icons/go-next.png b/test/qad/graphic/qpicalculator/icons/go-next.png new file mode 100644 index 0000000..aa7cbb9 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/go-next.png differ diff --git a/test/qad/graphic/qpicalculator/icons/go-previous.png b/test/qad/graphic/qpicalculator/icons/go-previous.png new file mode 100644 index 0000000..8230340 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/go-previous.png differ diff --git a/test/qad/graphic/qpicalculator/icons/graphic_add.png b/test/qad/graphic/qpicalculator/icons/graphic_add.png new file mode 100644 index 0000000..a002a95 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/graphic_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/graphics.png b/test/qad/graphic/qpicalculator/icons/graphics.png new file mode 100644 index 0000000..8cd2359 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/graphics.png differ diff --git a/test/qad/graphic/qpicalculator/icons/help-contents.png b/test/qad/graphic/qpicalculator/icons/help-contents.png new file mode 100644 index 0000000..b3fbea8 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/help-contents.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_brick_add.png b/test/qad/graphic/qpicalculator/icons/history_brick_add.png new file mode 100644 index 0000000..808b43d Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_brick_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_brick_del.png b/test/qad/graphic/qpicalculator/icons/history_brick_del.png new file mode 100644 index 0000000..da48458 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_brick_del.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_brick_move.png b/test/qad/graphic/qpicalculator/icons/history_brick_move.png new file mode 100644 index 0000000..7d42f91 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_brick_move.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_conn_add.png b/test/qad/graphic/qpicalculator/icons/history_conn_add.png new file mode 100644 index 0000000..1f2839f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_conn_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_conn_del.png b/test/qad/graphic/qpicalculator/icons/history_conn_del.png new file mode 100644 index 0000000..48284dc Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_conn_del.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_conn_edit.png b/test/qad/graphic/qpicalculator/icons/history_conn_edit.png new file mode 100644 index 0000000..74ecd00 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_conn_edit.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_conn_retrace.png b/test/qad/graphic/qpicalculator/icons/history_conn_retrace.png new file mode 100644 index 0000000..427f1ce Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_conn_retrace.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_convert.png b/test/qad/graphic/qpicalculator/icons/history_convert.png new file mode 100644 index 0000000..b84e2b7 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_convert.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_freq.png b/test/qad/graphic/qpicalculator/icons/history_freq.png new file mode 100644 index 0000000..2b68fac Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_freq.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_point.xcf b/test/qad/graphic/qpicalculator/icons/history_point.xcf new file mode 100644 index 0000000..6c08963 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_point.xcf differ diff --git a/test/qad/graphic/qpicalculator/icons/history_point_add.png b/test/qad/graphic/qpicalculator/icons/history_point_add.png new file mode 100644 index 0000000..89f738b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_point_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_point_del.png b/test/qad/graphic/qpicalculator/icons/history_point_del.png new file mode 100644 index 0000000..970d0d7 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_point_del.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_point_move.png b/test/qad/graphic/qpicalculator/icons/history_point_move.png new file mode 100644 index 0000000..9a1f895 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_point_move.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_seg_add.png b/test/qad/graphic/qpicalculator/icons/history_seg_add.png new file mode 100644 index 0000000..2862e21 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_seg_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_seg_del.png b/test/qad/graphic/qpicalculator/icons/history_seg_del.png new file mode 100644 index 0000000..3b8ebaa Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_seg_del.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_seg_move.png b/test/qad/graphic/qpicalculator/icons/history_seg_move.png new file mode 100644 index 0000000..594711e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_seg_move.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_text_add.png b/test/qad/graphic/qpicalculator/icons/history_text_add.png new file mode 100644 index 0000000..be78f1e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_text_add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_text_del.png b/test/qad/graphic/qpicalculator/icons/history_text_del.png new file mode 100644 index 0000000..7f6eb67 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_text_del.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_text_edit.png b/test/qad/graphic/qpicalculator/icons/history_text_edit.png new file mode 100644 index 0000000..b2c973e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_text_edit.png differ diff --git a/test/qad/graphic/qpicalculator/icons/history_text_move.png b/test/qad/graphic/qpicalculator/icons/history_text_move.png new file mode 100644 index 0000000..141876a Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/history_text_move.png differ diff --git a/test/qad/graphic/qpicalculator/icons/initial.png b/test/qad/graphic/qpicalculator/icons/initial.png new file mode 100644 index 0000000..b889071 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/initial.png differ diff --git a/test/qad/graphic/qpicalculator/icons/insert-image.png b/test/qad/graphic/qpicalculator/icons/insert-image.png new file mode 100644 index 0000000..9a1b321 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/insert-image.png differ diff --git a/test/qad/graphic/qpicalculator/icons/insert-text.png b/test/qad/graphic/qpicalculator/icons/insert-text.png new file mode 100644 index 0000000..65d645d Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/insert-text.png differ diff --git a/test/qad/graphic/qpicalculator/icons/layer-visible-off.png b/test/qad/graphic/qpicalculator/icons/layer-visible-off.png new file mode 100644 index 0000000..24277f6 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/layer-visible-off.png differ diff --git a/test/qad/graphic/qpicalculator/icons/layer-visible-on.png b/test/qad/graphic/qpicalculator/icons/layer-visible-on.png new file mode 100644 index 0000000..ea53bd1 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/layer-visible-on.png differ diff --git a/test/qad/graphic/qpicalculator/icons/list-add.png b/test/qad/graphic/qpicalculator/icons/list-add.png new file mode 100644 index 0000000..1e03be9 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/list-add.png differ diff --git a/test/qad/graphic/qpicalculator/icons/mbricks.svg b/test/qad/graphic/qpicalculator/icons/mbricks.svg new file mode 100644 index 0000000..38ed473 --- /dev/null +++ b/test/qad/graphic/qpicalculator/icons/mbricks.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + MB + diff --git a/test/qad/graphic/qpicalculator/icons/mbricks_128.png b/test/qad/graphic/qpicalculator/icons/mbricks_128.png new file mode 100644 index 0000000..7eb729b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/mbricks_128.png differ diff --git a/test/qad/graphic/qpicalculator/icons/mbricks_22.png b/test/qad/graphic/qpicalculator/icons/mbricks_22.png new file mode 100644 index 0000000..cc19f2f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/mbricks_22.png differ diff --git a/test/qad/graphic/qpicalculator/icons/mbricks_256.png b/test/qad/graphic/qpicalculator/icons/mbricks_256.png new file mode 100644 index 0000000..22c4f53 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/mbricks_256.png differ diff --git a/test/qad/graphic/qpicalculator/icons/mbricks_64.png b/test/qad/graphic/qpicalculator/icons/mbricks_64.png new file mode 100644 index 0000000..11fbb75 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/mbricks_64.png differ diff --git a/test/qad/graphic/qpicalculator/icons/media-playback-pause.png b/test/qad/graphic/qpicalculator/icons/media-playback-pause.png new file mode 100644 index 0000000..a9b3113 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/media-playback-pause.png differ diff --git a/test/qad/graphic/qpicalculator/icons/media-playback-start.png b/test/qad/graphic/qpicalculator/icons/media-playback-start.png new file mode 100644 index 0000000..80ff3a1 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/media-playback-start.png differ diff --git a/test/qad/graphic/qpicalculator/icons/media-playback-stop.png b/test/qad/graphic/qpicalculator/icons/media-playback-stop.png new file mode 100644 index 0000000..180280e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/media-playback-stop.png differ diff --git a/test/qad/graphic/qpicalculator/icons/media-skip-forward.png b/test/qad/graphic/qpicalculator/icons/media-skip-forward.png new file mode 100644 index 0000000..0d22924 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/media-skip-forward.png differ diff --git a/test/qad/graphic/qpicalculator/icons/object-flip-horizontal.png b/test/qad/graphic/qpicalculator/icons/object-flip-horizontal.png new file mode 100644 index 0000000..6b428d0 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/object-flip-horizontal.png differ diff --git a/test/qad/graphic/qpicalculator/icons/player-time.png b/test/qad/graphic/qpicalculator/icons/player-time.png new file mode 100644 index 0000000..77ba33c Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/player-time.png differ diff --git a/test/qad/graphic/qpicalculator/icons/preferences-desktop-display.png b/test/qad/graphic/qpicalculator/icons/preferences-desktop-display.png new file mode 100644 index 0000000..bd64701 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/preferences-desktop-display.png differ diff --git a/test/qad/graphic/qpicalculator/icons/preferences-desktop-keyboard.png b/test/qad/graphic/qpicalculator/icons/preferences-desktop-keyboard.png new file mode 100644 index 0000000..d13683f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/preferences-desktop-keyboard.png differ diff --git a/test/qad/graphic/qpicalculator/icons/run-build.png b/test/qad/graphic/qpicalculator/icons/run-build.png new file mode 100644 index 0000000..d5fa88f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/run-build.png differ diff --git a/test/qad/graphic/qpicalculator/icons/tree.png b/test/qad/graphic/qpicalculator/icons/tree.png new file mode 100644 index 0000000..592446a Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/tree.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-center.png b/test/qad/graphic/qpicalculator/icons/view-center.png new file mode 100644 index 0000000..8de1c9b Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-center.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-fullscreen.png b/test/qad/graphic/qpicalculator/icons/view-fullscreen.png new file mode 100644 index 0000000..d41fc7e Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-fullscreen.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-grid.png b/test/qad/graphic/qpicalculator/icons/view-grid.png new file mode 100644 index 0000000..0f1d70c Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-grid.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-mode-compact.png b/test/qad/graphic/qpicalculator/icons/view-mode-compact.png new file mode 100644 index 0000000..d02228c Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-mode-compact.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-mode-expanded.png b/test/qad/graphic/qpicalculator/icons/view-mode-expanded.png new file mode 100644 index 0000000..59a82d2 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-mode-expanded.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-mode-icon.png b/test/qad/graphic/qpicalculator/icons/view-mode-icon.png new file mode 100644 index 0000000..78f9f85 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-mode-icon.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-refresh.png b/test/qad/graphic/qpicalculator/icons/view-refresh.png new file mode 100644 index 0000000..86b6f82 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-refresh.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-restore.png b/test/qad/graphic/qpicalculator/icons/view-restore.png new file mode 100644 index 0000000..7726c35 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-restore.png differ diff --git a/test/qad/graphic/qpicalculator/icons/view-tooltips.png b/test/qad/graphic/qpicalculator/icons/view-tooltips.png new file mode 100644 index 0000000..ca5652c Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/view-tooltips.png differ diff --git a/test/qad/graphic/qpicalculator/icons/window-new.png b/test/qad/graphic/qpicalculator/icons/window-new.png new file mode 100644 index 0000000..4268189 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/window-new.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-fit-best.png b/test/qad/graphic/qpicalculator/icons/zoom-fit-best.png new file mode 100644 index 0000000..07cfc98 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-fit-best.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-in.png b/test/qad/graphic/qpicalculator/icons/zoom-in.png new file mode 100644 index 0000000..6b7a682 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-in.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-original.png b/test/qad/graphic/qpicalculator/icons/zoom-original.png new file mode 100644 index 0000000..c894400 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-original.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-out.png b/test/qad/graphic/qpicalculator/icons/zoom-out.png new file mode 100644 index 0000000..0ee8c6f Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-out.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-range.png b/test/qad/graphic/qpicalculator/icons/zoom-range.png new file mode 100644 index 0000000..c052512 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-range.png differ diff --git a/test/qad/graphic/qpicalculator/icons/zoom-rect.png b/test/qad/graphic/qpicalculator/icons/zoom-rect.png new file mode 100644 index 0000000..da44fd5 Binary files /dev/null and b/test/qad/graphic/qpicalculator/icons/zoom-rect.png differ diff --git a/test/qad/graphic/qpicalculator/main.cpp b/test/qad/graphic/qpicalculator/main.cpp new file mode 100644 index 0000000..afb41cf --- /dev/null +++ b/test/qad/graphic/qpicalculator/main.cpp @@ -0,0 +1,13 @@ +#include +#include "mainwindow.h" + + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/test/qad/graphic/qpicalculator/mainwindow.cpp b/test/qad/graphic/qpicalculator/mainwindow.cpp new file mode 100644 index 0000000..3ebdf57 --- /dev/null +++ b/test/qad/graphic/qpicalculator/mainwindow.cpp @@ -0,0 +1,260 @@ +#include "mainwindow.h" + + +MainWindow::MainWindow(QWidget * parent): QMainWindow(parent), Ui::MainWindow() { + setupUi(this); + active_ = false; + lineInput->setFocus(); +#if QT_VERSION >= 0x050000 + treeGraphics->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + treeGraphics->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); +#else + treeGraphics->header()->setResizeMode(0, QHeaderView::ResizeToContents); + treeGraphics->header()->setResizeMode(1, QHeaderView::ResizeToContents); +#endif + npal = epal = lineInput->palette(); + epal.setColor(lineInput->backgroundRole(), QColor(Qt::red).lighter(150)); + connect(&session, SIGNAL(loading(QPIConfig & )), this, SLOT(loading(QPIConfig & ))); + connect(&session, SIGNAL(saving(QPIConfig & )), this, SLOT(saving(QPIConfig & ))); + session.setFile("session_qpicalc.conf"); + session.addEntry(this); + session.addEntry(lineInput); + session.addEntry(tabWidget); + session.load(); + ans = evaluator.setVariable("ans"); + on_lineInput_returnPressed(); +} + + +MainWindow::~MainWindow() {session.save(); +} + + +void MainWindow::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + + +void MainWindow::updateGraphics() { + graphic->setGraphicsCount(treeGraphics->topLevelItemCount()); + for (int i = 0; i < treeGraphics->topLevelItemCount(); ++i) + graphic->setGraphicColor(treeGraphics->topLevelItem(i)->data(0, Qt::DecorationRole).value(), i); + redrawGraphics(); +} + + +void MainWindow::redrawGraphics() { + QRectF sr = graphic->visualRect(); + double dx = (sr.right() - sr.left()) / graphic->width(), sx = sr.left(), fx = sr.right(), cx; + QPolygonF pol; + evaluator.setVariable("x"); + int vi = evaluator.variableIndex("x"); + graphic->setAutoUpdate(false); + for (int i = 0; i < treeGraphics->topLevelItemCount(); ++i) { + QTreeWidgetItem * ti = treeGraphics->topLevelItem(i); + graphic->setGraphicName(ti->text(1), i); + pol.clear(); + if (ti->checkState(0) == Qt::Checked) { + if (evaluator.check(ti->text(1))) { + cx = sx; + while (cx < fx) { + evaluator.setVariable(vi, complexd(cx, 0.)); + pol << QPointF(cx, evaluator.evaluate().real()); + cx += dx; + } + } + } + graphic->setGraphicVisible(ti->checkState(0) == Qt::Checked, i); + graphic->setGraphicData(pol, i); + } + graphic->setAutoUpdate(true); + graphic->update(true); +} + + +void MainWindow::loading(QPIConfig & conf) { + active_ = false; + QStringList vars = conf.getValue("variables", QStringList()); + int vc = vars.size() / 2; + for (int i = 0; i < vc; ++i) { + QTreeWidgetItem * ti = new QTreeWidgetItem(treeVariables); + ti->setText(0, vars[i * 2]); + ti->setText(1, vars[i * 2 + 1]); + ti->setFlags(ti->flags() | Qt::ItemIsEditable); + treeVariables->addTopLevelItem(ti); + } + buttonVarClear->setEnabled(treeVariables->topLevelItemCount() > 0); + QByteArray ba = QString2QByteArray(conf.getValue("graphics")); QDataStream s(ba); + QVector g; + if (!ba.isEmpty()) s >> g; + graphic->setAllGraphics(g); + for (int i = 0; i < graphic->graphicsCount(); ++i) { + graphic->setCurrentGraphic(i); + QTreeWidgetItem * ti = new QTreeWidgetItem(treeGraphics); + ti->setFlags(ti->flags() | Qt::ItemIsEditable); + ti->setCheckState(0, graphic->graphicVisible() ? Qt::Checked : Qt::Unchecked); + ti->setData(0, Qt::DecorationRole, graphic->graphicColor()); + ti->setText(1, graphic->graphicName()); + treeGraphics->addTopLevelItem(ti); + } + buttonGraphicClear->setEnabled(treeGraphics->topLevelItemCount() > 0); + graphic->setVisualRect(conf.getValue("graphicRect", QRectF(-1., -1., 2., 2.))); + ba = conf.getValue("graphic_state", QByteArray()); + if (!ba.isEmpty()) + graphic->load(ba); + on_tabWidget_currentChanged(0); + redrawGraphics(); + active_ = true; +} + + +void MainWindow::saving(QPIConfig & conf) { + QStringList vars; + int vc = treeVariables->topLevelItemCount(); + for (int i = 0; i < vc; ++i) { + QTreeWidgetItem * ti = treeVariables->topLevelItem(i); + vars << ti->text(0) << ti->text(1); + } + conf.setValue("variables", vars); + + QVector g; + vc = treeGraphics->topLevelItemCount(); + for (int i = 0; i < vc; ++i) { + QTreeWidgetItem * ti = treeGraphics->topLevelItem(i); + vars << QString::number(ti->backgroundColor(1).rgb()) << ti->text(2); + } + QByteArray ba; QDataStream s(&ba, QIODevice::WriteOnly); + s << graphic->allGraphics(); + conf.setValue("graphics", QByteArray2QString(ba)); + conf.setValue("graphicRect", graphic->visualRect()); + conf.setValue("graphic_state", graphic->save()); +} + + +void MainWindow::on_lineInput_textChanged(QString text) { + if (evaluator.check(text)) lineInput->setPalette(npal); + else lineInput->setPalette(epal); + labelParsed->setText(evaluator.expression()); + labelError->setText(evaluator.error()); +} + + +void MainWindow::on_lineInput_returnPressed() { + bool ret = evaluator.check(lineInput->text()); + if (ret) lineInput->setPalette(npal); + else lineInput->setPalette(epal); + labelParsed->setText(evaluator.expression()); + labelError->setText(evaluator.error()); + if (!ret) return; + complexd val = evaluator.evaluate(); + evaluator.setVariable(ans, val); + if (val.imag() == 0) labelResult->setText(QString::number(val.real())); + else { + if (val.real() == 0) labelResult->setText(QString::number(val.imag()) + "i"); + else { + if (val.imag() > 0) labelResult->setText(QString::number(val.real()) + + " + " + QString::number(val.imag()) + "i"); + else labelResult->setText(QString::number(val.real()) + + " - " + QString::number(fabs(val.imag())) + "i"); + } + } + if (lineInput->text().trimmed().isEmpty()) return; + QTreeWidgetItem * ti = 0, * pti = 0; + if (treeHistory->topLevelItemCount() > 0) + pti = treeHistory->topLevelItem(treeHistory->topLevelItemCount() - 1); + if (pti != 0) + if (pti->text(0) == lineInput->text()) + return; + ti = new QTreeWidgetItem(treeHistory); + ti->setText(0, lineInput->text()); + ti->setText(1, labelResult->text()); + treeHistory->addTopLevelItem(ti); + if (treeHistory->verticalScrollBar()->value() == treeHistory->verticalScrollBar()->maximum()) + treeHistory->scrollToBottom(); +} + + +void MainWindow::on_treeGraphics_itemDoubleClicked(QTreeWidgetItem * item, int column) { + Qt::ItemFlags f = item->flags(); + if (column != 1) f &= ~Qt::ItemIsEditable; + else f |= Qt::ItemIsEditable; + item->setFlags(f); + if (column != 0) return; + QColor col = QColorDialog::getColor(item->data(0, Qt::DecorationRole).value(), this, "Select color for graphic", QColorDialog::ShowAlphaChannel); + if (!col.isValid()) return; + item->setData(0, Qt::DecorationRole, col); + updateGraphics(); +} + + +void MainWindow::on_buttonVarAdd_clicked() { + QTreeWidgetItem * ti = new QTreeWidgetItem(treeVariables); + ti->setSelected(true); + ti->setFlags(ti->flags() | Qt::ItemIsEditable); + treeVariables->addTopLevelItem(ti); + treeVariables->setFocus(); + treeVariables->editItem(ti); + buttonVarClear->setEnabled(treeVariables->topLevelItemCount() > 0); +} + + +void MainWindow::on_buttonVarDel_clicked() { + QList si = treeVariables->selectedItems(); + foreach (QTreeWidgetItem * i, si) + delete i; + buttonVarClear->setEnabled(treeVariables->topLevelItemCount() > 0); +} + + +void MainWindow::on_buttonGraphicAdd_clicked() { + graphic->setGraphicsCount(graphic->graphicsCount() + 1); + graphic->setCurrentGraphic(graphic->graphicsCount() - 1); + QTreeWidgetItem * ti = new QTreeWidgetItem(treeGraphics); + ti->setSelected(true); + ti->setFlags(ti->flags() | Qt::ItemIsEditable); + ti->setCheckState(0, Qt::Checked); + ti->setData(0, Qt::DecorationRole, graphic->graphicColor()); + treeGraphics->addTopLevelItem(ti); + treeGraphics->setFocus(); + treeGraphics->editItem(ti, 1); + buttonGraphicClear->setEnabled(treeGraphics->topLevelItemCount() > 0); + updateGraphics(); +} + + +void MainWindow::on_buttonGraphicDel_clicked() { + QList si = treeGraphics->selectedItems(); + foreach (QTreeWidgetItem * i, si) + delete i; + buttonGraphicClear->setEnabled(treeGraphics->topLevelItemCount() > 0); + updateGraphics(); +} + + +void MainWindow::on_buttonGraphicClear_clicked() { + treeGraphics->clear(); + buttonGraphicClear->setEnabled(false); + updateGraphics(); +} + + +void MainWindow::on_tabWidget_currentChanged(int index) { + QPIEvaluator eval; + evaluator.clearCustomVariables(); + for (int i = 0; i < treeVariables->topLevelItemCount(); ++i) { + QString vn, vv; + vn = treeVariables->topLevelItem(i)->text(0); + vv = treeVariables->topLevelItem(i)->text(1); + eval.check(vv); + evaluator.setVariable(vn, eval.evaluate()); + } + if (index == 0) on_lineInput_returnPressed(); + if (index == 2) redrawGraphics(); +} diff --git a/test/qad/graphic/qpicalculator/mainwindow.h b/test/qad/graphic/qpicalculator/mainwindow.h new file mode 100644 index 0000000..efb44b9 --- /dev/null +++ b/test/qad/graphic/qpicalculator/mainwindow.h @@ -0,0 +1,56 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include "ui_mainwindow.h" +#include "qpievaluator.h" +#include "session_manager.h" + + +class MainWindow: public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget * parent = 0); + ~MainWindow(); + +protected: + void changeEvent(QEvent * e); + +private: + void updateGraphics(); + void redrawGraphics(); + + QPIEvaluator evaluator; + QPalette npal, epal; + SessionManager session; + int ans; + bool active_; + +private slots: + void loading(QPIConfig & conf); + void saving(QPIConfig & conf); + void on_lineInput_textChanged(QString s); + void on_lineInput_returnPressed(); + void on_treeHistory_itemDoubleClicked(QTreeWidgetItem * item, int column) {lineInput->setText(item->text(0));} + void on_treeVariables_itemSelectionChanged() {buttonVarDel->setDisabled(treeVariables->selectedItems().isEmpty());} + void on_treeGraphics_itemSelectionChanged() {buttonGraphicDel->setDisabled(treeGraphics->selectedItems().isEmpty());} + void on_treeGraphics_itemChanged(QTreeWidgetItem * , int col) { if (active_) redrawGraphics();} + void on_treeGraphics_itemDoubleClicked(QTreeWidgetItem * item, int column); + void on_buttonVarAdd_clicked(); + void on_buttonVarDel_clicked(); + void on_buttonVarClear_clicked() {treeVariables->clear(); buttonVarClear->setEnabled(false);} + void on_buttonGraphicAdd_clicked(); + void on_buttonGraphicDel_clicked(); + void on_buttonGraphicClear_clicked(); + void on_tabWidget_currentChanged(int index); + void on_graphic_visualRectChanged() {redrawGraphics();} + +}; + +#endif // MAINWINDOW_H diff --git a/test/qad/graphic/qpicalculator/mainwindow.ui b/test/qad/graphic/qpicalculator/mainwindow.ui new file mode 100644 index 0000000..234fed7 --- /dev/null +++ b/test/qad/graphic/qpicalculator/mainwindow.ui @@ -0,0 +1,379 @@ + + + MainWindow + + + + 0 + 0 + 780 + 521 + + + + QPICalculator + + + + :/icons/Apps-accessories-calculator-icon.png:/icons/Apps-accessories-calculator-icon.png + + + + + + + 2 + + + + Calculator + + + + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + true + + + 300 + + + + Expression + + + + + Result + + + + + + + + + + + Correct + + + + + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + 14 + 75 + true + + + + 0 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + Variables + + + + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + true + + + 200 + + + + Name + + + + + Value + + + + + + + + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + false + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + Del + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + false + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Graphics + + + + + + Qt::Horizontal + + + + + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + true + + + false + + + 50 + + + + On + + + + + Function + + + + + + + + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + false + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + Del + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + false + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Graphic::BorderInputs|Graphic::Configure|Graphic::CursorAxis|Graphic::Fullscreen|Graphic::Grid|Graphic::Save + + + true + + + false + + + false + + + + + + + + + + + + + + + Graphic + QFrame +
graphic.h
+
+ + CLineEdit + QLineEdit +
clineedit.h
+
+
+ + + + + +
diff --git a/test/qad/graphic/qpicalculator/qpicalculator.qrc b/test/qad/graphic/qpicalculator/qpicalculator.qrc new file mode 100644 index 0000000..823218d --- /dev/null +++ b/test/qad/graphic/qpicalculator/qpicalculator.qrc @@ -0,0 +1,9 @@ + + + icons/edit-clear.png + icons/edit-delete.png + icons/list-add.png + icons/application-exit.png + icons/Apps-accessories-calculator-icon.png + + diff --git a/test/qad/graphic/uglwidget.h b/test/qad/graphic/uglwidget.h new file mode 100644 index 0000000..859070b --- /dev/null +++ b/test/qad/graphic/uglwidget.h @@ -0,0 +1,68 @@ +#ifndef UGLWIDGET_H +#define UGLWIDGET_H + +#include +#include +#if QT_VERSION >= 0x050400 +# include +typedef QOpenGLWidget __GLWidget__; +#else +# include +typedef QGLWidget __GLWidget__; +# ifndef GL_MULTISAMPLE +# define GL_MULTISAMPLE 0x809D +# endif +#endif + + +class UGLWidget: public __GLWidget__ +{ + Q_OBJECT +public: +#if QT_VERSION >= 0x050400 + UGLWidget(QWidget * parent = 0): __GLWidget__(parent) {QSurfaceFormat sf = format(); sf.setSamples(8); setFormat(sf);} +#else + UGLWidget(QWidget * parent = 0): __GLWidget__(QGLFormat(QGL::DoubleBuffer | QGL::AlphaChannel | QGL::DirectRendering | QGL::SampleBuffers), parent) {} +#endif + //UGLWidget(QGLContext * context, QWidget * parent = 0): __GLWidget__(context, parent) {} + +#if QT_VERSION >= 0x050400 + QImage grabFrameBuffer() {return grabFramebuffer();} +#endif + +protected: +#if QT_VERSION >= 0x050400 + virtual void paintGL() {emit paintSignal();} +#else + virtual void paintEvent(QPaintEvent * ) {emit paintSignal();} +#endif + virtual void resizeEvent(QResizeEvent * e) { + __GLWidget__::resizeEvent(e); + emit resizeSignal(); + } + + +signals: + void closeEvent(QCloseEvent * e); + void dragEnterEvent(QDragEnterEvent * e); + void dragLeaveEvent(QDragLeaveEvent * e); + void dragMoveEvent(QDragMoveEvent * e); + void dropEvent(QDropEvent * e); + void enterEvent(QEvent * e); + void hideEvent(QHideEvent * e); + void keyPressEvent(QKeyEvent * e); + void keyReleaseEvent(QKeyEvent * e); + void leaveEvent(QEvent * e); + void mouseDoubleClickEvent(QMouseEvent * e); + void mouseMoveEvent(QMouseEvent * e); + void mousePressEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent * e); + void moveEvent(QMoveEvent * e); + void showEvent(QShowEvent * e); + void wheelEvent(QWheelEvent * e); + void resizeSignal(); + void paintSignal(); + +}; + +#endif diff --git a/test/qad/graphic/uwidget.h b/test/qad/graphic/uwidget.h new file mode 100644 index 0000000..e7f4151 --- /dev/null +++ b/test/qad/graphic/uwidget.h @@ -0,0 +1,52 @@ +#ifndef UWIDGET_H +#define UWIDGET_H + +#include +#include +#include +#include +#include + + +class UWidget: public QWidget +{ + Q_OBJECT +public: + UWidget(QWidget * parent = 0): QWidget(parent) {} + +private: + virtual bool event(QEvent * e) { + if (e->type() != QEvent::Paint) return QWidget::event(e); + e->accept(); + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + emit paintEvent((QPaintEvent * )e); + return true; + } + +signals: + void closeEvent(QCloseEvent * e); + void dragEnterEvent(QDragEnterEvent * e); + void dragLeaveEvent(QDragLeaveEvent * e); + void dragMoveEvent(QDragMoveEvent * e); + void dropEvent(QDropEvent * e); + void enterEvent(QEvent * e); + void hideEvent(QHideEvent * e); + void keyPressEvent(QKeyEvent * e); + void keyReleaseEvent(QKeyEvent * e); + void leaveEvent(QEvent * e); + void mouseDoubleClickEvent(QMouseEvent * e); + void mouseMoveEvent(QMouseEvent * e); + void mousePressEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent * e); + void moveEvent(QMoveEvent * e); + void resizeEvent(QResizeEvent * e); + void showEvent(QShowEvent * e); + void wheelEvent(QWheelEvent * e); + void paintEvent(QPaintEvent * e); + +}; + +#endif diff --git a/test/qad/icons/align-bottom-center.png b/test/qad/icons/align-bottom-center.png new file mode 100644 index 0000000..8678b5d Binary files /dev/null and b/test/qad/icons/align-bottom-center.png differ diff --git a/test/qad/icons/align-bottom-left.png b/test/qad/icons/align-bottom-left.png new file mode 100644 index 0000000..b25edc0 Binary files /dev/null and b/test/qad/icons/align-bottom-left.png differ diff --git a/test/qad/icons/align-bottom-right.png b/test/qad/icons/align-bottom-right.png new file mode 100644 index 0000000..dea560e Binary files /dev/null and b/test/qad/icons/align-bottom-right.png differ diff --git a/test/qad/icons/align-center-center.png b/test/qad/icons/align-center-center.png new file mode 100644 index 0000000..0054ee3 Binary files /dev/null and b/test/qad/icons/align-center-center.png differ diff --git a/test/qad/icons/align-center-left.png b/test/qad/icons/align-center-left.png new file mode 100644 index 0000000..ccc2e95 Binary files /dev/null and b/test/qad/icons/align-center-left.png differ diff --git a/test/qad/icons/align-center-right.png b/test/qad/icons/align-center-right.png new file mode 100644 index 0000000..0e9335c Binary files /dev/null and b/test/qad/icons/align-center-right.png differ diff --git a/test/qad/icons/align-hor.png b/test/qad/icons/align-hor.png new file mode 100644 index 0000000..a477a99 Binary files /dev/null and b/test/qad/icons/align-hor.png differ diff --git a/test/qad/icons/align-top-center.png b/test/qad/icons/align-top-center.png new file mode 100644 index 0000000..5dc912f Binary files /dev/null and b/test/qad/icons/align-top-center.png differ diff --git a/test/qad/icons/align-top-left.png b/test/qad/icons/align-top-left.png new file mode 100644 index 0000000..38b22a1 Binary files /dev/null and b/test/qad/icons/align-top-left.png differ diff --git a/test/qad/icons/align-top-right.png b/test/qad/icons/align-top-right.png new file mode 100644 index 0000000..2232cac Binary files /dev/null and b/test/qad/icons/align-top-right.png differ diff --git a/test/qad/icons/align-ver.png b/test/qad/icons/align-ver.png new file mode 100644 index 0000000..75ad0c1 Binary files /dev/null and b/test/qad/icons/align-ver.png differ diff --git a/test/qad/icons/alpha.png b/test/qad/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/test/qad/icons/alpha.png differ diff --git a/test/qad/icons/application-exit.png b/test/qad/icons/application-exit.png new file mode 100644 index 0000000..8adbcba Binary files /dev/null and b/test/qad/icons/application-exit.png differ diff --git a/test/qad/icons/archive-extract.png b/test/qad/icons/archive-extract.png new file mode 100644 index 0000000..0e64c87 Binary files /dev/null and b/test/qad/icons/archive-extract.png differ diff --git a/test/qad/icons/archive-insert-directory.png b/test/qad/icons/archive-insert-directory.png new file mode 100644 index 0000000..e26f51c Binary files /dev/null and b/test/qad/icons/archive-insert-directory.png differ diff --git a/test/qad/icons/archive-insert.png b/test/qad/icons/archive-insert.png new file mode 100644 index 0000000..4143a38 Binary files /dev/null and b/test/qad/icons/archive-insert.png differ diff --git a/test/qad/icons/arrow-down.png b/test/qad/icons/arrow-down.png new file mode 100644 index 0000000..61f2f1d Binary files /dev/null and b/test/qad/icons/arrow-down.png differ diff --git a/test/qad/icons/arrow-up.png b/test/qad/icons/arrow-up.png new file mode 100644 index 0000000..2b1453b Binary files /dev/null and b/test/qad/icons/arrow-up.png differ diff --git a/test/qad/icons/axis_x.png b/test/qad/icons/axis_x.png new file mode 100644 index 0000000..321899c Binary files /dev/null and b/test/qad/icons/axis_x.png differ diff --git a/test/qad/icons/axis_y.png b/test/qad/icons/axis_y.png new file mode 100644 index 0000000..7d4d9ec Binary files /dev/null and b/test/qad/icons/axis_y.png differ diff --git a/test/qad/icons/blockview.png b/test/qad/icons/blockview.png new file mode 100644 index 0000000..6e25090 Binary files /dev/null and b/test/qad/icons/blockview.png differ diff --git a/test/qad/icons/blockview.xcf b/test/qad/icons/blockview.xcf new file mode 100644 index 0000000..54eb897 Binary files /dev/null and b/test/qad/icons/blockview.xcf differ diff --git a/test/qad/icons/border-line.png b/test/qad/icons/border-line.png new file mode 100644 index 0000000..4b47837 Binary files /dev/null and b/test/qad/icons/border-line.png differ diff --git a/test/qad/icons/button.png b/test/qad/icons/button.png new file mode 100644 index 0000000..266fb0f Binary files /dev/null and b/test/qad/icons/button.png differ diff --git a/test/qad/icons/case-sensitive.png b/test/qad/icons/case-sensitive.png new file mode 100644 index 0000000..39c5c5a Binary files /dev/null and b/test/qad/icons/case-sensitive.png differ diff --git a/test/qad/icons/chardialog.png b/test/qad/icons/chardialog.png new file mode 100644 index 0000000..5c39db0 Binary files /dev/null and b/test/qad/icons/chardialog.png differ diff --git a/test/qad/icons/checkbox-unchecked.png b/test/qad/icons/checkbox-unchecked.png new file mode 100644 index 0000000..01a7307 Binary files /dev/null and b/test/qad/icons/checkbox-unchecked.png differ diff --git a/test/qad/icons/checkbox.png b/test/qad/icons/checkbox.png new file mode 100644 index 0000000..1c311e2 Binary files /dev/null and b/test/qad/icons/checkbox.png differ diff --git a/test/qad/icons/clear-history.png b/test/qad/icons/clear-history.png new file mode 100644 index 0000000..805631f Binary files /dev/null and b/test/qad/icons/clear-history.png differ diff --git a/test/qad/icons/clineedit.png b/test/qad/icons/clineedit.png new file mode 100644 index 0000000..b2d95ee Binary files /dev/null and b/test/qad/icons/clineedit.png differ diff --git a/test/qad/icons/code-error.png b/test/qad/icons/code-error.png new file mode 100644 index 0000000..a76314c Binary files /dev/null and b/test/qad/icons/code-error.png differ diff --git a/test/qad/icons/code-parents.png b/test/qad/icons/code-parents.png new file mode 100644 index 0000000..a4247e1 Binary files /dev/null and b/test/qad/icons/code-parents.png differ diff --git a/test/qad/icons/code-struct.png b/test/qad/icons/code-struct.png new file mode 100644 index 0000000..425b7b9 Binary files /dev/null and b/test/qad/icons/code-struct.png differ diff --git a/test/qad/icons/code-union.png b/test/qad/icons/code-union.png new file mode 100644 index 0000000..337f0de Binary files /dev/null and b/test/qad/icons/code-union.png differ diff --git a/test/qad/icons/code-variable.png b/test/qad/icons/code-variable.png new file mode 100644 index 0000000..3c768e6 Binary files /dev/null and b/test/qad/icons/code-variable.png differ diff --git a/test/qad/icons/code-word.png b/test/qad/icons/code-word.png new file mode 100644 index 0000000..3f6725a Binary files /dev/null and b/test/qad/icons/code-word.png differ diff --git a/test/qad/icons/colorbutton.png b/test/qad/icons/colorbutton.png new file mode 100644 index 0000000..7df718d Binary files /dev/null and b/test/qad/icons/colorbutton.png differ diff --git a/test/qad/icons/configure.png b/test/qad/icons/configure.png new file mode 100644 index 0000000..2a81957 Binary files /dev/null and b/test/qad/icons/configure.png differ diff --git a/test/qad/icons/configure_16.png b/test/qad/icons/configure_16.png new file mode 100644 index 0000000..5ce478b Binary files /dev/null and b/test/qad/icons/configure_16.png differ diff --git a/test/qad/icons/database.png b/test/qad/icons/database.png new file mode 100644 index 0000000..9c773d5 Binary files /dev/null and b/test/qad/icons/database.png differ diff --git a/test/qad/icons/dialog-cancel.png b/test/qad/icons/dialog-cancel.png new file mode 100644 index 0000000..ace88ab Binary files /dev/null and b/test/qad/icons/dialog-cancel.png differ diff --git a/test/qad/icons/dialog-close.png b/test/qad/icons/dialog-close.png new file mode 100644 index 0000000..6072634 Binary files /dev/null and b/test/qad/icons/dialog-close.png differ diff --git a/test/qad/icons/dialog-ok-apply.png b/test/qad/icons/dialog-ok-apply.png new file mode 100644 index 0000000..3260f60 Binary files /dev/null and b/test/qad/icons/dialog-ok-apply.png differ diff --git a/test/qad/icons/dialog-warning.png b/test/qad/icons/dialog-warning.png new file mode 100644 index 0000000..c62afad Binary files /dev/null and b/test/qad/icons/dialog-warning.png differ diff --git a/test/qad/icons/document-close.png b/test/qad/icons/document-close.png new file mode 100644 index 0000000..0ea563e Binary files /dev/null and b/test/qad/icons/document-close.png differ diff --git a/test/qad/icons/document-edit.png b/test/qad/icons/document-edit.png new file mode 100644 index 0000000..1ecb10c Binary files /dev/null and b/test/qad/icons/document-edit.png differ diff --git a/test/qad/icons/document-new.png b/test/qad/icons/document-new.png new file mode 100644 index 0000000..e4d8b47 Binary files /dev/null and b/test/qad/icons/document-new.png differ diff --git a/test/qad/icons/document-open-recent.png b/test/qad/icons/document-open-recent.png new file mode 100644 index 0000000..5dea408 Binary files /dev/null and b/test/qad/icons/document-open-recent.png differ diff --git a/test/qad/icons/document-open.png b/test/qad/icons/document-open.png new file mode 100644 index 0000000..63380e4 Binary files /dev/null and b/test/qad/icons/document-open.png differ diff --git a/test/qad/icons/document-open_16.png b/test/qad/icons/document-open_16.png new file mode 100644 index 0000000..17076a3 Binary files /dev/null and b/test/qad/icons/document-open_16.png differ diff --git a/test/qad/icons/document-print.png b/test/qad/icons/document-print.png new file mode 100644 index 0000000..6c5e351 Binary files /dev/null and b/test/qad/icons/document-print.png differ diff --git a/test/qad/icons/document-save-all.png b/test/qad/icons/document-save-all.png new file mode 100644 index 0000000..73405f7 Binary files /dev/null and b/test/qad/icons/document-save-all.png differ diff --git a/test/qad/icons/document-save-as.png b/test/qad/icons/document-save-as.png new file mode 100644 index 0000000..b5da838 Binary files /dev/null and b/test/qad/icons/document-save-as.png differ diff --git a/test/qad/icons/document-save.png b/test/qad/icons/document-save.png new file mode 100644 index 0000000..aee3e22 Binary files /dev/null and b/test/qad/icons/document-save.png differ diff --git a/test/qad/icons/draw-ellipse.png b/test/qad/icons/draw-ellipse.png new file mode 100644 index 0000000..a3e67ac Binary files /dev/null and b/test/qad/icons/draw-ellipse.png differ diff --git a/test/qad/icons/draw-line.png b/test/qad/icons/draw-line.png new file mode 100644 index 0000000..a521e62 Binary files /dev/null and b/test/qad/icons/draw-line.png differ diff --git a/test/qad/icons/draw-rectangle.png b/test/qad/icons/draw-rectangle.png new file mode 100644 index 0000000..77a682f Binary files /dev/null and b/test/qad/icons/draw-rectangle.png differ diff --git a/test/qad/icons/draw-text.png b/test/qad/icons/draw-text.png new file mode 100644 index 0000000..c8efb7d Binary files /dev/null and b/test/qad/icons/draw-text.png differ diff --git a/test/qad/icons/ecombobox.png b/test/qad/icons/ecombobox.png new file mode 100644 index 0000000..f0a2d17 Binary files /dev/null and b/test/qad/icons/ecombobox.png differ diff --git a/test/qad/icons/edit-clear-locationbar-rtl.png b/test/qad/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..0e6d645 Binary files /dev/null and b/test/qad/icons/edit-clear-locationbar-rtl.png differ diff --git a/test/qad/icons/edit-clear.png b/test/qad/icons/edit-clear.png new file mode 100644 index 0000000..cae930b Binary files /dev/null and b/test/qad/icons/edit-clear.png differ diff --git a/test/qad/icons/edit-copy.png b/test/qad/icons/edit-copy.png new file mode 100644 index 0000000..a8178ca Binary files /dev/null and b/test/qad/icons/edit-copy.png differ diff --git a/test/qad/icons/edit-delete.png b/test/qad/icons/edit-delete.png new file mode 100644 index 0000000..38f11cd Binary files /dev/null and b/test/qad/icons/edit-delete.png differ diff --git a/test/qad/icons/edit-find.png b/test/qad/icons/edit-find.png new file mode 100644 index 0000000..140e581 Binary files /dev/null and b/test/qad/icons/edit-find.png differ diff --git a/test/qad/icons/edit-find_16.png b/test/qad/icons/edit-find_16.png new file mode 100644 index 0000000..9a462c0 Binary files /dev/null and b/test/qad/icons/edit-find_16.png differ diff --git a/test/qad/icons/edit-guides.png b/test/qad/icons/edit-guides.png new file mode 100644 index 0000000..0065b28 Binary files /dev/null and b/test/qad/icons/edit-guides.png differ diff --git a/test/qad/icons/edit-paste.png b/test/qad/icons/edit-paste.png new file mode 100644 index 0000000..a9aac10 Binary files /dev/null and b/test/qad/icons/edit-paste.png differ diff --git a/test/qad/icons/edit-redo.png b/test/qad/icons/edit-redo.png new file mode 100644 index 0000000..353a6c0 Binary files /dev/null and b/test/qad/icons/edit-redo.png differ diff --git a/test/qad/icons/edit-table-insert-row-below.png b/test/qad/icons/edit-table-insert-row-below.png new file mode 100644 index 0000000..c3695c7 Binary files /dev/null and b/test/qad/icons/edit-table-insert-row-below.png differ diff --git a/test/qad/icons/edit-undo.png b/test/qad/icons/edit-undo.png new file mode 100644 index 0000000..a677409 Binary files /dev/null and b/test/qad/icons/edit-undo.png differ diff --git a/test/qad/icons/edockwidget.png b/test/qad/icons/edockwidget.png new file mode 100644 index 0000000..9eee04f Binary files /dev/null and b/test/qad/icons/edockwidget.png differ diff --git a/test/qad/icons/etabwidget.png b/test/qad/icons/etabwidget.png new file mode 100644 index 0000000..4fe88c6 Binary files /dev/null and b/test/qad/icons/etabwidget.png differ diff --git a/test/qad/icons/evalspinbox.png b/test/qad/icons/evalspinbox.png new file mode 100644 index 0000000..5c80df1 Binary files /dev/null and b/test/qad/icons/evalspinbox.png differ diff --git a/test/qad/icons/expand_s_x.png b/test/qad/icons/expand_s_x.png new file mode 100644 index 0000000..185dcd9 Binary files /dev/null and b/test/qad/icons/expand_s_x.png differ diff --git a/test/qad/icons/expand_s_y.png b/test/qad/icons/expand_s_y.png new file mode 100644 index 0000000..9707071 Binary files /dev/null and b/test/qad/icons/expand_s_y.png differ diff --git a/test/qad/icons/expand_x.png b/test/qad/icons/expand_x.png new file mode 100644 index 0000000..0089290 Binary files /dev/null and b/test/qad/icons/expand_x.png differ diff --git a/test/qad/icons/expand_y.png b/test/qad/icons/expand_y.png new file mode 100644 index 0000000..0e01625 Binary files /dev/null and b/test/qad/icons/expand_y.png differ diff --git a/test/qad/icons/f1.png b/test/qad/icons/f1.png new file mode 100644 index 0000000..fc3aeae Binary files /dev/null and b/test/qad/icons/f1.png differ diff --git a/test/qad/icons/format-fill-color.png b/test/qad/icons/format-fill-color.png new file mode 100644 index 0000000..99412e9 Binary files /dev/null and b/test/qad/icons/format-fill-color.png differ diff --git a/test/qad/icons/format-stroke-color.png b/test/qad/icons/format-stroke-color.png new file mode 100644 index 0000000..4c4d290 Binary files /dev/null and b/test/qad/icons/format-stroke-color.png differ diff --git a/test/qad/icons/format-text-subscript.png b/test/qad/icons/format-text-subscript.png new file mode 100644 index 0000000..206b147 Binary files /dev/null and b/test/qad/icons/format-text-subscript.png differ diff --git a/test/qad/icons/games-solve.png b/test/qad/icons/games-solve.png new file mode 100644 index 0000000..2177b4d Binary files /dev/null and b/test/qad/icons/games-solve.png differ diff --git a/test/qad/icons/go-next.png b/test/qad/icons/go-next.png new file mode 100644 index 0000000..cacea05 Binary files /dev/null and b/test/qad/icons/go-next.png differ diff --git a/test/qad/icons/go-previous.png b/test/qad/icons/go-previous.png new file mode 100644 index 0000000..b4b00f5 Binary files /dev/null and b/test/qad/icons/go-previous.png differ diff --git a/test/qad/icons/graphic.png b/test/qad/icons/graphic.png new file mode 100644 index 0000000..677fa54 Binary files /dev/null and b/test/qad/icons/graphic.png differ diff --git a/test/qad/icons/graphics.png b/test/qad/icons/graphics.png new file mode 100644 index 0000000..64abcb5 Binary files /dev/null and b/test/qad/icons/graphics.png differ diff --git a/test/qad/icons/historyview.png b/test/qad/icons/historyview.png new file mode 100644 index 0000000..cf3ede9 Binary files /dev/null and b/test/qad/icons/historyview.png differ diff --git a/test/qad/icons/icons.xcf b/test/qad/icons/icons.xcf new file mode 100644 index 0000000..d4c9a8b Binary files /dev/null and b/test/qad/icons/icons.xcf differ diff --git a/test/qad/icons/item-add.png b/test/qad/icons/item-add.png new file mode 100644 index 0000000..8a422c7 Binary files /dev/null and b/test/qad/icons/item-add.png differ diff --git a/test/qad/icons/item.png b/test/qad/icons/item.png new file mode 100644 index 0000000..50bfddb Binary files /dev/null and b/test/qad/icons/item.png differ diff --git a/test/qad/icons/lastmoves.png b/test/qad/icons/lastmoves.png new file mode 100644 index 0000000..4454bca Binary files /dev/null and b/test/qad/icons/lastmoves.png differ diff --git a/test/qad/icons/layer-visible-off.png b/test/qad/icons/layer-visible-off.png new file mode 100644 index 0000000..03ca47d Binary files /dev/null and b/test/qad/icons/layer-visible-off.png differ diff --git a/test/qad/icons/layer-visible-on.png b/test/qad/icons/layer-visible-on.png new file mode 100644 index 0000000..11fad5e Binary files /dev/null and b/test/qad/icons/layer-visible-on.png differ diff --git a/test/qad/icons/led_3.png b/test/qad/icons/led_3.png new file mode 100644 index 0000000..a2c73ab Binary files /dev/null and b/test/qad/icons/led_3.png differ diff --git a/test/qad/icons/led_gray.png b/test/qad/icons/led_gray.png new file mode 100644 index 0000000..264e6a1 Binary files /dev/null and b/test/qad/icons/led_gray.png differ diff --git a/test/qad/icons/led_off.png b/test/qad/icons/led_off.png new file mode 100644 index 0000000..e056895 Binary files /dev/null and b/test/qad/icons/led_off.png differ diff --git a/test/qad/icons/led_on.png b/test/qad/icons/led_on.png new file mode 100644 index 0000000..fe2b87e Binary files /dev/null and b/test/qad/icons/led_on.png differ diff --git a/test/qad/icons/legend.png b/test/qad/icons/legend.png new file mode 100644 index 0000000..94447c2 Binary files /dev/null and b/test/qad/icons/legend.png differ diff --git a/test/qad/icons/list-add.png b/test/qad/icons/list-add.png new file mode 100644 index 0000000..a15dd10 Binary files /dev/null and b/test/qad/icons/list-add.png differ diff --git a/test/qad/icons/list-remove.png b/test/qad/icons/list-remove.png new file mode 100644 index 0000000..fffb248 Binary files /dev/null and b/test/qad/icons/list-remove.png differ diff --git a/test/qad/icons/log_parser.png b/test/qad/icons/log_parser.png new file mode 100644 index 0000000..cf0ca98 Binary files /dev/null and b/test/qad/icons/log_parser.png differ diff --git a/test/qad/icons/mail.png b/test/qad/icons/mail.png new file mode 100644 index 0000000..81adbf9 Binary files /dev/null and b/test/qad/icons/mail.png differ diff --git a/test/qad/icons/media-playback-pause.png b/test/qad/icons/media-playback-pause.png new file mode 100644 index 0000000..db3bd3b Binary files /dev/null and b/test/qad/icons/media-playback-pause.png differ diff --git a/test/qad/icons/media-playback-start.png b/test/qad/icons/media-playback-start.png new file mode 100644 index 0000000..c53ff0f Binary files /dev/null and b/test/qad/icons/media-playback-start.png differ diff --git a/test/qad/icons/media-playback-stop.png b/test/qad/icons/media-playback-stop.png new file mode 100644 index 0000000..fef42cc Binary files /dev/null and b/test/qad/icons/media-playback-stop.png differ diff --git a/test/qad/icons/media-record.png b/test/qad/icons/media-record.png new file mode 100644 index 0000000..7adb749 Binary files /dev/null and b/test/qad/icons/media-record.png differ diff --git a/test/qad/icons/media-skip-forward.png b/test/qad/icons/media-skip-forward.png new file mode 100644 index 0000000..3b25f45 Binary files /dev/null and b/test/qad/icons/media-skip-forward.png differ diff --git a/test/qad/icons/node-add.png b/test/qad/icons/node-add.png new file mode 100644 index 0000000..80b5c61 Binary files /dev/null and b/test/qad/icons/node-add.png differ diff --git a/test/qad/icons/node.png b/test/qad/icons/node.png new file mode 100644 index 0000000..fc8194e Binary files /dev/null and b/test/qad/icons/node.png differ diff --git a/test/qad/icons/pause-back.png b/test/qad/icons/pause-back.png new file mode 100644 index 0000000..d3407e2 Binary files /dev/null and b/test/qad/icons/pause-back.png differ diff --git a/test/qad/icons/pause-front.png b/test/qad/icons/pause-front.png new file mode 100644 index 0000000..448bb6a Binary files /dev/null and b/test/qad/icons/pause-front.png differ diff --git a/test/qad/icons/qcodeedit.png b/test/qad/icons/qcodeedit.png new file mode 100644 index 0000000..4299440 Binary files /dev/null and b/test/qad/icons/qcodeedit.png differ diff --git a/test/qad/icons/qpiconsole.png b/test/qad/icons/qpiconsole.png new file mode 100644 index 0000000..9555ab8 Binary files /dev/null and b/test/qad/icons/qpiconsole.png differ diff --git a/test/qad/icons/quickopen.png b/test/qad/icons/quickopen.png new file mode 100644 index 0000000..d758fff Binary files /dev/null and b/test/qad/icons/quickopen.png differ diff --git a/test/qad/icons/qvariantedit.png b/test/qad/icons/qvariantedit.png new file mode 100644 index 0000000..9376a9f Binary files /dev/null and b/test/qad/icons/qvariantedit.png differ diff --git a/test/qad/icons/spinslider.png b/test/qad/icons/spinslider.png new file mode 100644 index 0000000..3a5e639 Binary files /dev/null and b/test/qad/icons/spinslider.png differ diff --git a/test/qad/icons/sql_table.png b/test/qad/icons/sql_table.png new file mode 100644 index 0000000..9da26eb Binary files /dev/null and b/test/qad/icons/sql_table.png differ diff --git a/test/qad/icons/tools-wizard.png b/test/qad/icons/tools-wizard.png new file mode 100644 index 0000000..0101c36 Binary files /dev/null and b/test/qad/icons/tools-wizard.png differ diff --git a/test/qad/icons/touchbuttframe.png b/test/qad/icons/touchbuttframe.png new file mode 100644 index 0000000..ecb3f66 Binary files /dev/null and b/test/qad/icons/touchbuttframe.png differ diff --git a/test/qad/icons/touchbutton.png b/test/qad/icons/touchbutton.png new file mode 100644 index 0000000..a277c42 Binary files /dev/null and b/test/qad/icons/touchbutton.png differ diff --git a/test/qad/icons/touchslider.png b/test/qad/icons/touchslider.png new file mode 100644 index 0000000..f0effc1 Binary files /dev/null and b/test/qad/icons/touchslider.png differ diff --git a/test/qad/icons/transform-move.png b/test/qad/icons/transform-move.png new file mode 100644 index 0000000..b20fc44 Binary files /dev/null and b/test/qad/icons/transform-move.png differ diff --git a/test/qad/icons/variable-add.png b/test/qad/icons/variable-add.png new file mode 100644 index 0000000..0a551b1 Binary files /dev/null and b/test/qad/icons/variable-add.png differ diff --git a/test/qad/icons/variable-copy.png b/test/qad/icons/variable-copy.png new file mode 100644 index 0000000..42d91c3 Binary files /dev/null and b/test/qad/icons/variable-copy.png differ diff --git a/test/qad/icons/variable-edit.png b/test/qad/icons/variable-edit.png new file mode 100644 index 0000000..420ace3 Binary files /dev/null and b/test/qad/icons/variable-edit.png differ diff --git a/test/qad/icons/variable-remove.png b/test/qad/icons/variable-remove.png new file mode 100644 index 0000000..0686e7c Binary files /dev/null and b/test/qad/icons/variable-remove.png differ diff --git a/test/qad/icons/view-autofit.png b/test/qad/icons/view-autofit.png new file mode 100644 index 0000000..d8defba Binary files /dev/null and b/test/qad/icons/view-autofit.png differ diff --git a/test/qad/icons/view-fullscreen.png b/test/qad/icons/view-fullscreen.png new file mode 100644 index 0000000..4a7c152 Binary files /dev/null and b/test/qad/icons/view-fullscreen.png differ diff --git a/test/qad/icons/view-grid.png b/test/qad/icons/view-grid.png new file mode 100644 index 0000000..9a0a458 Binary files /dev/null and b/test/qad/icons/view-grid.png differ diff --git a/test/qad/icons/view-preview.png b/test/qad/icons/view-preview.png new file mode 100644 index 0000000..ac3cd37 Binary files /dev/null and b/test/qad/icons/view-preview.png differ diff --git a/test/qad/icons/view-refresh.png b/test/qad/icons/view-refresh.png new file mode 100644 index 0000000..66f0ceb Binary files /dev/null and b/test/qad/icons/view-refresh.png differ diff --git a/test/qad/icons/z-bottom.png b/test/qad/icons/z-bottom.png new file mode 100644 index 0000000..fa05b36 Binary files /dev/null and b/test/qad/icons/z-bottom.png differ diff --git a/test/qad/icons/z-down.png b/test/qad/icons/z-down.png new file mode 100644 index 0000000..6f72223 Binary files /dev/null and b/test/qad/icons/z-down.png differ diff --git a/test/qad/icons/z-top.png b/test/qad/icons/z-top.png new file mode 100644 index 0000000..75a6eec Binary files /dev/null and b/test/qad/icons/z-top.png differ diff --git a/test/qad/icons/z-up.png b/test/qad/icons/z-up.png new file mode 100644 index 0000000..d4b3806 Binary files /dev/null and b/test/qad/icons/z-up.png differ diff --git a/test/qad/icons/zoom-draw.png b/test/qad/icons/zoom-draw.png new file mode 100644 index 0000000..470b7f0 Binary files /dev/null and b/test/qad/icons/zoom-draw.png differ diff --git a/test/qad/icons/zoom-fit-best.png b/test/qad/icons/zoom-fit-best.png new file mode 100644 index 0000000..a1395b4 Binary files /dev/null and b/test/qad/icons/zoom-fit-best.png differ diff --git a/test/qad/icons/zoom-in.png b/test/qad/icons/zoom-in.png new file mode 100644 index 0000000..9a97124 Binary files /dev/null and b/test/qad/icons/zoom-in.png differ diff --git a/test/qad/icons/zoom-original.png b/test/qad/icons/zoom-original.png new file mode 100644 index 0000000..55b3ac8 Binary files /dev/null and b/test/qad/icons/zoom-original.png differ diff --git a/test/qad/icons/zoom-out.png b/test/qad/icons/zoom-out.png new file mode 100644 index 0000000..6e4e03f Binary files /dev/null and b/test/qad/icons/zoom-out.png differ diff --git a/test/qad/icons/zoom-range.png b/test/qad/icons/zoom-range.png new file mode 100644 index 0000000..070a16f Binary files /dev/null and b/test/qad/icons/zoom-range.png differ diff --git a/test/qad/icons/zoom-rect.png b/test/qad/icons/zoom-rect.png new file mode 100644 index 0000000..5fa6283 Binary files /dev/null and b/test/qad/icons/zoom-rect.png differ diff --git a/test/qad/icons/zoom-select.png b/test/qad/icons/zoom-select.png new file mode 100644 index 0000000..220d42c Binary files /dev/null and b/test/qad/icons/zoom-select.png differ diff --git a/test/qad/sql_table/CMakeLists.txt b/test/qad/sql_table/CMakeLists.txt new file mode 100644 index 0000000..c7f7282 --- /dev/null +++ b/test/qad/sql_table/CMakeLists.txt @@ -0,0 +1 @@ +qad_project(sql_table "Gui;Widgets;Sql" "qad_utils;qad_widgets") diff --git a/test/qad/sql_table/lang/qad_sql_table_ru.ts b/test/qad/sql_table/lang/qad_sql_table_ru.ts new file mode 100644 index 0000000..6a96bff --- /dev/null +++ b/test/qad/sql_table/lang/qad_sql_table_ru.ts @@ -0,0 +1,58 @@ + + + + + SQLFilterEdit + + ≈ + + + + ≠ + + + + ≥ + + + + ≤ + + + + + SQLTableWidget + + Table doesn`t exists! + Таблица не существует! + + + Add record + Добавить запись + + + Configure filters + Настроить фильтры + + + Clear + Очистить + + + Operator AND + Операция И + + + Operator OR + Операция ИЛИ + + + Remove selected + Удалить выделенные + + + Table "%1" doesn`t exists! + Таблица "%1" не существует! + + + diff --git a/test/qad/sql_table/plugin/CMakeLists.txt b/test/qad/sql_table/plugin/CMakeLists.txt new file mode 100644 index 0000000..d6e9378 --- /dev/null +++ b/test/qad/sql_table/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(sql_table "Gui;Widgets;Sql" "") diff --git a/test/qad/sql_table/plugin/qad_sql_table.cpp b/test/qad/sql_table/plugin/qad_sql_table.cpp new file mode 100644 index 0000000..795bfb6 --- /dev/null +++ b/test/qad/sql_table/plugin/qad_sql_table.cpp @@ -0,0 +1,19 @@ +#include "qad_sql_table.h" +#include "sql_table_plugin.h" +#include "sql_record_plugin.h" + + +QADSQLTable::QADSQLTable(QObject * parent): QObject(parent) { + m_widgets.append(new SQLTablePlugin(this)); + m_widgets.append(new SQLRecordPlugin(this)); +} + + +QList QADSQLTable::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_sql_table_plugin, QADSQLTable) +#endif diff --git a/test/qad/sql_table/plugin/qad_sql_table.h b/test/qad/sql_table/plugin/qad_sql_table.h new file mode 100644 index 0000000..bb59554 --- /dev/null +++ b/test/qad/sql_table/plugin/qad_sql_table.h @@ -0,0 +1,23 @@ +#ifndef QAD_SQL_TABLE_H +#define QAD_SQL_TABLE_H + +#include +#include + +class QADSQLTable: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.sql_table") +#endif +public: + explicit QADSQLTable(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_SQL_TABLE_H diff --git a/test/qad/sql_table/plugin/sql_record_plugin.cpp b/test/qad/sql_table/plugin/sql_record_plugin.cpp new file mode 100644 index 0000000..031977d --- /dev/null +++ b/test/qad/sql_table/plugin/sql_record_plugin.cpp @@ -0,0 +1,69 @@ +#include "sql_record_widget.h" +#include "sql_record_plugin.h" +#include + + +SQLRecordPlugin::SQLRecordPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void SQLRecordPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool SQLRecordPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * SQLRecordPlugin::createWidget(QWidget * parent) { + return new SQLRecordWidget(parent); +} + + +QString SQLRecordPlugin::name() const { + return QLatin1String("SQLRecordWidget"); +} + + +QString SQLRecordPlugin::group() const { + return QLatin1String("Editor Widgets"); +} + + +QIcon SQLRecordPlugin::icon() const { + return QIcon(":/icons/sql_table.png"); +} + + +QString SQLRecordPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString SQLRecordPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool SQLRecordPlugin::isContainer() const { + return false; +} + + +QString SQLRecordPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString SQLRecordPlugin::includeFile() const { + return QLatin1String("sql_record_widget.h"); +} + diff --git a/test/qad/sql_table/plugin/sql_record_plugin.h b/test/qad/sql_table/plugin/sql_record_plugin.h new file mode 100644 index 0000000..8278f86 --- /dev/null +++ b/test/qad/sql_table/plugin/sql_record_plugin.h @@ -0,0 +1,36 @@ +#ifndef SQLRECORDPLUGIN_H +#define SQLRECORDPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class SQLRecordPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + SQLRecordPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // SQLRECORDPLUGIN_H diff --git a/test/qad/sql_table/plugin/sql_table_plugin.cpp b/test/qad/sql_table/plugin/sql_table_plugin.cpp new file mode 100644 index 0000000..50e0969 --- /dev/null +++ b/test/qad/sql_table/plugin/sql_table_plugin.cpp @@ -0,0 +1,69 @@ +#include "sql_table_widget.h" +#include "sql_table_plugin.h" +#include + + +SQLTablePlugin::SQLTablePlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void SQLTablePlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool SQLTablePlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * SQLTablePlugin::createWidget(QWidget * parent) { + return new SQLTableWidget(parent); +} + + +QString SQLTablePlugin::name() const { + return QLatin1String("SQLTableWidget"); +} + + +QString SQLTablePlugin::group() const { + return QLatin1String("Editor Widgets"); +} + + +QIcon SQLTablePlugin::icon() const { + return QIcon(":/icons/sql_table.png"); +} + + +QString SQLTablePlugin::toolTip() const { + return QLatin1String(""); +} + + +QString SQLTablePlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool SQLTablePlugin::isContainer() const { + return false; +} + + +QString SQLTablePlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString SQLTablePlugin::includeFile() const { + return QLatin1String("sql_table_widget.h"); +} + diff --git a/test/qad/sql_table/plugin/sql_table_plugin.h b/test/qad/sql_table/plugin/sql_table_plugin.h new file mode 100644 index 0000000..e3ce3f1 --- /dev/null +++ b/test/qad/sql_table/plugin/sql_table_plugin.h @@ -0,0 +1,36 @@ +#ifndef SQLTABLEPLUGIN_H +#define SQLTABLEPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class SQLTablePlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + SQLTablePlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // SQLTABLEPLUGIN_H diff --git a/test/qad/sql_table/qad_sql_table.qrc b/test/qad/sql_table/qad_sql_table.qrc new file mode 100644 index 0000000..b1caabc --- /dev/null +++ b/test/qad/sql_table/qad_sql_table.qrc @@ -0,0 +1,26 @@ + + + ../icons/edit-table-insert-row-below.png + ../icons/dialog-close.png + ../icons/edit-clear.png + ../icons/edit-guides.png + ../icons/view-grid.png + ../icons/zoom-fit-best.png + ../icons/configure.png + ../icons/document-save.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/item.png + ../icons/node-add.png + ../icons/node.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/expand_s_x.png + ../icons/expand_s_y.png + ../icons/expand_x.png + ../icons/expand_y.png + ../icons/sql_table.png + + diff --git a/test/qad/sql_table/sql_record_widget.cpp b/test/qad/sql_table/sql_record_widget.cpp new file mode 100644 index 0000000..eac9bdc --- /dev/null +++ b/test/qad/sql_table/sql_record_widget.cpp @@ -0,0 +1,352 @@ +#include "sql_record_widget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +SQLRecordWidget::SQLRecordWidget(QWidget * parent): QWidget(parent) { + setLayout(new QFormLayout()); + ro = false; +} + + +SQLRecordWidget::~SQLRecordWidget() { +} + + +void SQLRecordWidget::setRecord(const QSqlRecord & q, bool full_update) { + //qDebug() << (q.count() - hidden.size()) << (layout()->count() / 2); + if (full_update || (q.count() - hidden.size()) != (layout()->count() / 2)) createWidgets(q); + for (int i = 0; i < q.count(); ++i) { + QSqlField f = q.field(i); + QWidget * val = 0; + for (int w = 0; w < cws.size(); ++w) + if (cws[w]->objectName() == f.name()) { + val = cws[w]; + break; + } + if (val == 0) continue; + if (qobject_cast(val)) { + if (relations.contains(f.name())) { + bool ok = false; + int sv(f.value().toInt(&ok)); + if (!ok) + qobject_cast(val)->setText(""); + else { + QList > & rv(relations[f.name()]); + for (int j = 0; j < rv.size(); ++j) { + if (sv == rv[j].first) { + qobject_cast(val)->setText(rv[j].second); + break; + } + } + } + } else + qobject_cast(val)->setText(f.value().toString()); + } + if (qobject_cast(val)) qobject_cast(val)->setChecked(f.value().toBool()); + if (qobject_cast(val)) qobject_cast(val)->setValue(f.value().toDouble()); + if (qobject_cast(val)) qobject_cast(val)->setColor(QColor::fromRgba(f.value().toUInt())); + if (qobject_cast(val)) { + QString s = f.value().toString(); + qobject_cast(val)->setValue(s.isEmpty() ? QStringList() : s.split(";")); + } + if (qobject_cast(val)) { + bool ok = false; + int sv(f.value().toInt(&ok)); + if (!ok) + ((QComboBox*)val)->setCurrentIndex(-1); + else { + QList > & rv(relations[f.name()]); + for (int j = 0; j < rv.size(); ++j) { + if (sv == rv[j].first) { + ((QComboBox*)val)->setCurrentIndex(j); + break; + } + } + } + } + } +} + + +void SQLRecordWidget::setFixedValue(const QString & name, const QVariant & value) { + foreach (QWidget * w, cws) { + if (w->objectName() != name) continue; + if (qobject_cast(w)) {qobject_cast(w)->setText(value.toString()); qobject_cast(w)->setReadOnly(true);} + if (qobject_cast(w)) {qobject_cast(w)->setChecked(value.toBool()); w->setEnabled(false);} + if (qobject_cast(w)) {qobject_cast(w)->setValue(value.toDouble()); qobject_cast(w)->setReadOnly(true);} + if (qobject_cast(w)) {qobject_cast(w)->setColor(QColor::fromRgba(value.toUInt())); qobject_cast(w)->setEnabled(false);} + if (qobject_cast(w)) {qobject_cast(w)->setValue(QStringList()); qobject_cast(w)->setEnabled(false);} + } + +} + + +void SQLRecordWidget::clearValues() { + foreach (QWidget * w, cws) { + if (qobject_cast(w)) qobject_cast(w)->setText(QString()); + if (qobject_cast(w)) qobject_cast(w)->setChecked(false); + if (qobject_cast(w)) qobject_cast(w)->setValue(0.); + if (qobject_cast(w)) qobject_cast(w)->setColor(Qt::black); + if (qobject_cast(w)) qobject_cast(w)->setValue(QStringList()); + } +} + + +void SQLRecordWidget::createWidgets(const QSqlRecord & q) { + qDeleteAll(cws); + cws.clear(); + delete layout(); + QFormLayout * lay = new QFormLayout(); + lay->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + lay->setLabelAlignment(Qt::AlignRight); + lay->setContentsMargins(0, 0, 0, 0); + setLayout(lay); + //qDebug() << "createWidgets" << q.count(); + for (int i = 0; i < q.count(); ++i) { + QSqlField f = q.field(i); + if (hidden.contains(f.name())) continue; + QPair ctr = trColumn(f.name()); + QWidget * val = 0; + if (relations.contains(f.name())) { + if (ro) { + val = new QLineEdit(); + } else { + val = new QComboBox(); + QList > & rv(relations[f.name()]); + bool ok = false; + int sv(f.value().toInt(&ok)); + for (int j = 0; j < rv.size(); ++j) { + ((QComboBox*)val)->addItem(rv[j].second, rv[j].first); + if (ok) + if (sv == rv[j].first) + ((QComboBox*)val)->setCurrentIndex(j); + } + if (!ok) + ((QComboBox*)val)->setCurrentIndex(-1); + } + } else { + switch (fieldType(f)) { + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + val = new QDoubleSpinBox(); + ((QDoubleSpinBox*)val)->setRange(-1E+99, 1E+99); + ((QDoubleSpinBox*)val)->setDecimals(3); break; + case QVariant::String: + val = new QLineEdit(); break; + case QVariant::Bool: + val = new QCheckBox(); break; + case QVariant::Color: + val = new ColorButton(); + ((ColorButton*)val)->setUseAlphaChannel(true); + ((ColorButton*)val)->setUseNativeDialog(true); + break; + case QVariant::StringList: + val = new StringListEdit(); + break; + default: break; + } + if (f.type() == QVariant::String || f.type() == QVariant::StringList) val->setProperty("_string", true); + } + if (val != 0) { + QString lt = ctr.first.left(1).toUpper() + ctr.first.right(ctr.first.length() - 1); + while (lt.endsWith("_")) lt.chop(1); + QLabel * lbl = new QLabel(lt + ":"); + lbl->setToolTip(ctr.second); + cws << lbl; + val->setObjectName(f.name()); + cws << val; + lay->addRow(lbl, val); + } else { + if (!hidden.contains(ctr.first)) + hidden << ctr.first; + } + } + updateWidgets(); + emit widgetsCreated(); +} + + +void SQLRecordWidget::updateWidgets() { + foreach (QWidget * w, cws) { + if (qobject_cast(w)) qobject_cast(w)->setReadOnly(ro); + if (qobject_cast(w)) qobject_cast(w)->setEnabled(!ro); + if (qobject_cast(w)) qobject_cast(w)->setReadOnly(ro); + if (qobject_cast(w)) qobject_cast(w)->setEnabled(!ro); + if (qobject_cast(w)) qobject_cast(w)->setEnabled(!ro); + if (qobject_cast(w)) qobject_cast(w)->setEnabled(!ro); + } +} + + +QVariant::Type SQLRecordWidget::fieldType(const QSqlField & f) { + QString ft = ftypes.value(f.name()); + if (ft.isEmpty()) return f.type(); + if (ft == "l") return QVariant::StringList; + if (ft == "b") return QVariant::Bool; + if (ft == "n") return QVariant::Int; + if (ft == "f") return QVariant::Double; + if (ft == "c") return QVariant::Color; + if (ft == "r") return QVariant::Rect; + if (ft == "a") return QVariant::RectF; + if (ft == "p") return QVariant::Point; + if (ft == "v") return QVariant::PointF; + return QVariant::String; +} + + +void SQLRecordWidget::addTranslation(const QString & file) { + QPIConfig conf(file, QIODevice::ReadOnly, QPIConfig::Config); + QPIConfig::Branch ae = conf.allLeaves(); + foreach (QPIConfig::Entry * e, ae) + translates[e->name()] = QPair(e->value(), e->comment()); +} + + +QString SQLRecordWidget::getValuesQuery() const { + QString ret("("); + bool first = true; + foreach (QWidget * w, cws) { + QWidget * vw(0); + if (qobject_cast(w)) {if (!qobject_cast(w)->text().isEmpty()) vw = w;} + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (vw == 0) continue; + if (!first) ret += ","; + first = false; + ret += vw->objectName(); + } + ret += ") VALUES ("; + foreach (QWidget * w, cws) { + QWidget * vw(0); + if (qobject_cast(w)) { + if (!qobject_cast(w)->text().isEmpty()) { + vw = w; + bool q = w->property("_string").toBool(); + if (q) ret += "'"; + ret += qobject_cast(w)->text(); + if (q) ret += "'"; + } + } + if (qobject_cast(w)) { + vw = w; + ret += qobject_cast(w)->isChecked() ? "1" : "0"; + } + if (qobject_cast(w)) { + vw = w; + ret += QString::number(qobject_cast(w)->value()); + } + if (qobject_cast(w)) { + vw = w; + ret += QString::number(qobject_cast(w)->color().rgba()); + } + if (qobject_cast(w)) { + vw = w; + ret += "'" + qobject_cast(w)->value().join(";") + "'"; + } + if (qobject_cast(w)) { + vw = w; + ret += ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex(), Qt::UserRole).toString(); + } + if (vw == 0) continue; + ret += ","; + } + if (ret.endsWith(",")) ret.chop(1); + ret += ")"; + return ret; +} + + +QString SQLRecordWidget::getUpdateQuery() const { + QString ret; + bool first = true; + foreach (QWidget * w, cws) { + QWidget * vw(0); + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (qobject_cast(w)) vw = w; + if (vw == 0) continue; + if (!first) ret += ","; + first = false; + ret += vw->objectName() + "="; + if (qobject_cast(w)) { + if (qobject_cast(w)->text().isEmpty()) + ret += "null"; + else { + bool q = w->property("_string").toBool(); + if (q) ret += "'"; + ret += qobject_cast(w)->text(); + if (q) ret += "'"; + } + } + if (qobject_cast(w)) + ret += qobject_cast(w)->isChecked() ? "1" : "0"; + if (qobject_cast(w)) + ret += QString::number(qobject_cast(w)->value()); + if (qobject_cast(w)) + ret += QString::number(qobject_cast(w)->color().rgba()); + if (qobject_cast(w)) + ret += "'" + qobject_cast(w)->value().join(";") + "'"; + if (qobject_cast(w)) { + QString cd = ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex(), Qt::UserRole).toString(); + ret += cd.isEmpty() ? "null" : cd; + } + } + return ret; +} + + +bool SQLRecordWidget::isEmpty() const { + return layout()->count() < 2; +} + + +void SQLRecordWidget::updateRelations() { + relations.clear(); + for (int i = 0; i < relations_src.size(); ++i) { + QSqlQuery q(QString("SELECT %1,%2 FROM %3").arg(relations_src[i].key, relations_src[i].ocol, relations_src[i].table), QSqlDatabase::database(connection_name)); + QList > cr; + while (q.next()) + cr << QPair(q.value(0).toInt(), q.value(1).toString()); + relations[relations_src[i].tcol] = cr; + } + QFormLayout * lay = (QFormLayout*)layout(); + if (!lay) return; + for (int i = 0; i < lay->rowCount(); ++i) { + QString name = lay->itemAt(i, QFormLayout::FieldRole)->widget()->objectName(); + if (hidden.contains(name)) continue; + QComboBox * val = qobject_cast(lay->itemAt(i, QFormLayout::FieldRole)->widget()); + if (!val) continue; + if (!relations.contains(name)) continue; + QList > & rv(relations[name]); + bool ok = false; + int sv(val->itemData(val->currentIndex()).toInt()); + val->clear(); + for (int j = 0; j < rv.size(); ++j) { + val->addItem(rv[j].second, rv[j].first); + if (sv == rv[j].first) + val->setCurrentIndex(j); + } + if (!ok) + val->setCurrentIndex(-1); + } +} diff --git a/test/qad/sql_table/sql_record_widget.h b/test/qad/sql_table/sql_record_widget.h new file mode 100644 index 0000000..68903fa --- /dev/null +++ b/test/qad/sql_table/sql_record_widget.h @@ -0,0 +1,74 @@ +#ifndef SQL_RECORD_WIDGET_H +#define SQL_RECORD_WIDGET_H + +#include +#include +#include +#include +#include +#include + +class SQLRecordWidget: public QWidget { + Q_OBJECT +public: + SQLRecordWidget(QWidget * parent = 0); + ~SQLRecordWidget(); + + void setRecord(const QSqlRecord & q, bool full_update = false); + void setReadOnly(bool yes) {ro = yes; updateWidgets();} + void setFixedValue(const QString & name, const QVariant & value); + void setTypeForName(const QString & name, const QString & type) {ftypes[name] = type;} + void clearValues(); + void removeWidgets() {qDeleteAll(cws); cws.clear();} + + void hideColumn(const QString & col) {hidden << col;} + void showColumn(const QString & col) {hidden.remove(col);} + void showColumns() {hidden.clear();} + void addRelation(const QString & this_column, const QString & other_table, const QString & other_key, const QString & other_column) {relations_src << RelationSrc(this_column, other_table, other_key, other_column); updateRelations();} + void removeRelations() {relations_src.clear(); updateRelations();} + void addTranslation(const QString & file); + void setConnectionName(const QString &conn_name) {connection_name = conn_name;} + QString connectionName() const {return connection_name;} + + QString getValuesQuery() const; + QString getUpdateQuery() const; + bool isEmpty() const; + QWidget * valueWidget(const QString & name) const {foreach (QWidget * w, cws) if (w->objectName() == name) return w; return 0;} + + void updateRelations(); + +protected: + QPair trColumn(const QString & n) {QPair trn = translates.value(n); if (trn.first.isEmpty()) return QPair(n, ""); return trn;} + void createWidgets(const QSqlRecord & q); + void updateWidgets(); + QVariant::Type fieldType(const QSqlField & f); + + struct RelationSrc { + RelationSrc(const QString & v0 = QString(), const QString & v1 = QString(), const QString & v2 = QString(), const QString & v3 = QString()): + tcol(v0), + table(v1), + key(v2), + ocol(v3) + {} + QString tcol; + QString table; + QString key; + QString ocol; + }; + + QMap > translates; + QVector relations_src; + QMap > > relations; + QMap ftypes; + QSet hidden; + QWidgetList cws; + bool ro; + QString connection_name; + +signals: + void updateRequest(); + void widgetsCreated(); + +}; + +#endif // SQL_RECORD_WIDGET_H diff --git a/test/qad/sql_table/sql_table_widget.cpp b/test/qad/sql_table/sql_table_widget.cpp new file mode 100644 index 0000000..8caf1be --- /dev/null +++ b/test/qad/sql_table/sql_table_widget.cpp @@ -0,0 +1,1081 @@ +#include "sql_table_widget.h" +#include "ui_sql_table_widget.h" +#include +#include +#include +#include +#include +#include "qad_types.h" + + +QString SQLTableWidget::_dir; + + +ColumnProperties::ColumnProperties(const QString & table_, const QString & type_, const QString & name_, const QString & def_, bool auto_, bool prim_, bool option_) { + visible = true; + table = table_; + name = name_; + def = def_; + auto_increment = auto_; + primary = prim_; + optional = option_; + int bs = type_.indexOf("("), bf = type_.indexOf(")"); + size = type_.mid(bs + 1, bf - bs - 1).toInt(); + key_column = 0; + type = typeFromString(type_.toLower()); + is_text = (type == ColumnProperties::Chars || type == ColumnProperties::Text); +} + + +ColumnProperties::DataType ColumnProperties::typeFromString(const QString & n) { + if (n.indexOf("datetime") >= 0 || n.indexOf("timestamp") >= 0) return ColumnProperties::DateTime; + if (n.indexOf("date") >= 0) return ColumnProperties::Date; + if (n.indexOf("time") >= 0) return ColumnProperties::Time; + if (n.indexOf("int") >= 0) return ColumnProperties::Int; + if (n.indexOf("char") >= 0) return ColumnProperties::Chars; + if (n.indexOf("dec") >= 0 || n.indexOf("numeric") >= 0 || n.indexOf("real") >= 0 || + n.indexOf("float") >= 0 || n.indexOf("double") >= 0) return ColumnProperties::Float; + if (n.indexOf("text") >= 0) return ColumnProperties::Text; + if (n.indexOf("blob") >= 0) return ColumnProperties::Binary; + return ColumnProperties::Unknown; +} + + + + +QVariant SQLQueryModel::data(const QModelIndex & item, int role) const { + QVariant var = QSqlQueryModel::data(item, role); + //QVariant::Type t = var.type(); + /*if (t == QVariant::Date) return var.toDate().toString("dd.MM.yyyy"); + if (t == QVariant::Time) return var.toTime().toString("hh:mm:ss"); + if (t == QVariant::DateTime) return var.toDateTime().toString("dd.MM.yyyy hh:mm:ss");*/ + return var; +} + + +bool SQLQueryModel::setData(const QModelIndex& index, const QVariant & value, int role) { + if (value.isValid() && role == Qt::EditRole) { + QSqlQuery q(QSqlDatabase::database(conn_name)); + int col = mapColumn(index.column()); + ColumnProperties::DataType t = column_props[col].type; + bool quotes = (t == ColumnProperties::Chars || t == ColumnProperties::Text || + t == ColumnProperties::Date || t == ColumnProperties::Time || + t == ColumnProperties::ColumnProperties::DateTime); + QString val = value.toString(); + if (t == ColumnProperties::Date) val = value.toDate().toString("yyyy-MM-dd"); + if (t == ColumnProperties::Time) val = value.toTime().toString("hh:mm:ss"); + if (t == ColumnProperties::DateTime) val = value.toDateTime().toString("yyyy-MM-dd hh:mm:ss"); + if (!q.exec("UPDATE " + table_ + " SET " + column_props[col].name + "=" + + (quotes ? "'" : "") + val + (quotes ? "'" : "") + + " WHERE " + column_props[0].name + "=" + record(index.row()).value(0).toString() + ";")) { + qDebug() << "[UPDATE]" << q.lastQuery() << "Error:" << q.lastError(); + return false; + } + //qDebug() << "[UPDATE]" << q.lastQuery(); + dataChanged(index, index); + updateTable(true); + emit tableChanged(); + return true; + } + return false; +} + + + + +SQLUniEdit::SQLUniEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop), connection_name(conn_name) { + wtext = 0; wint = 0; wfloat = 0; wdate = 0; wtime = 0; wdatetime = 0; wrelation = 0; + setProp(prop_); + w_ << (QWidget **)&wtext << (QWidget **)&wint << (QWidget **)&wfloat << (QWidget **)&wdate << (QWidget **)&wtime << (QWidget **)&wdatetime << (QWidget **)&wrelation; +} + + +void SQLUniEdit::setProp(const ColumnProperties & p) { + for (int i = 0; i < w_.size(); ++i) deleteW(*w_[i]); + wtext = 0; wint = 0; wfloat = 0; wdate = 0; wtime = 0; wdatetime = 0; wrelation = 0; + prop_ = p; + if (prop_.isRelation()) { + wrelation = new QComboBox(this); + wrelation->installEventFilter(this); + for (int i = 0; i < prop_.relation_list.size(); ++i) + wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first); + connect(wrelation, SIGNAL(currentIndexChanged(int)), this, SLOT(value_int(int))); + } else { + ColumnProperties::DataType t = prop_.getType(); + switch (t) { + case ColumnProperties::Int: wint = new QSpinBox(this); wint->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect(wint, SIGNAL(valueChanged(int)), this, SLOT(value_int(int))); break; + case ColumnProperties::Float: wfloat = new QDoubleSpinBox(this); wfloat->setRange(-1E+300, 1E+300); wfloat->setDecimals(2); connect(wfloat, SIGNAL(valueChanged(double)), this, SLOT(value_float(double))); break; + case ColumnProperties::Date: wdate = new QDateEdit(this); wdate->setDisplayFormat("dd.MM.yyyy"); wdate->setCalendarPopup(true); wdate->setDate(QDate::currentDate()); connect(wdate, SIGNAL(dateChanged(QDate)), this, SLOT(value_date(QDate))); break; + case ColumnProperties::Time: wtime = new QTimeEdit(this); wtime->setDisplayFormat("hh:mm:ss"); wtime->setTime(QTime::currentTime()); connect(wtime, SIGNAL(timeChanged(QTime)), this, SLOT(value_time(QTime))); break; + case ColumnProperties::DateTime: wdatetime = new QDateTimeEdit(this); wdatetime->setDisplayFormat("dd.MM.yyyy hh:mm:ss"); wdatetime->setCalendarPopup(true); wdatetime->setDateTime(QDateTime::currentDateTime()); connect(wdatetime, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(value_datetime(QDateTime))); break; + case ColumnProperties::Chars: + case ColumnProperties::Text: + case ColumnProperties::Unknown: + default: wtext = new QLineEdit(this); connect(wtext, SIGNAL(textChanged(QString)), this, SLOT(value_text(QString))); break; + } + } + for (int i = 0; i < w_.size(); ++i) + if (*w_[i]) (*w_[i])->setHidden(false); + resizeEvent(0); +} + + +QString SQLUniEdit::value(bool for_insert) { + QString ret; + ColumnProperties::DataType t = (for_insert ? prop_.type : prop_.getType()); + bool quotes = (t == ColumnProperties::Chars || t == ColumnProperties::Text || + t == ColumnProperties::Date || t == ColumnProperties::Time || + t == ColumnProperties::ColumnProperties::DateTime); + if (quotes) ret = "'"; + if (prop_.isRelation()) + if (for_insert) + ret += wrelation->itemData(wrelation->currentIndex()).toString(); + else + ret += wrelation->currentText(); + else { + if (wint != 0) ret += QString::number(wint->value()); + else { + if (wfloat != 0) ret += QString::number(wfloat->value()); + else { + if (wdate != 0) ret += wdate->date().toString("yyyy-MM-dd"); + else { + if (wtime != 0) ret += wtime->time().toString("hh:mm:ss"); + else { + if (wdatetime != 0) ret += wdatetime->dateTime().toString("yyyy-MM-dd hh:mm:ss"); + else { + ret += wtext->text(); + } + } + } + } + } + } + if (quotes) ret += "'"; + if (ret == "''") ret.clear(); + return ret; +} + + +bool SQLUniEdit::eventFilter(QObject * o, QEvent * e) { + if (e->type() == QEvent::MouseButtonPress) + updateRelation(); + return QWidget::eventFilter(o, e); +} + + +void SQLUniEdit::setValue(const QVariant & value) { + if (prop_.isRelation()) return; + if (wint != 0) wint->setValue(value.toInt()); + else { + if (wfloat != 0) wfloat->setValue(value.toDouble()); + else { + if (wdate != 0) wdate->setDate(value.toDate()); + else { + if (wtime != 0) wtime->setTime(value.toTime()); + else { + if (wdatetime != 0) wdatetime->setDateTime(value.toDateTime()); + else { + wtext->setText(value.toString()); + } + } + } + } + } + +} + + +void SQLUniEdit::updateRelation() { + prop_.relation_list = SQLTableWidget::getColumnValues(prop_.relation_table, prop_.relation_key, prop_.relation_column, connection_name); + int pi = wrelation->currentIndex(); + wrelation->blockSignals(true); + wrelation->clear(); + for (int i = 0; i < prop_.relation_list.size(); ++i) + wrelation->addItem(prop_.relation_list[i].second, prop_.relation_list[i].first); + wrelation->setCurrentIndex(pi); + wrelation->blockSignals(false); +} + + + +SQLNewEdit::SQLNewEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) { + line = new SQLUniEdit(prop, conn_name); + line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + check = new QCheckBox(); + check->setChecked(true); + check->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + connect(check, SIGNAL(toggled(bool)), line, SLOT(setEnabled(bool))); + QLayout * lay = new QHBoxLayout(); + lay->addWidget(check); + lay->addWidget(line); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + setLayout(lay); + setProp(prop); + check->setChecked(!prop.primary); +} + + +void SQLNewEdit::setProp(const ColumnProperties & p) { + prop_ = p; + line->setProp(p); + check->setEnabled(p.optional); + if (!check->isEnabled()) check->setChecked(true); +} + + + +SQLFilterEdit::SQLFilterEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent): QWidget(parent), prop_(prop) { + line = new SQLUniEdit(prop, conn_name); + line->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + combo = new QComboBox(); + combo->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + QStringList sl; + sl << " " << "=" << trUtf8("≈") << trUtf8("≠") << ">" << trUtf8("≥") << "<" << trUtf8("≤"); + combo->addItems(sl); + combo->setCurrentIndex(0); + connect(line, SIGNAL(valueChanged(QVariant)), this, SIGNAL(filterChanged())); + connect(line, SIGNAL(valueChanged(QVariant)), this, SLOT(value_changed())); + connect(combo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(filterChanged())); + QLayout * lay = new QHBoxLayout(); + lay->addWidget(combo); + lay->addWidget(line); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(2); + setLayout(lay); + setProp(prop); +} + + +QString SQLFilterEdit::filter() const { + QString ret; + if (combo->currentIndex() == 0) return ret; + ret = prop_.fullName(); + switch (combo->currentIndex()) { + case 1: ret.append("="); break; + case 2: ret.append("="); break; + case 3: ret.append("!="); break; + case 4: ret.append(">"); break; + case 5: ret.append(">="); break; + case 6: ret.append("<"); break; + case 7: ret.append("<="); break; + }; + QString val = line->value(); + if (prop_.is_text && combo->currentIndex() == 2) { + ret.chop(1); + ret.append(" LIKE '%"); + ret.append(val.mid(1, val.length() - 2)); + ret.append("%'"); + } else + ret.append(val); + return ret; +} + + +void SQLFilterEdit::clear() { + line->setValue(QVariant()); + combo->setCurrentIndex(0); + +} + + + + +SQLItemDelegate::SQLItemDelegate(QList & cp, QVector & ci, bool & ro, const QString & conn_name, QObject * parent): + QStyledItemDelegate(parent), column_props(cp), column_indexes(ci), read_only(ro), connection_name(conn_name) {;} + + +QWidget * SQLItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const { + if (read_only) return 0; + ColumnProperties & cp(column_props[mapColumn(index.column())]); + if (cp.isRelation()) { + cp.relation_list = SQLTableWidget::getColumnValues(cp.relation_table, cp.relation_key, cp.relation_column, connection_name); + QComboBox * w = new QComboBox(parent); + w->setGeometry(option.rect); + for (int i = 0; i < cp.relation_list.size(); ++i) { + w->addItem(cp.relation_list[i].second, cp.relation_list[i].first); + if (index.data().toString() == cp.relation_list[i].second) + w->setCurrentIndex(i); + } + return w; + } else { + QWidget * w = QStyledItemDelegate::createEditor(parent, option, index); + if (qobject_cast(w)) {qobject_cast(w)->setDisplayFormat("hh:mm:ss");} + else if (qobject_cast(w)) {qobject_cast(w)->setCalendarPopup(true); qobject_cast(w)->setDisplayFormat("dd.MM.yyyy");} + else if (qobject_cast(w)) {qobject_cast(w)->setCalendarPopup(true); qobject_cast(w)->setDisplayFormat("dd.MM.yyyy hh:mm:ss");} + return w; + } + return 0; +} + + +QString SQLItemDelegate::displayText(const QVariant & value, const QLocale & locale) const { + QVariant::Type t = value.type(); + if (t == QVariant::Date) return value.toDate().toString("dd.MM.yyyy"); + if (t == QVariant::Time) return value.toTime().toString("hh:mm:ss"); + if (t == QVariant::DateTime) return value.toDateTime().toString("dd.MM.yyyy hh:mm:ss"); + return QStyledItemDelegate::displayText(value, locale); +} + + +void SQLItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + if (qobject_cast(editor)) { + model->setData(index, qobject_cast(editor)->itemData(qobject_cast(editor)->currentIndex())); + return; + } + QStyledItemDelegate::setModelData(editor, model, index); +} + + + +SQLTableWidget::SQLTableWidget(QWidget * parent): QWidget(parent), filters_group(this) { + ui = new Ui::SQLTableWidget(); + ui->setupUi(this); + model = 0; + timer = 0; + connection_name = QSqlDatabase::defaultConnection; + filters_active = first_update = true; + table_opened = read_only = false; + ui->labelNew->setFixedSize(preferredIconSize(1.2, this)); + ui->labelFilter->setFixedSize(preferredIconSize(1.2, this)); + ui->view->installEventFilter(this); + ui->view->viewport()->installEventFilter(this); + ui->view->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder); + ui->view->horizontalHeader()->viewport()->installEventFilter(this); + ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name)); + ui->view->horizontalHeader()->setItemDelegate(new QItemDelegate()); + ui->view->verticalHeader()->setDefaultSectionSize(fontHeight() * 1.5); + //qDebug() << view->horizontalHeader()->itemDelegate(); + //qDebug() << fontMetrics().elidedText(, Qt::ElideNone, 30, Qt::TextWordWrap); + setTableVisible(false); + connect(ui->view->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(header_sectionResized(int, int, int))); + connect(ui->view->horizontalHeader(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SLOT(header_sortIndicatorChanged(int, Qt::SortOrder))); + connect(ui->view->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrolled(int))); + + + //act_add.setText(trUtf8("Add ...")); + //act_del.setText(trUtf8("Remove selected")); + action_del = new QAction(QIcon(":/icons/edit-delete.png"), trUtf8("Remove selected"), this); + connect(action_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered())); + popup_menu.addAction(action_del); + ui->actionFilter_AND->setChecked(true); + filters_group.addAction(ui->actionFilter_AND); + filters_group.addAction(ui->actionFilter_OR); + connect(&filters_group, SIGNAL(triggered(QAction * )), this, SLOT(updateTable())); + popup_filter.addAction(ui->actionFiltersClear); + popup_filter.addSeparator(); + popup_filter.addAction(ui->actionFilter_AND); + popup_filter.addAction(ui->actionFilter_OR); + ui->buttonFilterConfig->setMenu(&popup_filter); + //connect(&act_add, SIGNAL(triggered(bool)), this, SLOT(add_triggered())); + //connect(&act_del, SIGNAL(triggered(bool)), this, SLOT(del_triggered())); +} + + +SQLTableWidget::~SQLTableWidget() { + stopTimer(); + if (model != 0) delete model; +} + + +bool SQLTableWidget::eventFilter(QObject * o, QEvent * e) { + if (o == ui->view) { + if (e->type() == QEvent::KeyPress) { + QKeyEvent * ke = (QKeyEvent * )e; + if (ke->key() == Qt::Key_Home) {ui->view->selectRow(0); return true;} + if (ke->key() == Qt::Key_End) {ui->view->selectRow(ui->view->verticalHeader()->count() - 1); return true;} + } + } + if (o == ui->view->viewport()) { + if (e->type() == QEvent::MouseButtonPress) { + QMouseEvent * me = (QMouseEvent * )e; + if (me->button() == Qt::RightButton) { + action_del->setVisible(!read_only); + popup_menu.popup(me->globalPos()); + } + } + } + if (o == ui->view->horizontalHeader()->viewport()) { + QMouseEvent * me = (QMouseEvent * )e; + if (e->type() == QEvent::MouseButtonPress) + pp = me->pos(); + if (e->type() == QEvent::MouseButtonRelease) { + if ((me->pos() - pp).manhattanLength() < QApplication::startDragDistance()) { + if (me->button() == Qt::LeftButton) { + int ci = ui->view->horizontalHeader()->logicalIndexAt(me->pos()), + ri = ui->view->horizontalHeader()->logicalIndexAt(me->pos() + QPoint(3, 0)), + li = ui->view->horizontalHeader()->logicalIndexAt(me->pos() - QPoint(3, 0)); + //qDebug() << ci << ri << li; + if ((ci < 0) || (ci >= ui->view->horizontalHeader()->count()) || (ci != ri) || (ci != li) || (tm.elapsed() <= QApplication::doubleClickInterval())) { + tm.restart(); + return QObject::eventFilter(o, e); + } + tm.restart(); + Qt::SortOrder o = Qt::AscendingOrder; + if (ui->view->horizontalHeader()->sortIndicatorSection() == ci && + ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder) + o = Qt::DescendingOrder; + ui->view->horizontalHeader()->setSortIndicator(ci, o); + return true; + } + if (me->button() == Qt::RightButton) { + popup_col.clear(); + for (int i = 0; i < column_props.size(); ++i) { + QPair ctr = trColumn(column_props[i].name); + QAction * a = popup_col.addAction(ctr.first, this, SLOT(column_triggered(bool))); + a->setToolTip(ctr.second); + a->setCheckable(true); + a->setChecked(column_props[i].visible); + a->setData(i); + } + popup_col.popup(me->globalPos()); + return true; + } + } else return true; + } + } + return QWidget::eventFilter(o, e); +} + + +void SQLTableWidget::timerEvent(QTimerEvent * ) { + setTableName(table_); +} + + +void SQLTableWidget::setTableName(const QString & t) { + ui->labelEmpty->setText(tr("Table \"%1\" doesn`t exists!").arg(t)); + stopTimer(); + table_opened = false; + first_update = true; + table_ = t; + if (!QSqlDatabase::database(connection_name).isOpen()) { + qDebug() << "[setTableName] Database in not opened!"; + setTableVisible(false); + timer = startTimer(1000); + return; + } + model = new SQLQueryModel(table_, connection_name, column_props, column_indexes, this); + connect(model, SIGNAL(updateTable(bool)), this, SLOT(updateTable(bool))); + connect(model, SIGNAL(tableChanged()), this, SIGNAL(tableChanged())); + //model->setTable(table_); + cquery = "SELECT * FROM " + table_ + ";"; + model->setQuery(cquery, QSqlDatabase::database(connection_name)); + ui->view->setModel(model); + connect(ui->view->selectionModel(), SIGNAL(currentRowChanged(QModelIndex, QModelIndex)), this, SIGNAL(selectionChanged()), Qt::UniqueConnection); + //((QSqlQueryModel * )model)->setQuery(QSqlQuery("SELECT id FROM " + table_ + ";")); + setTableVisible(checkTable()); + updateTable(); + foreach (const QString & wr, wait_rels) { + QStringList wrl = wr.split(";"); + addRelation(wrl[0], wrl[1], wrl[2], wrl[3]); + } + QMapIterator cnit(col_vis); + while (cnit.hasNext()) { + cnit.next(); + setColumnVisible(cnit.key(), cnit.value()); + } +} + + +bool SQLTableWidget::checkTable() { + table_opened = false; + QSqlDatabase db = QSqlDatabase::database(connection_name); + if (db.driver() == 0) return false; + bool te = db.tables(QSql::Tables).contains(table_, Qt::CaseInsensitive ); + //qDebug() << "tables:" << db.tables(QSql::Tables) << table_ << table_opened; + if (!te) { + timer = startTimer(1000); + return false; + } + /*if (!q.exec("SHOW TABLES")) { + qDebug() << "[SHOW TABLES]" << q.lastError(); + timer = startTimer(1000); + return false; + } + while (q.next()) { + //qDebug() << q.value(0); + if (q.value(0) == table_) + {table_opened = true; break;} + } + if (!table_opened) { + timer = startTimer(1000); + return false; + }*/ + QSqlRecord header = db.record(table_); + QString pin = db.primaryIndex(table_).fieldName(0); + table_opened = true; + column_props.clear(); + for (int i = 0; i < header.count(); ++i) { + QSqlField f = header.field(i); + column_props << ColumnProperties(table_, + f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type()), + f.name(), + f.defaultValue().toString(), + f.isAutoValue(), + f.name() == pin, + f.requiredStatus() == QSqlField::Optional + ); + } + /*if (!q.exec("DESCRIBE " + table_ + ";")) { + qDebug() << "[DESCRIBE " + table_ + "]" << q.lastError(); + timer = startTimer(1000); + return false; + } + while (q.next()) { + column_props << ColumnProperties(table_, + q.value(1).toString().toLower(), + q.value(0).toString().toLower(), + q.value(4).toString().toLower(), + q.value(5).toString().toLower().indexOf("auto_increment") >= 0, + q.value(3).toString().toLower().indexOf("pri") >= 0); + }*/ + //ui->labelNew->setFixedWidth(qMax(ui->view->verticalHeader()->sizeHint().width() + 2, 16)); + //ui->labelFilter->setFixedWidth(qMax(ui->view->verticalHeader()->sizeHint().width() + 2, 16)); + ui->scrollAreaNew->setFixedHeight(QLineEdit().sizeHint().height() + 2); + ui->scrollAreaFilter->setFixedHeight(QLineEdit().sizeHint().height() + 2); + qDeleteAll(column_news); + qDeleteAll(column_filters); + column_news.clear(); + column_filters.clear(); + SQLNewEdit * le = 0; + for (int i = 0; i < column_props.size(); ++i) { + le = new SQLNewEdit(column_props[i], connection_name); + le->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i)); + column_news.push_back(le); + ui->layoutNew->insertWidget(i, le); + } + for (int i = 0; i < column_props.size(); ++i) { + SQLFilterEdit * fe = new SQLFilterEdit(column_props[i], connection_name); + fe->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i)); + column_filters.push_back(fe); + ui->layoutFilter->insertWidget(i, fe); + connect(fe, SIGNAL(filterChanged()), this, SLOT(updateTable())); + } + return true; +} + + +QStringList SQLTableWidget::getTableColumns(const QString & t) { + QStringList ret; + QSqlDatabase db = QSqlDatabase::database(connection_name); + QSqlRecord header = db.record(t); + for (int i = 0; i < header.count(); ++i) + ret << header.field(i).name(); + return ret; +} + + +QString SQLTableWidget::getColumnType(const QString & t, const QString & c) { + QSqlDatabase db = QSqlDatabase::database(connection_name); + QSqlRecord header = db.record(t); + for (int i = 0; i < header.count(); ++i) { + QSqlField f = header.field(i); + if (f.name() != c) continue; + return (f.type() == QVariant::String ? "text" : QVariant::typeToName(f.type())); + } + return QString(); +} + + +QList > SQLTableWidget::getColumnValues(const QString &t, const QString &k, const QString &c, const QString &conn_name) { + QList > ret; + QSqlQuery q(QSqlDatabase::database(conn_name)); + if (!q.exec("SELECT " + t + "." + k + "," + t + "." + c + " FROM " + t + ";")) + return ret; + while (q.next()) + ret << QPair(q.value(0).toString(), q.value(1).toString()); + return ret; +} + + +QPair SQLTableWidget::trColumn(const QString & n) { + QPair ftr = fixed_translates.value(n); + if (!ftr.first.isEmpty()) + return ftr; + QPair trn = translates.value(n); + if (trn.first.isEmpty()) return QPair(n, ""); + return trn; +} + + +void SQLTableWidget::updateTable(bool save_selection) { + if (!filters_active || !table_opened) return; + int vp = ui->view->verticalScrollBar()->value(); + int hp = ui->view->horizontalScrollBar()->value(); + //bool focus = view->hasFocus(); + QModelIndex csi; + QModelIndexList sl; + if (save_selection) { + csi = ui->view->selectionModel()->currentIndex(); + sl = ui->view->selectionModel()->selectedRows(); + } + if (custom_query.isEmpty()) { + QString cr = columnRelations(), cf = columnFilters(); + bool where = !cr.isEmpty() || !cf.isEmpty(), and_ = !cr.isEmpty() && !cf.isEmpty(); + cquery = "SELECT " + columnNames() + " FROM " + tableNames() + + (where ? " WHERE " : "") + cr + (and_ ? " AND " : "") + cf + " ORDER BY " + + column_props[ui->view->horizontalHeader()->sortIndicatorSection()].fullName() + + (ui->view->horizontalHeader()->sortIndicatorOrder() == Qt::DescendingOrder ? " DESC" : " ASC") + ";"; + } else + cquery = custom_query; + //qDebug() << cquery; + model->setQuery(cquery, QSqlDatabase::database(connection_name)); + if (model->lastError().isValid()) + qDebug() << model->lastError(); + if (first_update) { + ui->view->resizeColumnsToContents(); + for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i) + if (ui->view->horizontalHeader()->sectionSize(i) < 100) + ui->view->horizontalHeader()->resizeSection(i, 100); + first_update = false; + } + //ui->labelNew->setFixedWidth(qMax(ui->view->verticalHeader()->sizeHint().width() + 2, 16)); + //ui->labelFilter->setFixedWidth(qMax(ui->view->verticalHeader()->sizeHint().width() + 2, 16)); + ui->layoutNew->invalidate(); + if (save_selection) { + QItemSelectionModel * sm = ui->view->selectionModel(); + foreach (const QModelIndex & i, sl) + sm->select(i, QItemSelectionModel::Select | QItemSelectionModel::Rows); + sm->setCurrentIndex(csi, QItemSelectionModel::Select); + //ui->view->setFocus(); + } + if (custom_query.isEmpty()) { + for (int i = 0; i < model->columnCount(); ++i) { + QPair ctr = trColumn(column_props[mapColumn(i)].name); + model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole); + model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole); + } + } else { + if (custom_col_names.isEmpty()) { + QSqlRecord header = model->record(); + for (int i = 0; i < header.count(); ++i) { + QSqlField f = header.field(i); + QPair ctr = trColumn(f.name()); + model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole); + model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole); + } + } else { + for (int i = 0; i < qMin(custom_col_names.size(), model->columnCount()); ++i) { + QPair ctr = trColumn(custom_col_names[i]); + model->setHeaderData(i, Qt::Horizontal, ctr.first, Qt::DisplayRole); + model->setHeaderData(i, Qt::Horizontal, ctr.second, Qt::ToolTipRole); + } + } + } + ui->view->verticalScrollBar()->setValue(vp); + ui->view->horizontalScrollBar()->setValue(hp); + //if (focus) view->setFocus(); +} + + +void SQLTableWidget::setConnectionName(const QString & conn_name) { + connection_name = conn_name; + ui->view->setItemDelegate(new SQLItemDelegate(column_props, column_indexes, read_only, connection_name)); +} + + +QString SQLTableWidget::tableNames() { + QString ret = table_; + QSet rtables; + for (int i = 0; i < column_props.size(); ++i) { + ColumnProperties & cp(column_props[i]); + if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty()) + continue; + if (rtables.contains(cp.relation_table)) + continue; + rtables << cp.relation_table; + ret.append("," + cp.relation_table); + } + return ret; +} + + +QString SQLTableWidget::columnNames() { + QString ret; + bool first = true; + column_indexes.clear(); + for (int i = 0; i < column_props.size(); ++i) { + ColumnProperties & cp(column_props[i]); + column_news[i]->setVisible(cp.visible); + column_filters[i]->setVisible(cp.visible); + if (!cp.visible && !cp.primary) + continue; + if (!first) ret.append(","); + first = false; + ret.append(cp.fullName()); + if (i > 0) column_indexes.push_back(i); + } + //qDebug() << column_indexes; + return ret; +} + + +QString SQLTableWidget::columnFilters() { + QString ret; + bool and_ = ui->actionFilter_AND->isChecked(); + for (int i = 0; i < column_props.size(); ++i) { + if (!column_filters[i]->isVisible() || column_filters[i]->isEmpty()) + continue; + if (!ret.isEmpty()) + ret.append(and_ ? " AND " : " OR "); + ret.append(column_filters[i]->filter()); + } + if (!ret.isEmpty()) { + ret.prepend("("); + ret.append(")"); + } + return ret; +} + + +QString SQLTableWidget::columnRelations() { + QString ret; + for (int i = 0; i < column_props.size(); ++i) { + ColumnProperties & cp(column_props[i]); + if (!cp.visible || cp.relation_key.isEmpty() || cp.relation_column.isEmpty()) + continue; + if (!ret.isEmpty()) + ret.append(" AND "); + ret.append(table_ + "." + cp.name + "="); + ret.append(cp.relation_table + "." + cp.relation_key); + } + if (!ret.isEmpty()) { + ret.prepend("("); + ret.append(")"); + } + return ret; +} + + +void SQLTableWidget::on_buttonAdd_clicked() { + QStringList values; + bool at_least = false; + for (int i = 0; i < column_props.size(); ++i) { + values.append(column_news[i]->value()); + if (!values.back().isEmpty()) at_least = true; + } + if (!at_least) return; + QString qs("INSERT INTO " + table_ + " ("); + bool first = true; + for (int i = 0; i < column_props.size(); ++i) { + if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue; + if (!first) qs.append(","); + qs.append(column_props[i].name); + first = false; + } + qs.append(") VALUES("); + first = true; + for (int i = 0; i < column_props.size(); ++i) { + if (values[i].isEmpty() || !column_news[i]->isEnabled()) continue; + if (!first) qs.append(","); + qs.append(values[i]); + first = false; + } + qs.append(");"); + QSqlQuery q(QSqlDatabase::database(connection_name)); + if (q.exec(qs)) { + updateTable(); + emit tableChanged(); + } else + qDebug() << q.lastError(); + //qDebug() << q.lastQuery(); +} + + +void SQLTableWidget::on_actionFiltersClear_triggered() { + filters_active = false; + foreach (SQLFilterEdit * l, column_filters) + l->clear(); + filters_active = true; + updateTable(); +} + + +void SQLTableWidget::on_view_clicked(const QModelIndex & index) { + emit rowClicked(index.row()); + emit recordClicked(model->record(index.row())); +} + + +void SQLTableWidget::del_triggered() { + QModelIndexList si = ui->view->selectionModel()->selectedIndexes(); + QSet ids; + foreach (const QModelIndex & i, si) + ids << model->index(i.row(), 0).data().toInt(); + if (ids.isEmpty()) return; + QString qs("DELETE FROM " + table_ + " WHERE " + column_props[0].name + " IN ("); + bool first = true; + foreach (int i, ids) { + if (!first) qs.append(","); + qs.append(QString::number(i)); + first = false; + } + qs.append(");"); + //qDebug() << qs; return; + QSqlQuery q(QSqlDatabase::database(connection_name)); + if (q.exec(qs)) { + updateTable(); + emit tableChanged(); + } else + qDebug() << q.lastError(); +} + + +void SQLTableWidget::setColumnVisible(int ind, bool visible) { + if (!columnExists(ind)) return; + column_props[ind].visible = visible; + if (ind == 0) { + ui->view->setColumnHidden(0, !visible); + if (visible) ui->view->horizontalHeader()->resizeSection(0, ui->view->horizontalHeader()->defaultSectionSize()); + } + //qDebug() << ind << on; + updateTable(); + for (int i = 0; i < ui->view->horizontalHeader()->count(); ++i) { + column_news[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i)); + column_filters[mapColumn(i)]->setFixedWidth(ui->view->horizontalHeader()->sectionSize(i)); + } +} + + +bool SQLTableWidget::addRelation(const QString & this_column, const QString & other_table, const QString & other_key, const QString & other_column) { + QString wr = this_column + ";" + other_table + ";" + other_key + ";" + other_column; + if (!wait_rels.contains(wr)) wait_rels << wr; + ColumnProperties * cp = const_cast(columnProperty(this_column)); + if (cp == 0) return false; + cp->relation_table = other_table; + cp->relation_key = other_key; + cp->relation_column = other_column; + cp->relation_type = ColumnProperties::typeFromString(getColumnType(other_table, other_column)); + cp->relation_list = getColumnValues(other_table, other_key, other_column, connection_name); + for (int i = 0; i < column_props.size(); ++i) + column_news[i]->setProp(column_props[i]); + for (int i = 0; i < column_props.size(); ++i) + column_filters[i]->setProp(column_props[i]); + updateTable(); + return true; +} + + +void SQLTableWidget::addTranslation(const QString & file) { + QPIConfig conf(file, QIODevice::ReadOnly, QPIConfig::Config); + QPIConfig::Branch ae = conf.allLeaves(); + foreach (QPIConfig::Entry * e, ae) + translates[e->name()] = QPair(e->value(), e->comment()); +} + + +void SQLTableWidget::addFixedColumnTranslation(const QString & col_name, const QString & col_tr, const QString & col_tt) { + fixed_translates[col_name] = QPair(col_tr, col_tt); + updateTable(); +} + + +void SQLTableWidget::selectId(int id) { + if (!model) return; + for (int i = 0; i < model->rowCount(); ++i) + if (model->data(model->index(i, 0)).toInt() == id) { + ui->view->selectRow(i); + break; + } +} + + +void SQLTableWidget::setAdditionalActions(QList a) { + foreach (QAction * i, add_actions) + popup_menu.removeAction(i); + add_actions = a; + popup_menu.addActions(a); +} + + +void SQLTableWidget::setCustomQuery(const QString & q) { + custom_query = q; + updateTable(); +} + + +QString SQLTableWidget::preprocessScript(QString text) { + QTextStream s(&text, QIODevice::ReadOnly); + QString out; + int ce = 0; + while (!s.atEnd()) { + QString line = s.readLine(); + ce = line.indexOf("--"); + if (ce >= 0) + line.chop(line.length() - ce); + out.append(line).append("\n"); + } + int cs = out.indexOf("/*"); + while (cs >= 0) { + ce = out.indexOf("*/", cs); + if (ce > cs) + out.remove(cs, ce - cs + 2); + cs = out.indexOf("/*"); + } + //qDebug() << out; + return out; +} + + +bool SQLTableWidget::executeScript(const QString & text_, QSqlDatabase db, bool skip_errors, bool sqlite) { + QString text = preprocessScript(text_); + int ls = 0, le = text.indexOf(';'); + QSqlQuery q(db); + db.transaction(); + while (le > 0) { + QString line = text.mid(ls, le - ls + 1).trimmed(), fword; + if (sqlite) line.replace("auto_increment", "autoincrement"); + QTextStream(&line, QIODevice::ReadOnly) >> fword; + fword = fword.trimmed().toLower(); + if (fword == "exec" || fword == "execute") { + line = line.right(line.length() - fword.length()).trimmed(); + if (line.endsWith(";")) line.chop(1); + line = line.trimmed(); + if (line.endsWith("'") || line.endsWith("\"")) line.chop(1); + if (line.startsWith("'") || line.startsWith("\"")) line.remove(0, 1); + executeScriptFile(line, db, skip_errors, sqlite); + } else { + if (!q.exec(line)) { + qDebug() << q.lastError(); + if (!skip_errors) + return false; + } + } + ls = le + 1; + le = text.indexOf(';', ls); + } + db.commit(); + return true; +} + + +bool SQLTableWidget::executeScriptFile(const QString & file, QSqlDatabase db, bool skip_errors, bool sqlite) { + QFile f(_dir + file); + if (!f.open(QIODevice::ReadOnly)) { + qDebug() << "Can`t open file" << file << "!"; + return false; + } + QString pd = _dir; + _dir = QFileInfo(f).absolutePath() + "/"; + QByteArray ba = f.readAll(); + bool ok = executeScript(QString::fromUtf8(ba.data(), ba.size()), db, skip_errors, sqlite); + f.close(); + _dir = pd; + return ok; +} + + +void SQLTableWidget::column_triggered(bool on) { + setColumnVisible(((QAction * )sender())->data().toInt(), on); +} + + +void SQLTableWidget::header_sectionResized(int logicalIndex, int oldSize, int newSize) { + column_news[mapColumn(logicalIndex)]->setFixedWidth(newSize); + column_filters[mapColumn(logicalIndex)]->setFixedWidth(newSize); +} + + +void SQLTableWidget::header_sortIndicatorChanged(int logicalIndex, Qt::SortOrder order) { + updateTable(); +} + + +bool SQLTableWidget::connectToDatabase(const QString & config, const QString & conn_name) { + bool ok = true; + { + QPIConfig conf(config, QIODevice::ReadOnly, QPIConfig::Config); + if (!conf.isOpen()) + ok = false; + else { + QSqlDatabase base = QSqlDatabase::addDatabase(conf.getValue("driver", "QMYSQL").stringValue(), conn_name); + if (!base.isValid()) { + qDebug() << "[QSqlDatabase::addDatabase] Error:" << base.lastError(); + ok = false; + } else { + base.setHostName(conf.getValue("host", "").stringValue()); + base.setPort(conf.getValue("port", -1)); + base.setDatabaseName(conf.getValue("database", "").stringValue()); + if (!base.open(conf.getValue("login", "").stringValue(), conf.getValue("password", "").stringValue())) { + qDebug() << "[QSqlDatabase::open] Error:" << base.lastError(); + ok = false; + } + } + } + } + if (!ok) + QSqlDatabase::removeDatabase(conn_name); + /*QSqlQuery q; + QString dbname = conf.getValue("database", "").stringValue(); + bool dbex = false; + if (!q.exec("SHOW DATABASES;")) { + qDebug() << "[SHOW DATABASES]" << q.lastError(); + return false; + } + while (q.next()) + if (q.value(0) == dbname) + {dbex = true; break;} + if (!dbex) { + if (!q.exec("CREATE DATABASE " + dbname + ";")) { + qDebug() << "[CREATE DATABASE]" << q.lastError(); + return false; + } + } + if (!q.exec("USE " + dbname + ";")) { + qDebug() << "[USE]" << q.lastError(); + return false; + }*/ + return ok; +} + + +QTableView * SQLTableWidget::tableView() { + return ui->view; +} + + +bool SQLTableWidget::lineNewVisible() const { + return ui->widgetNew->isVisibleTo(ui->pageTable); +} + + +bool SQLTableWidget::lineFilterVisible() const { + return ui->widgetFilter->isVisibleTo(ui->pageTable); +} + + +QSqlRecord SQLTableWidget::currentRecord() const { + if (model) return model->record(ui->view->currentIndex().row()); + return QSqlRecord(); +} + + +void SQLTableWidget::setTableVisible(bool on) { + ui->stackedWidget->setCurrentIndex(on ? 1 : 0); +} + + +void SQLTableWidget::setLineNewVisible(bool on) { + ui->widgetNew->setVisible(on); + ui->buttonAdd->setVisible(on); +} + + +void SQLTableWidget::setLineFilterVisible(bool on) { + ui->widgetFilter->setVisible(on); + ui->buttonFilterConfig->setVisible(on); +} + + +void SQLTableWidget::setReadOnly(bool yes) { + read_only = yes; + ui->scrollAreaNew->setDisabled(yes); + ui->buttonAdd->setDisabled(yes); +} + + +void SQLTableWidget::scrolled(int value) { + ui->scrollAreaNew->horizontalScrollBar()->setValue(value); + ui->scrollAreaFilter->horizontalScrollBar()->setValue(value); +} diff --git a/test/qad/sql_table/sql_table_widget.h b/test/qad/sql_table/sql_table_widget.h new file mode 100644 index 0000000..b518ee0 --- /dev/null +++ b/test/qad/sql_table/sql_table_widget.h @@ -0,0 +1,335 @@ +#ifndef MYSQLTABLE_H +#define MYSQLTABLE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qpiconfig.h" + + +struct ColumnProperties { + enum DataType {Unknown, Int, Float, Chars, Text, Binary, Date, Time, DateTime}; + ColumnProperties(const QString & table_, const QString & type_, const QString & name_, const QString & def_, bool auto_, bool prim_, bool option_); + bool isRelation() const {return (!relation_key.isEmpty() && !relation_column.isEmpty());} + QString shortName() const {if (!isRelation()) return name; return relation_column;} + QString fullName() const {if (!isRelation()) return table + "." + name; return (relation_table.isEmpty() ? "" : relation_table + ".") + relation_column;} + DataType getType() const {if (!isRelation()) return type; return relation_type;} + static DataType typeFromString(const QString & n); + + DataType type; + int size; + bool auto_increment; + bool primary; + bool optional; + bool visible; + bool is_text; + QString table; + QString name; + QString relation_table; + QString relation_key; + QString relation_column; + QList > relation_list; // + DataType relation_type; + int key_column; + QString def; +}; + + + + +struct TableColumns { + int mapColumn(int abs_ind) const {if (abs_ind <= 0 || abs_ind >= column_indexes.size() + 1) return 0; return column_indexes[abs_ind - 1];} + QVector column_indexes; + QList columns; +}; + + + + +class SQLQueryModel: public QSqlQueryModel { + Q_OBJECT +public: + explicit SQLQueryModel(QString & t, QString & conn, QList & cp, QVector & ci, QObject* parent = 0): QSqlQueryModel(parent), table_(t), column_props(cp), column_indexes(ci), conn_name(conn) {;} + virtual QVariant data(const QModelIndex & item, int role = Qt::DisplayRole) const; +protected: + virtual Qt::ItemFlags flags(const QModelIndex & ) const {return (Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);} + virtual bool setData(const QModelIndex & index, const QVariant& value, int role = Qt::EditRole); + int mapColumn(int abs_ind) const {if (abs_ind <= 0 || abs_ind >= column_indexes.size() + 1) return 0; return column_indexes[abs_ind - 1];} + QString & table_; + QList & column_props; + QVector & column_indexes; + QString & conn_name; +signals: + void updateTable(bool save_selection); + void tableChanged(); +}; + + + + +class SQLUniEdit: public QWidget { + Q_OBJECT +public: + explicit SQLUniEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent = 0); + ~SQLUniEdit() {for (int i = 0; i < w_.size(); ++i) deleteW(*w_[i]);} + void setProp(const ColumnProperties & p); + QString value(bool for_insert = false); + void clear() {setValue(QVariant());} +private: + void deleteW(QWidget * w) {if (w != 0) delete w;} + void resizeW(QWidget * w) {if (w != 0) w->setGeometry(QRect(0, 0, width(), height()));} + void resizeEvent(QResizeEvent * ) {for (int i = 0; i < w_.size(); ++i) resizeW(*w_[i]);} + bool eventFilter(QObject * o, QEvent * e); + QLineEdit * wtext; + QSpinBox * wint; + QDoubleSpinBox * wfloat; + QDateEdit * wdate; + QTimeEdit * wtime; + QDateTimeEdit * wdatetime; + QComboBox * wrelation; + QList w_; + ColumnProperties prop_; + QString connection_name; +private slots: + void value_text(QString value) {valueChanged(value);} + void value_int(int value) {valueChanged(value);} + void value_float(double value) {valueChanged(value);} + void value_date(QDate value) {valueChanged(value);} + void value_time(QTime value) {valueChanged(value);} + void value_datetime(QDateTime value) {valueChanged(value);} +public slots: + void setValue(const QVariant & value); + void updateRelation(); +signals: + void valueChanged(const QVariant & ); +}; + + + + +class SQLNewEdit: public QWidget { + Q_OBJECT + friend class SQLTableWidget; +public: + explicit SQLNewEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent = 0); + ~SQLNewEdit() {delete line; delete check;} + void setProp(const ColumnProperties & p); + bool isEnabled() const {return check->isChecked();} + QString value() const {return line->value(true);} + void clear() {line->clear();} +private: + ColumnProperties prop_; + SQLUniEdit * line; + QCheckBox * check; +}; + + + + +class SQLFilterEdit: public QWidget { + Q_OBJECT + friend class SQLTableWidget; +public: + explicit SQLFilterEdit(const ColumnProperties & prop, const QString & conn_name, QWidget * parent = 0); + ~SQLFilterEdit() {delete line; delete combo;} + void setProp(const ColumnProperties & p) {prop_ = p; line->setProp(p);} + QString filter() const; + bool isEmpty() const {return line->value().isEmpty() || combo->currentIndex() == 0;} + void clear(); +private: + ColumnProperties prop_; + SQLUniEdit * line; + QComboBox * combo; +private slots: + void value_changed() {if (combo->currentIndex() == 0) combo->setCurrentIndex(prop_.is_text ? 2 : 1);} +public slots: +signals: + void filterChanged(); +}; + + + + +class SQLItemDelegate: public QStyledItemDelegate { + Q_OBJECT +public: + explicit SQLItemDelegate(QList & cp, QVector & ci, bool & ro, const QString & conn_name, QObject * parent = 0); + ~SQLItemDelegate() {;} +private: + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const; + QString displayText(const QVariant & value, const QLocale & locale) const; + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + int mapColumn(int abs_ind) const {if (abs_ind <= 0 || abs_ind >= column_indexes.size() + 1) return 0; return column_indexes[abs_ind - 1];} + void setConnectionName(const QString & conn_name) {connection_name = conn_name;} + QList & column_props; + QVector & column_indexes; + bool & read_only; + QString connection_name; +private slots: +public slots: +signals: +}; + + + + +namespace Ui { + class SQLTableWidget; +}; + + +class SQLTableWidget: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString tableName READ tableName WRITE setTableName) + Q_PROPERTY(QString connectionName READ connectionName WRITE setConnectionName) + Q_PROPERTY(bool lineNewVisible READ lineNewVisible WRITE setLineNewVisible) + Q_PROPERTY(bool lineFilterVisible READ lineFilterVisible WRITE setLineFilterVisible) + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly) + friend class SQLItemDelegate; + friend class SQLUniEdit; +public: + SQLTableWidget(QWidget * parent = 0); + virtual ~SQLTableWidget(); + + bool isTableExists() const {return table_opened;} + const QString & tableName() const {return table_;} + + QTableView * tableView(); + bool readOnly() const {return read_only;} + + const ColumnProperties * columnProperty(int index) const {if (index < 0 || index >= column_props.count()) return 0; return &(column_props[index]);} + const ColumnProperties * columnProperty(const QString & name) const {return columnProperty(columnByName(name));} + const QList & columnProperties() const {return column_props;} + QStringList columnNames() const {QStringList sl; foreach (const ColumnProperties & i, column_props) sl << i.name; return sl;} + int columnsCount() const {return column_props.size();} + bool isColumnVisible(int ind) {if (!columnExists(ind)) return false; return column_props[ind].visible;} + bool isColumnVisible(const QString & name) {if (!columnExists(name)) return false; return column_props[columnByName(name)].visible;} + bool isColumnHidden(int ind) {if (!columnExists(ind)) return true; return !column_props[ind].visible;} + bool isColumnHidden(const QString & name) {if (!columnExists(name)) return true; return !column_props[columnByName(name)].visible;} + void setColumnVisible(int ind, bool visible); + void setColumnVisible(const QString & name, bool visible) {col_vis[name] = visible; setColumnVisible(columnByName(name), visible);} + void setColumnHidden(int ind, bool hidden) {setColumnVisible(ind, !hidden);} + void setColumnHidden(const QString & name, bool hidden) {col_vis[name] = !hidden; setColumnVisible(columnByName(name), !hidden);} + + bool lineNewVisible() const; + bool lineFilterVisible() const; + bool addRelation(const QString & this_column, const QString & other_table, const QString & other_key, const QString & other_column); + void addTranslation(const QString & file); + void addFixedColumnTranslation(const QString & col_name, const QString & col_tr, const QString & col_tt = QString()); + void fetchMore() {if (model) model->fetchMore();} + void fetchAll() {if (model) while (model->canFetchMore()) model->fetchMore();} + void selectId(int id); + + void setAdditionalActions(QList a); + + QSqlRecord headerRecord() const {if (model) return model->record(); return QSqlRecord();} + QSqlRecord currentRecord() const; + + void setCustomQuery(const QString & q); + void setCustomColumnNames(const QStringList & cn) {custom_col_names = cn; updateTable();} + + static bool isTableExists(const QString & table, const QString & conn_name = QLatin1String(QSqlDatabase::defaultConnection)) {return QSqlDatabase::database(conn_name).tables().contains(table, Qt::CaseInsensitive);} + + static bool executeScript(const QString & text, QSqlDatabase db = QSqlDatabase::database(), bool skip_errors = false, bool sqlite = false); + static bool executeScriptFile(const QString & file, QSqlDatabase db = QSqlDatabase::database(), bool skip_errors = false, bool sqlite = false); + + static bool isConnectedToDatabase(const QString & conn_name = QLatin1String(QSqlDatabase::defaultConnection)) {return QSqlDatabase::database(conn_name, false).isOpen();} + static bool connectToDatabase(const QString & config, const QString & conn_name = QLatin1String(QSqlDatabase::defaultConnection)); + + QString connectionName() const {return connection_name;} + +private: + static QString preprocessScript(QString text); + + bool eventFilter(QObject * o, QEvent * e); + void timerEvent(QTimerEvent * ); + void stopTimer() {if (timer != 0) killTimer(timer); timer = 0;} + bool checkTable(); + QStringList getTableColumns(const QString & t); + QString getColumnType(const QString & t, const QString & c); + static QList > getColumnValues(const QString & t, const QString & k, const QString & c, const QString & conn_name); + void setTableVisible(bool on); + int mapColumn(int abs_ind) const {if (abs_ind <= 0 || abs_ind >= column_indexes.size() + 1) return 0; return column_indexes[abs_ind - 1];} + int columnByName(const QString & name) const {for (int i = 0; i < column_props.size(); ++i) if (column_props[i].name == name) return i; return -1;} + bool columnExists(int index) const {return (index >= 0 && index < column_props.size());} + bool columnExists(const QString & name) const {for (int i = 0; i < column_props.size(); ++i) if (column_props[i].name == name) return true; return false;} + QPair trColumn(const QString & n); + QString tableNames(); + QString columnNames(); + QString columnFilters(); + QString columnRelations(); + + Ui::SQLTableWidget * ui; + QString table_, cquery, custom_query; + QStringList custom_col_names; + SQLQueryModel * model; + //QAction act_add, act_del; + QList column_props; + QList column_news; + QList column_filters; + QVector column_indexes; + QStringList wait_rels; + QMenu popup_menu, popup_col, popup_filter; + QTime tm; + QActionGroup filters_group; + QAction * action_del; + QList add_actions; + QMap > translates, fixed_translates; + QMap col_vis; + int timer; + bool filters_active, table_opened, read_only, first_update; + static QString _dir; + QPoint pp; + QString connection_name; + + +public slots: + void setTableName(const QString & t); + void setLineNewVisible(bool on); + void setLineFilterVisible(bool on); + void setReadOnly(bool yes); + void updateTable(bool save_selection = false); + void setConnectionName(const QString &conn_name); + +private slots: + void on_buttonAdd_clicked(); + void on_actionFiltersClear_triggered(); + void on_view_clicked(const QModelIndex & index); + void header_sectionResized(int logicalIndex, int oldSize, int newSize); + void header_sortIndicatorChanged(int logicalIndex, Qt::SortOrder order); + void scrolled(int value); + void del_triggered(); + void column_triggered(bool on); + +signals: + void rowClicked(int row); + void recordClicked(QSqlRecord rec); + void selectionChanged(); + void tableChanged(); + +}; + + +#endif // MYSQLTABLE_H diff --git a/test/qad/sql_table/sql_table_widget.ui b/test/qad/sql_table/sql_table_widget.ui new file mode 100644 index 0000000..fba0fc1 --- /dev/null +++ b/test/qad/sql_table/sql_table_widget.ui @@ -0,0 +1,369 @@ + + + SQLTableWidget + + + + 0 + 0 + 445 + 570 + + + + SQLTableWidget + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Table doesn`t exists! + + + Qt::AlignCenter + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16 + 0 + + + + :/icons/edit-table-insert-row-below.png + + + true + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 352 + 154 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 1 + 10 + + + + + + + + + + + + + + + Add record + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 16 + 0 + + + + :/icons/edit-find.png + + + true + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 154 + 256 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 1 + 10 + + + + + + + + + + + + + + + Configure filters + + + + :/icons/configure.png:/icons/configure.png + + + QToolButton::InstantPopup + + + + + + + true + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + true + + + + + + + + + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + Clear + + + + + true + + + Operator AND + + + Operator AND + + + + + true + + + Operator OR + + + Operator OR + + + + + + + + diff --git a/test/qad/touch_widgets/CMakeLists.txt b/test/qad/touch_widgets/CMakeLists.txt new file mode 100644 index 0000000..1140e08 --- /dev/null +++ b/test/qad/touch_widgets/CMakeLists.txt @@ -0,0 +1 @@ +qad_project(touch_widgets "Gui;Widgets" "") diff --git a/test/qad/touch_widgets/plugin/CMakeLists.txt b/test/qad/touch_widgets/plugin/CMakeLists.txt new file mode 100644 index 0000000..558030d --- /dev/null +++ b/test/qad/touch_widgets/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(touch_widgets "Gui;Widgets" "") diff --git a/test/qad/touch_widgets/plugin/qad_touch_widgets.cpp b/test/qad/touch_widgets/plugin/qad_touch_widgets.cpp new file mode 100644 index 0000000..caf2bbe --- /dev/null +++ b/test/qad/touch_widgets/plugin/qad_touch_widgets.cpp @@ -0,0 +1,21 @@ +#include "qad_touch_widgets.h" +#include "touchsliderplugin.h" +#include "touchbuttframeplugin.h" +#include "touchbuttonplugin.h" + + +QADTouchWidgets::QADTouchWidgets(QObject * parent): QObject(parent) { + m_widgets.append(new TouchSliderPlugin(this)); + m_widgets.append(new TouchButtFramePlugin(this)); + m_widgets.append(new TouchButtonPlugin(this)); +} + + +QList QADTouchWidgets::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_touch_widgets_plugin, QADTouchWidgets) +#endif diff --git a/test/qad/touch_widgets/plugin/qad_touch_widgets.h b/test/qad/touch_widgets/plugin/qad_touch_widgets.h new file mode 100644 index 0000000..e7cbf2a --- /dev/null +++ b/test/qad/touch_widgets/plugin/qad_touch_widgets.h @@ -0,0 +1,23 @@ +#ifndef QAD_TOUCH_WIDGETS_H +#define QAD_TOUCH_WIDGETS_H + +#include +#include + +class QADTouchWidgets: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.touch_widgets") +#endif +public: + explicit QADTouchWidgets(QObject *parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_TOUCH_WIDGETS_H diff --git a/test/qad/touch_widgets/plugin/touchbuttframeplugin.cpp b/test/qad/touch_widgets/plugin/touchbuttframeplugin.cpp new file mode 100644 index 0000000..dee389b --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchbuttframeplugin.cpp @@ -0,0 +1,90 @@ +#include "touchbuttframe.h" +#include "touchbuttframeplugin.h" + +#include +#include +#include + + +TouchButtFramePlugin::TouchButtFramePlugin(QObject *parent) + : QObject(parent) +{ + m_initialized = false; +} + +void TouchButtFramePlugin::initialize(QDesignerFormEditorInterface * /*core*/ ) +{ + if (m_initialized) + return; + + // Add extension registrations, etc. here + //core->propertyEditor()->property(); + m_initialized = true; +} + +bool TouchButtFramePlugin::isInitialized() const +{ + return m_initialized; +} + +QWidget *TouchButtFramePlugin::createWidget(QWidget *parent) +{ + TouchButtFrame * tbw = new TouchButtFrame(parent); + QStringList l; + l.append("First"); + l.append("Second"); + tbw->setButtons(l); + return tbw; +} + +QString TouchButtFramePlugin::name() const +{ + return QLatin1String("TouchButtFrame"); +} + +QString TouchButtFramePlugin::group() const +{ + return QLatin1String("Touch Widgets"); +} + +QIcon TouchButtFramePlugin::icon() const +{ + return QIcon(":/icons/touchbuttframe.png"); +} + +QString TouchButtFramePlugin::toolTip() const +{ + return QLatin1String(""); +} + +QString TouchButtFramePlugin::whatsThis() const +{ + return QLatin1String(""); +} + +bool TouchButtFramePlugin::isContainer() const +{ + return false; +} + +QString TouchButtFramePlugin::domXml() const +{ + return QLatin1String("\n" + " \n" + " \n" + " \n" + " 0\n" + " 0\n" + " 100\n" + " 100\n" + " \n" + " \n" + " \n" + "\n"); +} + +QString TouchButtFramePlugin::includeFile() const +{ + return QLatin1String("touchbuttframe.h"); +} + diff --git a/test/qad/touch_widgets/plugin/touchbuttframeplugin.h b/test/qad/touch_widgets/plugin/touchbuttframeplugin.h new file mode 100644 index 0000000..a4314b8 --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchbuttframeplugin.h @@ -0,0 +1,35 @@ +#ifndef TOUCHBUTTFRAMEPLUGIN_H +#define TOUCHBUTTFRAMEPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class TouchButtFramePlugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + TouchButtFramePlugin(QObject *parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); + +private: + bool m_initialized; +}; + +#endif diff --git a/test/qad/touch_widgets/plugin/touchbuttonplugin.cpp b/test/qad/touch_widgets/plugin/touchbuttonplugin.cpp new file mode 100644 index 0000000..9d977a0 --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchbuttonplugin.cpp @@ -0,0 +1,74 @@ +#include "touchbutton.h" +#include "touchbuttonplugin.h" + +#include +#include +#include + + +TouchButtonPlugin::TouchButtonPlugin(QObject *parent) + : QObject(parent) +{ + m_initialized = false; +} + +void TouchButtonPlugin::initialize(QDesignerFormEditorInterface * /* core */) +{ + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + +bool TouchButtonPlugin::isInitialized() const +{ + return m_initialized; +} + +QWidget *TouchButtonPlugin::createWidget(QWidget *parent) +{ + return new TouchButton(parent); +} + +QString TouchButtonPlugin::name() const +{ + return QLatin1String("TouchButton"); +} + +QString TouchButtonPlugin::group() const +{ + return QLatin1String("Touch Widgets"); +} + +QIcon TouchButtonPlugin::icon() const +{ + return QIcon(":/icons/touchbutton.png"); +} + +QString TouchButtonPlugin::toolTip() const +{ + return QLatin1String(""); +} + +QString TouchButtonPlugin::whatsThis() const +{ + return QLatin1String(""); +} + +bool TouchButtonPlugin::isContainer() const +{ + return false; +} + +QString TouchButtonPlugin::domXml() const +{ + return QLatin1String("\n\n"); +} + +QString TouchButtonPlugin::includeFile() const +{ + return QLatin1String("touchbutton.h"); +} + diff --git a/test/qad/touch_widgets/plugin/touchbuttonplugin.h b/test/qad/touch_widgets/plugin/touchbuttonplugin.h new file mode 100644 index 0000000..423c164 --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchbuttonplugin.h @@ -0,0 +1,35 @@ +#ifndef TOUCHBUTTONPLUGIN_H +#define TOUCHBUTTONPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class TouchButtonPlugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + TouchButtonPlugin(QObject *parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); + +private: + bool m_initialized; +}; + +#endif // TOUCHBUTTONPLUGIN_H diff --git a/test/qad/touch_widgets/plugin/touchsliderplugin.cpp b/test/qad/touch_widgets/plugin/touchsliderplugin.cpp new file mode 100644 index 0000000..c49f321 --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchsliderplugin.cpp @@ -0,0 +1,81 @@ +#include "touchslider.h" +#include "touchsliderplugin.h" + +#include +#include +#include + + +TouchSliderPlugin::TouchSliderPlugin(QObject *parent) + : QObject(parent) +{ + m_initialized = false; +} + +void TouchSliderPlugin::initialize(QDesignerFormEditorInterface * /* core */) +{ + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + +bool TouchSliderPlugin::isInitialized() const +{ + return m_initialized; +} + +QWidget *TouchSliderPlugin::createWidget(QWidget *parent) +{ + TouchSlider * ts = new TouchSlider(parent); + ts->setMinimum(-5.); + ts->setMaximum(5.); + ts->setPrecision(0.2); + ts->setValue(2.2); + ts->setPrefix("Distanse"); + ts->setSuffix("meters"); + return ts; +} + +QString TouchSliderPlugin::name() const +{ + return QLatin1String("TouchSlider"); +} + +QString TouchSliderPlugin::group() const +{ + return QLatin1String("Touch Widgets"); +} + +QIcon TouchSliderPlugin::icon() const +{ + return QIcon(":/icons/touchslider.png"); +} + +QString TouchSliderPlugin::toolTip() const +{ + return QLatin1String(""); +} + +QString TouchSliderPlugin::whatsThis() const +{ + return QLatin1String(""); +} + +bool TouchSliderPlugin::isContainer() const +{ + return false; +} + +QString TouchSliderPlugin::domXml() const +{ + return QLatin1String("\n\n"); +} + +QString TouchSliderPlugin::includeFile() const +{ + return QLatin1String("touchslider.h"); +} + diff --git a/test/qad/touch_widgets/plugin/touchsliderplugin.h b/test/qad/touch_widgets/plugin/touchsliderplugin.h new file mode 100644 index 0000000..3ceaa34 --- /dev/null +++ b/test/qad/touch_widgets/plugin/touchsliderplugin.h @@ -0,0 +1,35 @@ +#ifndef TOUCHSLIDERPLUGIN_H +#define TOUCHSLIDERPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class TouchSliderPlugin : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + TouchSliderPlugin(QObject *parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); + +private: + bool m_initialized; +}; + +#endif diff --git a/test/qad/touch_widgets/touch_bar.h b/test/qad/touch_widgets/touch_bar.h new file mode 100644 index 0000000..ab14b9c --- /dev/null +++ b/test/qad/touch_widgets/touch_bar.h @@ -0,0 +1,17 @@ +#ifndef TOUCH_BAR_H +#define TOUCH_BAR_H + +#include + +class touch_bar: public QProgressBar { + Q_OBJECT +public: + touch_bar(QWidget * parent = 0): QProgressBar(parent) + { + //setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + } +signals: + void mouseMoveEvent(QMouseEvent * e); + void mousePressEvent(QMouseEvent * e); +}; +#endif // TOUCH_BAR_H diff --git a/test/qad/touch_widgets/touch_widgets.qrc b/test/qad/touch_widgets/touch_widgets.qrc new file mode 100644 index 0000000..cfb77f1 --- /dev/null +++ b/test/qad/touch_widgets/touch_widgets.qrc @@ -0,0 +1,7 @@ + + + ../icons/touchbuttframe.png + ../icons/touchbutton.png + ../icons/touchslider.png + + diff --git a/test/qad/touch_widgets/touchbuttframe.cpp b/test/qad/touch_widgets/touchbuttframe.cpp new file mode 100644 index 0000000..5f5b0fa --- /dev/null +++ b/test/qad/touch_widgets/touchbuttframe.cpp @@ -0,0 +1,84 @@ +#include "touchbuttframe.h" + + +TouchButtFrame::TouchButtFrame(QWidget * parent, Qt::Orientation orientation): QFrame(parent) { + id_click = id_set = -1; + TouchButton b; + colr = b.noColor(); + colg = b.yesColor(); + colw = b.grayColor(); + colp = b.downColor(); + setFrameShape(QFrame::StyledPanel); + lay = new QBoxLayout(QBoxLayout::LeftToRight); + lay->setContentsMargins(0, 0, 0, 0); + if (orientation == Qt::Vertical) lay->setDirection(QBoxLayout::TopToBottom); +} + + +void TouchButtFrame::addButton(const QString & caption) { + TouchButton * butt = new TouchButton(lay->count(), caption); + butt->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + butt->setCheckable(true); + butt->setAutoExclusive(true); + //butt->setAutoFillBackground(true); + lay->addWidget(butt); + this->setLayout(lay); + connect(butt, SIGNAL(clickedID(int)), SLOT(butt_click(int))); + connect(butt, SIGNAL(toggledID(int, bool)), SLOT(butt_toggle(int, bool))); +} + + +void TouchButtFrame::resetColors() { + for (int i = 0; i < lay->count(); ++i) { + button(i)->setYesColor(colg); + button(i)->setNoColor(colr); + button(i)->setGrayColor(colw); + button(i)->setDownColor(colp); + } +} + + +void TouchButtFrame::deleteButtons() { + while (!lay->isEmpty()) + delete button(0); +} + + +void TouchButtFrame::setButtons(const QStringList & captions) { + int cur = currentButton(); + deleteButtons(); + for (int i = 0; i < captions.size(); ++i) + addButton(captions.at(i)); + if (button(cur) != 0) button(cur)->setChecked(true); + else if (!captions.isEmpty()) button(0)->setChecked(true); +} + + +void TouchButtFrame::setCurrentButton(int index) { + if (button(index) != 0) { + if (currentButton() != -1) button(currentButton())->setChecked(false); + button(index)->setChecked(true); + } +} + + +int TouchButtFrame::currentButton() { + for (int i = 0; i < buttonsCount(); ++i) + if (button(i)->isChecked()) return i; + return -1; +} + + +QStringList TouchButtFrame::buttons() { + QStringList l; + for (int i = 0; i < lay->count(); ++i) + l.append(button(i)->text()); + return l; +} + + +TouchButton * TouchButtFrame::button(int index) { + if (index >= 0 && index < lay->count()) + return (qobject_cast(lay->itemAt(index)->widget())); + return 0; +} diff --git a/test/qad/touch_widgets/touchbuttframe.h b/test/qad/touch_widgets/touchbuttframe.h new file mode 100644 index 0000000..1d695d8 --- /dev/null +++ b/test/qad/touch_widgets/touchbuttframe.h @@ -0,0 +1,113 @@ +#ifndef TOUCHBUTTFRAME_H +#define TOUCHBUTTFRAME_H + +#include +#include +#include +#include "touchbutton.h" + + +class TouchButtFrame: public QFrame +{ + Q_OBJECT + Q_PROPERTY(QColor colorYes READ colorYes WRITE setColorYes) + Q_PROPERTY(QColor colorNo READ colorNo WRITE setColorNo) + Q_PROPERTY(QColor colorGray READ colorGray WRITE setColorGray) + Q_PROPERTY(QColor colorDown READ colorDown WRITE setColorDown) + Q_PROPERTY(QStringList buttons READ buttons WRITE setButtons) + Q_PROPERTY(int currentButton READ currentButton WRITE setCurrentButton) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(int clickID READ clickID WRITE setClickID) + Q_PROPERTY(int setID READ setID WRITE setSetID) + +public: + TouchButtFrame(QWidget * parent = 0, Qt::Orientation orientation = Qt::Horizontal); + + int clickID() const {return id_click;} + int setID() const {return id_set;} + + void setButtons(const QStringList & captions); + QStringList buttons(); + TouchButton * button(int index); + int buttonsCount() const {return lay->count();} + int currentButton(); + Qt::Orientation orientation() const {if (lay->direction() == 0) return Qt::Horizontal; else return Qt::Vertical;} + QColor colorYes() const {return colg;} + QColor colorNo() const {return colr;} + QColor colorGray() const {return colw;} + QColor colorDown() const {return colp;} + + void setCurrentButton(int index); + void setColorYes(QColor col) {colg = col; resetColors();} + void setColorNo(QColor col) {colr = col; resetColors();} + void setColorGray(QColor col) {colw = col; resetColors();} + void setColorDown(QColor col) {colp = col; resetColors();} + void setOrientation(Qt::Orientation orientation) {if (orientation == Qt::Horizontal) lay->setDirection(QBoxLayout::LeftToRight); else lay->setDirection(QBoxLayout::TopToBottom);} + +private: + void resetColors(); + void deleteButtons(); + void addButton(const QString & caption); + + int id_click, id_set; + QColor colr, colg, colw, colp; + QBoxLayout * lay; + //int count; + +private slots: + void butt_click(int index) {emit clicked(index); emit clickedID(id_click, index);} + void butt_toggle(int index, bool checked) {emit toggled(index, checked); emit toggledID(id_click, index, checked);} + +public slots: + void enable() {setEnabled(true);} + void disable() {setAllButtonsGray(); setEnabled(false);} + + void setClickID(int id) {id_click = id;} + void setSetID(int id) {id_set = id;} + + + void setButtonChecked(int index) {if (button(index) != 0) button(index)->setChecked(true);} + void setButtonUnchecked(int index) {if (button(index) != 0) button(index)->setChecked(false);} + void setButtonState(int index, TouchButton::State state) {if (button(index) != 0) button(index)->setState(state);} + void setButtonYes(int index) {if (button(index) != 0) button(index)->setStateYes();} + void setButtonNo(int index) {if (button(index) != 0) button(index)->setStateNo();} + void setButtonGray(int index) {if (button(index) != 0) button(index)->setStateGray();} + void setAllButtonsState(TouchButton::State state) {for (int i = 0; i < buttonsCount(); ++i) button(i)->setState(state);} + void setAllButtonsYes() {for (int i = 0; i < buttonsCount(); ++i) button(i)->setStateYes();} + void setAllButtonsNo() {for (int i = 0; i < buttonsCount(); ++i) button(i)->setStateNo();} + void setAllButtonsGray() {for (int i = 0; i < buttonsCount(); ++i) button(i)->setStateGray();} + + void hideButton(int index) {if (button(index) != 0) button(index)->hide();} + void showButton(int index) {if (button(index) != 0) button(index)->show();} + void enableButton(int index) {if (button(index) != 0) button(index)->enable();} + void disableButton(int index) {if (button(index) != 0) button(index)->disable();} + + + void enableID(int set_id) {if (set_id == id_set) enable();} + void disableID(int set_id) {if (set_id == id_set) disable();} + + void setButtonCheckedID(int set_id, int index) {if (set_id == id_set) setButtonChecked(index);} + void setButtonUncheckedID(int set_id, int index) {if (set_id == id_set) setButtonUnchecked(index);} + void setButtonStateID(int set_id, int index, TouchButton::State state) {if (set_id == id_set) setButtonState(index, state);} + void setButtonYesID(int set_id, int index) {if (set_id == id_set) setButtonYes(index);} + void setButtonNoID(int set_id, int index) {if (set_id == id_set) setButtonNo(index);} + void setButtonGrayID(int set_id, int index) {if (set_id == id_set) setButtonGray(index);} + void setAllButtonsStateID(int set_id, TouchButton::State state) {if (set_id == id_set) setAllButtonsState(state);} + void setAllButtonsYesID(int set_id) {if (set_id == id_set) setAllButtonsYes();} + void setAllButtonsNoID(int set_id) {if (set_id == id_set) setAllButtonsNo();} + void setAllButtonsGrayID(int set_id) {if (set_id == id_set) setAllButtonsGray();} + + void hideButtonID(int set_id, int index) {if (set_id == id_set) hideButton(index);} + void showButtonID(int set_id, int index) {if (set_id == id_set) showButton(index);} + void enableButtonID(int set_id, int index) {if (set_id == id_set) enableButton(index);} + void disableButtonID(int set_id, int index) {if (set_id == id_set) disableButton(index);} + +signals: + void clicked(int index); + void toggled(int index, bool checked); + void clickedID(int id, int index); + void toggledID(int id, int index, bool checked); + +}; + +#endif // TOUCHBUTTFRAME_H diff --git a/test/qad/touch_widgets/touchbutton.cpp b/test/qad/touch_widgets/touchbutton.cpp new file mode 100644 index 0000000..7da1be1 --- /dev/null +++ b/test/qad/touch_widgets/touchbutton.cpp @@ -0,0 +1,104 @@ +#include "touchbutton.h" + + +TouchButton::TouchButton(QWidget * parent): QToolButton(parent) { + init(); +} + + +TouchButton::TouchButton(int id__, const QString & text): QToolButton(0) { + init(); + id_click = id_yes = id_no = id__; + setText(text); +} + + +TouchButton::~TouchButton() { +} + + +void TouchButton::init() { + connect(this, SIGNAL(clicked(bool)), this, SLOT(_clicked())); + connect(this, SIGNAL(toggled(bool)), this, SLOT(_toggled(bool))); + animation.setTargetObject(this); + animation.setPropertyName("currentColor"); + animation.setEasingCurve(QEasingCurve::OutSine); + animation.setDuration(350); + delay_blink = 500; + anim = auto_gray = true; + state_ = Gray; + id_click = id_yes = id_no = -1; + timer = 0; + pal = palette(); + col_yes = QColor(Qt::green); + col_no = QColor(Qt::red); + col_down = QColor(Qt::yellow); + col_gray = col_cur = col_up = pal.button().color(); + col_dst = col_gray; +} + + +void TouchButton::timerEvent(QTimerEvent * e) { + if (e->timerId() == timer) { + killTimer(e->timerId()); + timer = 0; + setCurrentColor(col_blink); + animateColor(col_dst); + return; + } + QToolButton::timerEvent(e); +} + + +void TouchButton::mousePressEvent(QMouseEvent * e) { + animateColor(col_down); + QToolButton::mousePressEvent(e); + emit pressedID(id_click); +} + + +void TouchButton::mouseReleaseEvent(QMouseEvent * e) { + setCurrentColor(col_down); + animateColor(col_dst); + QToolButton::mouseReleaseEvent(e); + emit releasedID(id_click); +} + + +void TouchButton::animateColor(const QColor & tc) { + if (anim) { + animation.stop(); + animation.setStartValue(currentColor()); + animation.setEndValue(tc); + animation.start(); + } else { + col_cur = tc; + applyColor(); + } +} + + +void TouchButton::applyState(State s) { + if (s == state_) return; + State ps = state_; + state_ = s; + emit stateChanged(state_, ps); + emit stateChangedID(id_click, state_, ps); +} + + +void TouchButton::blink(const QColor & tc) { + col_blink = tc; + if (timer > 0) killTimer(timer); + timer = startTimer(delay_blink); +} + + +void TouchButton::setState(State s) { + switch (s) { + case Gray: setStateGray(); break; + case Yes: setStateYes(); break; + case No: setStateNo(); break; + default: break; + }; +} diff --git a/test/qad/touch_widgets/touchbutton.h b/test/qad/touch_widgets/touchbutton.h new file mode 100644 index 0000000..0733b1c --- /dev/null +++ b/test/qad/touch_widgets/touchbutton.h @@ -0,0 +1,154 @@ +#ifndef TOUCHBUTTON_H +#define TOUCHBUTTON_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class TouchButton: public QToolButton +{ + Q_OBJECT + Q_ENUMS(State) + Q_PROPERTY(QColor downColor READ downColor WRITE setDownColor) + Q_PROPERTY(QColor upColor READ upColor WRITE setUpColor) + Q_PROPERTY(QColor grayColor READ grayColor WRITE setGrayColor) + Q_PROPERTY(QColor yesColor READ yesColor WRITE setYesColor) + Q_PROPERTY(QColor noColor READ noColor WRITE setNoColor) + Q_PROPERTY(QColor currentColor READ currentColor WRITE setCurrentColor DESIGNABLE false) + Q_PROPERTY(State state READ state WRITE setState) + Q_PROPERTY(bool autoGray READ autoGray WRITE setAutoGray) + Q_PROPERTY(bool animated READ animated WRITE setAnimated) + Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration) + Q_PROPERTY(QEasingCurve::Type animationCurve READ animationCurve WRITE setAnimationCurve) + Q_PROPERTY(int blinkDelay READ blinkDelay WRITE setBlinkDelay) + Q_PROPERTY(int clickID READ clickID WRITE setClickID) + Q_PROPERTY(int yesID READ yesID WRITE setYesID) + Q_PROPERTY(int noID READ noID WRITE setNoID) + +public: + explicit TouchButton(QWidget * parent = 0); + TouchButton(int id, const QString & text = QString()); + ~TouchButton(); + + enum State {Gray, Yes, No}; + + int clickID() const {return id_click;} + int yesID() const {return id_yes;} + int noID() const {return id_no;} + + QColor downColor() const {return col_down;} + QColor upColor() const {return col_up;} + QColor grayColor() const {return col_gray;} + QColor yesColor() const {return col_yes;} + QColor noColor() const {return col_no;} + QColor currentColor() const {return col_cur;} + State state() const {return state_;} + bool autoGray() const {return auto_gray;} + bool animated() const {return anim;} + int animationDuration() const {return animation.duration();} + QEasingCurve::Type animationCurve() const {return animation.easingCurve().type();} + int blinkDelay() const {return delay_blink;} + + void blink(const QColor & tc); + +private: + void timerEvent(QTimerEvent * e); + void mousePressEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent * e); + + void init(); + void animateColor(const QColor & tc); + void applyColor() {pal.setColor(QPalette::Button, col_cur); setPalette(pal);} + void applyState(State s); + + QPalette pal; + QColor col_down, col_up, col_gray, col_yes, col_no, col_cur, col_dst, col_blink; + QPropertyAnimation animation; + State state_; + int id_click, id_yes, id_no, delay_blink, timer; + bool anim, auto_gray; + +private slots: + void _clicked() {emit clickedID(id_click);} + void _toggled(bool on) {if (!on && auto_gray) setStateGray(); emit toggledID(id_click, on);} + +public slots: + void enable() {setEnabled(true);} + void disable() {setStateGray(); setEnabled(false);} + + void setClickID(int id) {id_click = id;} + void setYesID(int id) {id_yes = id;} + void setNoID(int id) {id_no = id;} + + void setDownColor(const QColor & col) {col_down = col;} + void setUpColor(const QColor & col) {col_up = col;} + void setGrayColor(const QColor & col) {col_gray = col;} + void setYesColor(const QColor & col) {col_yes = col;} + void setNoColor(const QColor & col) {col_no = col;} + + + void setState(State s); + void setStateGray() {col_dst = col_gray; applyState(Gray); animateColor(col_dst);} + void setStateYes() {col_dst = col_yes; applyState(Yes); animateColor(col_dst);} + void setStateNo() {col_dst = col_no; applyState(No); animateColor(col_dst);} + void setStateYesOrNo(bool yes) {if (yes) setStateYes(); else setStateNo();} + void setStateYesOrGray(bool yes) {if (yes) setStateYes(); else setStateGray();} + void setStateNoOrYes(bool no) {if (no) setStateNo(); else setStateYes();} + void setStateNoOrGray(bool no) {if (no) setStateNo(); else setStateGray();} + + void blinkYes() {blink(col_yes);} + void blinkNo() {blink(col_no);} + void blinkYesOrNo(bool yes) {blink(yes ? col_yes : col_no);} + void blinkNoOrYes(bool no) {blink(no ? col_no : col_yes);} + + + void enableID(int yes_id) {if (yes_id == id_yes) enable();} + void disableID(int yes_id) {if (yes_id == id_yes) disable();} + + void setStateID(int yes_id, State s) {if (yes_id == id_yes) setState(s);} + void setStateGrayID(int yes_id) {if (yes_id == id_yes) setStateGray();} + void setStateYesID(int yes_id) {if (yes_id == id_yes) setStateYes();} + void setStateNoID(int no_id) {if (no_id == id_no) setStateNo();} + void setStateYesOrNoID(int yes_id, bool yes) {if (yes_id == id_yes) setStateYesOrNo(yes);} + void setStateYesOrGrayID(int yes_id, bool yes) {if (yes_id == id_yes) setStateYesOrGray(yes);} + void setStateNoOrYesID(int no_id, bool no) {if (no_id == id_no) setStateNoOrYes(no);} + void setStateNoOrGrayID(int no_id, bool no) {if (no_id == id_no) setStateNoOrGray(no);} + + void blinkYesID(int yes_id) {if (yes_id == id_yes) blinkYes();} + void blinkNoID(int no_id) {if (no_id == id_no) blinkNo();} + void blinkYesOrNoID(int yes_id, bool yes) {if (yes_id == id_yes) blinkYesOrNo(yes);} + void blinkNoOrYesID(int no_id, bool no) {if (no_id == id_no) blinkNoOrYes(no);} + + + void setColor(const QColor & col) {col_dst = col; animateColor(col_dst);} + void setCurrentColor(const QColor & col) {col_cur = col; applyColor();} + + void setAutoGray(bool yes) {auto_gray = yes;} + void setAnimated(bool yes) {anim = yes;} + void setAnimationDuration(int dur) {animation.setDuration(dur);} + void setAnimationCurve(QEasingCurve::Type curve) {animation.setEasingCurve(QEasingCurve(curve));} + void setBlinkDelay(int dur) {delay_blink = dur;} + + void delayedClick() {QMetaObject::invokeMethod(this, "click", Qt::QueuedConnection);} + void delayedToggle() {QMetaObject::invokeMethod(this, "toggle", Qt::QueuedConnection);} + + void delayedClickID(int yes_id) {if (yes_id == id_yes) delayedClick();} + void delayedToggleID(int yes_id) {if (yes_id == id_yes) delayedToggle();} + +signals: + void clickedID(int id); + void pressedID(int id); + void releasedID(int id); + void toggledID(int id, bool on); + void stateChanged(State state, State prev_state); + void stateChangedID(int id, State state, State prev_state); + +}; + +QT_END_NAMESPACE + +#endif // TOUCHBUTTON_H diff --git a/test/qad/touch_widgets/touchslider.cpp b/test/qad/touch_widgets/touchslider.cpp new file mode 100644 index 0000000..515acdc --- /dev/null +++ b/test/qad/touch_widgets/touchslider.cpp @@ -0,0 +1,242 @@ +#include "touchslider.h" +#include "ui_touchslider.h" + + +TouchSlider::TouchSlider(QWidget * parent): QGroupBox(parent), ui(new Ui::TouchSlider) { + ui->setupUi(this); + ui->barNeg->setMinimum(0); + prec = 1; + id_click = id_set = -1; + hasZero = true; + m_readOnly = false; + m_showMinMax = false; +} + + +TouchSlider::~TouchSlider() { + delete ui; +} + + +void TouchSlider::on_pbMin_clicked() { + if (m_readOnly) return; + ui->barNeg->setValue(ui->barNeg->maximum()); + ui->barPos->setValue(ui->barPos->minimum()); +} + + +void TouchSlider::on_pbMax_clicked() { + if (m_readOnly) return; + ui->barNeg->setValue(ui->barNeg->minimum()); + ui->barPos->setValue(ui->barPos->maximum()); +} + + +void TouchSlider::on_pbInc_clicked() { + if (ui->barPos->value() >= 0 && ui->barNeg->value() == 0) ui->barPos->setValue(ui->barPos->value() + 1); + if (ui->barNeg->value() > 0) ui->barNeg->setValue(ui->barNeg->value() - 1); +} + + +void TouchSlider::on_pbDec_clicked() { + if (ui->barNeg->value() >= 0 && ui->barPos->value() == 0 && hasZero) ui->barNeg->setValue(ui->barNeg->value() + 1); + if (ui->barPos->value() > 0) ui->barPos->setValue(ui->barPos->value() - 1); +} + + +void TouchSlider::on_pbZero_clicked() { + ui->barNeg->setValue(ui->barNeg->minimum()); + ui->barPos->setValue(ui->barPos->minimum()); +} + + +void TouchSlider::setValue(double val) { + if (val < minimum()) val = minimum(); + if (val > maximum()) val = maximum(); + if (val < 0) { + ui->barPos->setValue(ui->barPos->minimum()); + if (hasZero) ui->barNeg->setValue(qRound(-val / prec)); + } else { + ui->barPos->setValue(qRound(val / prec)); + ui->barNeg->setValue(ui->barNeg->minimum()); + } + updateCaption(); +} + + +double TouchSlider::value() const { + if (ui->barNeg->value() <= ui->barNeg->minimum()) + return (double)(ui->barPos->value()) * prec; + else return -(double)(ui->barNeg->value()) * prec; +} + + +void TouchSlider::setMaximum(double max) { + double val = value(); + if (max < 0) return; + ui->barPos->setMaximum(qRound(max / prec)); + setValue(val); + ui->pbMax->setText(QString::number(maximum())); +} + + +double TouchSlider::maximum() const { + return (double)(ui->barPos->maximum()) * prec; +} + + +void TouchSlider::setMinimum(double min) { + double val = value(); + if (min < 0) { + hasZero = true; + ui->barNeg->setMaximum(qRound(-min / prec)); + ui->barPos->setMinimum(0); + ui->barNeg->setVisible(true); + ui->pbZero->setVisible(true); + setValue(val); + } else { + hasZero = false; + ui->barNeg->setMaximum(0); + ui->barNeg->setVisible(false); + ui->pbZero->setVisible(false); + ui->barPos->setMinimum(qRound(min / prec)); + setValue(val); + } + ui->pbMin->setText(QString::number(minimum())); +} + + +double TouchSlider::minimum() const { + if (hasZero) return -(double)(ui->barNeg->maximum()) * prec; + return (double)(ui->barPos->minimum()) * prec; +} + + +void TouchSlider::setPrecision(double precision) { + double min = minimum(); + double max = maximum(); + double val = value(); + prec = precision; + setMinimum(min); + setMaximum(max); + setValue(val); + ui->pbInc->setText("+" + QString::number(precision)); + ui->pbDec->setText("-" + QString::number(precision)); +} + + +double TouchSlider::precision() const { + return prec; +} + + +void TouchSlider::updateCaption() { + setTitle(pref + " " + QString::number(value()) + " " + suff); + emit valueChanged(value()); + emit valueChanged((int)value()); + emit valueChangedID(id_click, value()); + emit valueChangedID(id_click, (int)value()); +} + + +void TouchSlider::on_barPos_mouseMoveEvent(QMouseEvent * e) { + if (m_readOnly) return; + int tx = e->x(); + if (tx > ui->barPos->width()) tx = ui->barPos->width(); + if (tx < 0) { + if (hasZero) { + int nx = tx + ui->barNeg->width() + layout()->spacing(); + if (nx < ui->barNeg->width()) { + QMouseEvent * event = new QMouseEvent(QMouseEvent::MouseMove, QPoint(nx, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + on_barNeg_mouseMoveEvent(event); + return; + } + } + tx = 0; + } + ui->barPos->setValue(ui->barPos->minimum() + qRound((double)(tx * (ui->barPos->maximum() - ui->barPos->minimum())) / (double)(ui->barPos->width()))); + ui->barNeg->setValue(ui->barNeg->minimum()); +} + + +void TouchSlider::on_barNeg_mouseMoveEvent(QMouseEvent * e) { + if (m_readOnly) return; + int tx = e->x(); + if (tx < 0) tx = 0; + if (tx > ui->barNeg->width()) { + int nx = tx - ui->barPos->width() - layout()->spacing(); + if (nx > 0) { + QMouseEvent * event = new QMouseEvent(QMouseEvent::MouseMove, QPoint(nx, 0), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + on_barPos_mouseMoveEvent(event); + return; + } + tx = ui->barNeg->width(); + } + ui->barNeg->setValue(ui->barNeg->maximum() - qRound((double)(tx * ui->barNeg->maximum()) / (double)(ui->barNeg->width()))); + ui->barPos->setValue(ui->barPos->minimum()); +} + + +void TouchSlider::on_barPos_mousePressEvent(QMouseEvent * e) { + if (m_readOnly) return; + ui->barPos->setValue(ui->barPos->minimum() + qRound((double)(e->x() * (ui->barPos->maximum() - ui->barPos->minimum())) / (double)(ui->barPos->width()))); + ui->barNeg->setValue(ui->barNeg->minimum()); +} + + +void TouchSlider::on_barNeg_mousePressEvent(QMouseEvent * e) { + if (m_readOnly) return; + ui->barNeg->setValue(ui->barNeg->maximum() - qRound((double)(e->x()*ui->barNeg->maximum()) / (double)(ui->barNeg->width()))); + ui->barPos->setValue(ui->barPos->minimum()); +} + + +void TouchSlider::on_barNeg_valueChanged(int) { + updateCaption(); +} + + +void TouchSlider::on_barPos_valueChanged(int) { + updateCaption(); +} + + +void TouchSlider::setReadOnly(bool arg) { + QBoxLayout * bl = qobject_cast(layout()); + m_readOnly = arg; + if (arg) { + ui->pbDec->hide(); + ui->pbInc->hide(); + if (hasZero) + ui->pbZero->hide(); + bl->setStretchFactor(ui->pbMin, 2); + bl->setStretchFactor(ui->pbMax, 2); + } else { + ui->pbDec->show(); + ui->pbInc->show(); + if (hasZero) + ui->pbZero->show(); + bl->setStretchFactor(ui->pbMin, 1); + bl->setStretchFactor(ui->pbMax, 1); + } +} + + +void TouchSlider::setShowMinMax(bool arg) { + QBoxLayout * bl = qobject_cast(layout()); + m_showMinMax = arg; + if (arg) { + ui->pbMin->hide(); + ui->pbMax->hide(); + bl->setStretchFactor(ui->pbInc, 2); + bl->setStretchFactor(ui->pbDec, 2); + bl->setStretchFactor(ui->pbZero, 2); + } else { + ui->pbMin->show(); + ui->pbMax->show(); + bl->setStretchFactor(ui->pbInc, 1); + bl->setStretchFactor(ui->pbDec, 1); + bl->setStretchFactor(ui->pbZero, 1); + } +} + diff --git a/test/qad/touch_widgets/touchslider.h b/test/qad/touch_widgets/touchslider.h new file mode 100644 index 0000000..97c2b92 --- /dev/null +++ b/test/qad/touch_widgets/touchslider.h @@ -0,0 +1,105 @@ +#ifndef TOUCHSLIDER_H +#define TOUCHSLIDER_H + +#include +#include + +namespace Ui { + class TouchSlider; +} + +class TouchSlider: public QGroupBox +{ + Q_OBJECT + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly) + Q_PROPERTY(bool showMinMax READ showMinMax WRITE setShowMinMax) + Q_PROPERTY(double minimum READ minimum WRITE setMinimum) + Q_PROPERTY(double maximum READ maximum WRITE setMaximum) + Q_PROPERTY(double value READ value WRITE setValue) + Q_PROPERTY(double precision READ precision WRITE setPrecision) + Q_PROPERTY(QString prefix READ prefix WRITE setPrefix) + Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) + Q_PROPERTY(int clickID READ clickID WRITE setClickID) + Q_PROPERTY(int setID READ setID WRITE setSetID) + +public: + explicit TouchSlider(QWidget *parent = 0); + ~TouchSlider(); + + int clickID() const {return id_click;} + int setID() const {return id_set;} + + double minimum() const; + double maximum() const; + double value() const; + double precision() const; + QString prefix() const {return pref;} + QString suffix() const {return suff;} + bool readOnly() const {return m_readOnly;} + bool showMinMax() const {return m_showMinMax;} + +private: + void updateCaption(); + + Ui::TouchSlider *ui; + bool hasZero; + bool m_readOnly; + bool m_showMinMax; + double prec; + int id_click, id_set; + QString pref, suff; + +public slots: + void enable() {setEnabled(true);} + void disable() {setEnabled(false);} + + void setClickID(int id) {id_click = id;} + void setSetID(int id) {id_set = id;} + + void setSuffix(const QString & text) {suff = text; updateCaption();} + void setPrefix(const QString & text) {pref = text; updateCaption();} + void setPrecision(double precision); + void setPrecision(int precision) {setPrecision((double)precision);} + void setMinimum(double min); + void setMinimum(int min) {setMinimum((double)min);} + void setMaximum(double max); + void setMaximum(int max) {setMaximum((double)max);} + void setReadOnly(bool yes); + void setShowMinMax(bool yes); + + void setValue(double val); + void setValue(int val) {setValue((double)val);} + + void enableID(int set_id) {if (set_id == id_set) enable();} + void disableID(int set_id) {if (set_id == id_set) disable();} + + void setValueID(int set_id, double val) {if (set_id == id_set) setValue(val);} + void setValueID(int set_id, int val) {if (set_id == id_set) setValue(val);} + + void clickMin() {on_pbMin_clicked();} + void clickMax() {on_pbMax_clicked();} + void clickInc() {on_pbInc_clicked();} + void clickDec() {on_pbDec_clicked();} + void clickZero() {on_pbZero_clicked();} + +private slots: + void on_barPos_valueChanged(int value); + void on_barNeg_valueChanged(int value); + void on_barNeg_mousePressEvent(QMouseEvent*); + void on_barPos_mousePressEvent(QMouseEvent*); + void on_barNeg_mouseMoveEvent(QMouseEvent*); + void on_barPos_mouseMoveEvent(QMouseEvent*); + void on_pbZero_clicked(); + void on_pbDec_clicked(); + void on_pbInc_clicked(); + void on_pbMax_clicked(); + void on_pbMin_clicked(); + +signals: + void valueChanged(double val); + void valueChanged(int val); + void valueChangedID(int id, double val); + void valueChangedID(int id, int val); +}; + +#endif // TOUCHSLIDER_H diff --git a/test/qad/touch_widgets/touchslider.ui b/test/qad/touch_widgets/touchslider.ui new file mode 100644 index 0000000..2d25657 --- /dev/null +++ b/test/qad/touch_widgets/touchslider.ui @@ -0,0 +1,187 @@ + + + TouchSlider + + + + 0 + 0 + 449 + 77 + + + + val + + + Qt::AlignCenter + + + + 4 + + + 2 + + + + + + 0 + 0 + + + + + 45 + 45 + + + + -100 + + + + + + + + 0 + 0 + + + + 0 + + + false + + + true + + + + + + + + 0 + 0 + + + + 100 + + + 50 + + + false + + + + + + + + 0 + 0 + + + + + 45 + 45 + + + + 100 + + + + + + + + 0 + 0 + + + + + 45 + 45 + + + + - + + + true + + + 500 + + + + + + + + 0 + 0 + + + + + 45 + 45 + + + + 0 + + + + + + + + 0 + 0 + + + + + 45 + 45 + + + + + + + + true + + + 500 + + + + + + + + TouchButton + QToolButton +
touchbutton.h
+
+ + touch_bar + QProgressBar +
touch_bar.h
+
+
+ + +
diff --git a/test/qad/utils/CMakeLists.txt b/test/qad/utils/CMakeLists.txt new file mode 100644 index 0000000..c6c681c --- /dev/null +++ b/test/qad/utils/CMakeLists.txt @@ -0,0 +1 @@ +qad_project(utils "Gui;Widgets" "") diff --git a/test/qad/utils/chunkstream.cpp b/test/qad/utils/chunkstream.cpp new file mode 100644 index 0000000..8c30120 --- /dev/null +++ b/test/qad/utils/chunkstream.cpp @@ -0,0 +1,38 @@ +#include "chunkstream.h" + + +void ChunkStream::setSource(const QByteArray & data) { + stream_.setVersion(QDataStream::Qt_4_8); + data_ = const_cast(&data); + _init(); +} + + +void ChunkStream::setSource(QByteArray * data) { + stream_.setVersion(QDataStream::Qt_4_8); + data_ = (data ? data : &tmp_data); + _init(); +} + + +void ChunkStream::setSource(QDataStream & str) { + stream_.setVersion(QDataStream::Qt_4_8); + str >> tmp_data; + data_ = &tmp_data; + _init(); +} + + +ChunkStream::~ChunkStream() { + +} + + +void ChunkStream::_init() { + last_id = -1; + last_data.clear(); + buffer.close(); + buffer.setBuffer(data_); + buffer.open(QIODevice::ReadWrite); + stream_.setDevice(&buffer); +} diff --git a/test/qad/utils/chunkstream.h b/test/qad/utils/chunkstream.h new file mode 100644 index 0000000..e873395 --- /dev/null +++ b/test/qad/utils/chunkstream.h @@ -0,0 +1,60 @@ +#ifndef CHUNKSTREAM_H +#define CHUNKSTREAM_H + +#include +#include +#include + + +class ChunkStream +{ +public: + ChunkStream(const QByteArray & data) {setSource(data);} + ChunkStream(QDataStream & str) {setSource(str);} + ChunkStream(QByteArray * data = 0) {setSource(data);} + ~ChunkStream(); + + template + struct Chunk { + Chunk(int i, const T & d): id(i), data(d) {} + int id; + T data; + }; + template static Chunk chunk(int id, const T & data) {return Chunk(id, data);} + + template ChunkStream & add(int id, const T & data) {*this << Chunk(id, data); return *this;} + + void setSource(const QByteArray & data); + void setSource(QDataStream & str); + void setSource(QByteArray * data); + QDataStream & dataStream() {return stream_;} + QByteArray data() const {return tmp_data;} + bool atEnd() const {return stream_.atEnd();} + + int read() {stream_ >> last_id >> last_data; return last_id;} + int getID() {return last_id;} + template + T getData() const {T ret; QDataStream s(last_data); s.setVersion(QDataStream::Qt_4_8); s >> ret; return ret;} + template + void get(T & v) const {v = getData();} +private: + void _init(); + + int last_id; + QByteArray * data_, last_data, tmp_data; + QBuffer buffer; + QDataStream stream_; + +}; + +template +ChunkStream & operator <<(ChunkStream & s, const ChunkStream::Chunk & c) { + QByteArray ba; + QDataStream bas(&ba, QIODevice::WriteOnly); + bas.setVersion(QDataStream::Qt_4_8); + bas << c.data; + s.dataStream() << c.id << ba; + return s; +} + +#endif // CHUNKSTREAM_H diff --git a/test/qad/utils/plugin/CMakeLists.txt b/test/qad/utils/plugin/CMakeLists.txt new file mode 100644 index 0000000..283b630 --- /dev/null +++ b/test/qad/utils/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(utils "Gui" "") diff --git a/test/qad/utils/plugin/qad_utils.cpp b/test/qad/utils/plugin/qad_utils.cpp new file mode 100644 index 0000000..0b70ed0 --- /dev/null +++ b/test/qad/utils/plugin/qad_utils.cpp @@ -0,0 +1,16 @@ +//#include "qpiconfigplugin.h" +#include "qad_utils.h" + +QADUtils::QADUtils(QObject * parent): QObject(parent) { + //m_widgets.append(new QPIConfigPlugin(this)); +} + + +QList QADUtils::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_utils_plugin, QADUtils) +#endif diff --git a/test/qad/utils/plugin/qad_utils.h b/test/qad/utils/plugin/qad_utils.h new file mode 100644 index 0000000..4c825f5 --- /dev/null +++ b/test/qad/utils/plugin/qad_utils.h @@ -0,0 +1,24 @@ +#ifndef QAD_UTILS_H +#define QAD_UTILS_H + +#include +#include + +class QADUtils: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + //Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.utils") +#endif + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +public: + explicit QADUtils(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_UTILS_H diff --git a/test/qad/utils/propertystorage.cpp b/test/qad/utils/propertystorage.cpp new file mode 100644 index 0000000..794a32c --- /dev/null +++ b/test/qad/utils/propertystorage.cpp @@ -0,0 +1,165 @@ +#include +#include "qad_types.h" + + +bool PropertyStorage::isPropertyExists(const QString & _name) const { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == _name) + return true; + return false; +} + + +void PropertyStorage::addProperty(const PropertyStorage::Property & p) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == p.name) { + props[i] = p; + return; + } + props << p; +} + + +void PropertyStorage::removeProperty(const QString & _name) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == _name) { + props.removeAt(i); + return; + } +} + + +void PropertyStorage::removePropertiesByFlag(int flag) { + for (int i = 0; i < props.size(); ++i) + if ((props[i].flags & flag) == flag) { + props.removeAt(i); + --i; + } +} + + +void PropertyStorage::updateProperties(const QList & properties_, int flag_ignore) { + QVariantMap values; + foreach (const PropertyStorage::Property & p, props) + if (((p.flags & flag_ignore) != flag_ignore) || (flag_ignore == 0)) + values[p.name] = p.value; + props = properties_; + for (int i = 0; i < props.size(); ++i) { + PropertyStorage::Property & p(props[i]); + if (values.contains(p.name)) { + QVariant pv = values[p.name]; + if (pv.userType() == p.value.userType()) + p.value = pv; + } + } +} + + +PropertyStorage::Property PropertyStorage::propertyByName(const QString & name) const { + foreach (const Property & p, props) + if (p.name == name) + return p; + return Property(); +} + + +QVariant PropertyStorage::propertyValueByName(const QString & name) const { + foreach (const Property & p, props) + if (p.name == name) + return p.value; + return QVariant(); +} + + +void PropertyStorage::setPropertyValue(const QString & name, const QVariant & value) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == name) { + props[i].value = value; + return; + } +} + + +void PropertyStorage::setPropertyComment(const QString & name, const QString & comment) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == name) { + props[i].comment = comment; + return; + } +} + + +void PropertyStorage::setPropertyFlags(const QString & name, int flags) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == name) { + props[i].flags = flags; + return; + } +} + + +PropertyStorage::Property PropertyStorage::parsePropertyLine(QString l) { + PropertyStorage::Property ret; + QString pn, pc, pt("s"), pv; + if (l.contains('#')) { + int i = l.indexOf('#'); + pn = l.left(i).trimmed(); + pc = l.right(l.length() - i - 1).trimmed(); + } else { + if (l.contains('(')) { + int bs = l.indexOf('('), be = l.indexOf(')'); + if (be > 0) { + pc = l.mid(bs + 1, be - bs - 1).trimmed(); + l.remove(bs, be - bs + 1); + } else { + pc = l.right(l.length() - bs - 1).trimmed(); + l = l.left(bs); + } + } + pn = l.trimmed(); + } + if (!pc.isEmpty()) { + pt = pc.left(1); + pc = pc.remove(0, 1).trimmed(); + } + if (pn.contains('=')) { + int i = pn.indexOf('='); + pv = pn.right(pn.length() - i - 1).trimmed(); + pn.truncate(i); + pn = pn.trimmed(); + } + ret.name = pn; + ret.comment = pc; + ret.value = QVariant(typeFromLetter(pt)); + if (!pv.isEmpty()) { + //qDebug() << "set value !" << pv; + switch (ret.value.type()) { + case QVariant::Bool: pv = pv.toLower(); ret.value = (pv == "on" || pv == "true" || pv == "enable" || pv == "enabled" || pv.toInt() > 0 ? true : false); break; + case QVariant::Int: ret.value = pv.toInt(); break; + case QVariant::UInt: ret.value = pv.toUInt(); break; + case QVariant::LongLong: ret.value = pv.toLongLong(); break; + case QVariant::ULongLong: ret.value = pv.toULongLong(); break; + case QVariant::Double: ret.value = pv.toDouble(); break; + case QVariant::Color: ret.value = QColor(pv); break; + default: ret.value = pv; break; + }; + } + return ret; +} + + +PropertyStorage::Property & PropertyStorage::operator[](const QString & name) { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == name) + return props[i]; + addProperty(name, ""); + return props.back(); +} + + +const PropertyStorage::Property PropertyStorage::operator[](const QString & name) const { + for (int i = 0; i < props.size(); ++i) + if (props[i].name == name) + return props[i]; + return Property(); +} diff --git a/test/qad/utils/propertystorage.h b/test/qad/utils/propertystorage.h new file mode 100644 index 0000000..f8e4490 --- /dev/null +++ b/test/qad/utils/propertystorage.h @@ -0,0 +1,120 @@ +#ifndef PROPERTYSTORAGE_H +#define PROPERTYSTORAGE_H + +#include +#include +#include +#include +#include +#include +#include "chunkstream.h" + + +class PropertyStorage { +public: + PropertyStorage() {} + + struct Property { + Property(const QString & n = QString(), const QString & c = QString(), const QVariant & v = QVariant(), int f = 0): + name(n), comment(c), value(v), flags(f) {} + bool toBool() const {return value.toBool();} + int toInt() const {return value.toInt();} + float toFloat() const {return value.toFloat();} + double toDouble() const {return value.toDouble();} + QString toString() const {return value.toString();} + QString name; + QString comment; + QVariant value; + int flags; + }; + + PropertyStorage(const QList & pl) {props = pl;} + + typedef QList::const_iterator const_iterator; + typedef QList::iterator iterator; + + iterator begin() {return props.begin();} + const_iterator begin() const {return props.begin();} + const_iterator constBegin() const {return props.constBegin();} + iterator end() {return props.end();} + const_iterator end() const {return props.end();} + const_iterator constEnd() const {return props.constEnd();} + + int count() const {return props.count();} + int length() const {return props.length();} + int size() const {return props.size();} + bool isEmpty() const {return props.isEmpty();} + Property & first() {return props.first();} + const Property & first() const {return props.first();} + Property & front() {return props.front();} + const Property & front() const {return props.front();} + Property & last() {return props.last();} + const Property & last() const {return props.last();} + Property & back() {return props.back();} + const Property & back() const {return props.back();} + void removeFirst() {props.removeFirst();} + void removeLast() {props.removeLast();} + void removeAt(int i) {props.removeAt(i);} + Property value(int i) const {return props.value(i);} + Property value(int i, const Property & defaultValue) const {return props.value(i, defaultValue);} + void clear() {props.clear();} + + PropertyStorage copy() const {return PropertyStorage(*this);} + int propertiesCount() const {return props.size();} + QList & properties() {return props;} + const QList & properties() const {return props;} + const PropertyStorage & propertyStorage() const {return *this;} + bool isPropertyExists(const QString & _name) const; + void clearProperties() {props.clear();} + void addProperty(const Property & p); + void addProperty(const QString & _name, const QVariant & _def_value, const QString & _comment = QString(), int _flags = 0) {addProperty(Property(_name, _comment, _def_value, _flags));} + void removeProperty(const QString & _name); + void removePropertiesByFlag(int flag); + void updateProperties(const QList & properties_, int flag_ignore = 0); + Property propertyByName(const QString & name) const; + QVariant propertyValueByName(const QString & name) const; + void setPropertyValue(const QString & name, const QVariant & value); + void setPropertyComment(const QString & name, const QString & comment); + void setPropertyFlags(const QString & name, int flags); + + PropertyStorage & operator <<(const PropertyStorage::Property & p) {props << p; return *this;} + PropertyStorage & operator <<(const QList & p) {props << p; return *this;} + PropertyStorage & operator <<(const PropertyStorage & p) {props << p.props; return *this;} + Property & operator[](int i) {return props[i];} + const Property & operator[](int i) const {return props[i];} + Property & operator[](const QString & name); + const Property operator[](const QString & name) const; + + static Property parsePropertyLine(QString l); + +protected: + QList props; + +}; + +inline QDebug operator <<(QDebug s, const PropertyStorage::Property & p) {s.nospace() << p.name << " (0x" << QString::number(p.flags, 16) << ") = " << p.value; return s.space();} + +inline QDataStream & operator <<(QDataStream & s, const PropertyStorage & p) {s << p.properties(); return s;} +inline QDataStream & operator >>(QDataStream & s, PropertyStorage & p) {s >> p.properties(); return s;} + +inline QDataStream & operator <<(QDataStream & s, const PropertyStorage::Property & p) { + ChunkStream cs; + cs << cs.chunk(1, p.name) << cs.chunk(2, p.comment) << cs.chunk(3, p.value) << cs.chunk(4, p.flags); + s << cs.data(); + return s; +} +inline QDataStream & operator >>(QDataStream & s, PropertyStorage::Property & p) { + ChunkStream cs(s); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: cs.get(p.name); break; + case 2: cs.get(p.comment); break; + case 3: cs.get(p.value); break; + case 4: cs.get(p.flags); break; + } + } + return s; +} + + +#endif // PROPERTYSTORAGE_H diff --git a/test/qad/utils/qad_types.cpp b/test/qad/utils/qad_types.cpp new file mode 100644 index 0000000..365a86f --- /dev/null +++ b/test/qad/utils/qad_types.cpp @@ -0,0 +1,275 @@ +#include "qad_types.h" +#include +#include +#include +#include +#if QT_VERSION >= 0x050000 +# include +# include +#endif + + +bool __QADTypesRegistrator__::_inited = false; + + +__QADTypesRegistrator__::__QADTypesRegistrator__(int) { + instance()->_inited = true; +} + +__QADTypesRegistrator__ *__QADTypesRegistrator__::instance() { + static __QADTypesRegistrator__ ret; + return &ret; +} + +__QADTypesRegistrator__::__QADTypesRegistrator__() { + if (_inited) return; + _inited = true; + + qRegisterMetaType("QAD::Enumerator"); + qRegisterMetaTypeStreamOperators("QAD::Enumerator"); + + qRegisterMetaType("QAD::Enum"); + qRegisterMetaTypeStreamOperators("QAD::Enum"); + + qRegisterMetaType("QAD::File"); + qRegisterMetaTypeStreamOperators("QAD::File"); + + qRegisterMetaType("QAD::Dir"); + qRegisterMetaTypeStreamOperators("QAD::Dir"); + + qRegisterMetaType("QAD::IODevice"); + qRegisterMetaTypeStreamOperators("QAD::IODevice"); + + qRegisterMetaType("QAD::MathVector"); + qRegisterMetaTypeStreamOperators("QAD::MathVector"); + + qRegisterMetaType("QAD::MathMatrix"); + qRegisterMetaTypeStreamOperators("QAD::MathMatrix"); + +#if QT_VERSION >= 0x050200 + QMetaType::registerConverter(&QAD::Enum::selectedValue); + QMetaType::registerConverter(&QAD::Enum::selectedName); + QMetaType::registerConverter(&QAD::File::toString); + QMetaType::registerConverter(&QAD::Dir::toString); + QMetaType::registerConverter(&QAD::IODevice::toString); +#endif +} + +__QADTypesRegistrator__ __registrator__(1); + + +QAD::Enum::Enum(const QMetaEnum & meta, int selected) { + enum_name = meta.name(); + for (int i = 0; i < meta.keyCount(); ++i) { + enum_list << QAD::Enumerator(meta.value(i), meta.key(i)); + } + selectValue(selected); +} + + +int QAD::Enum::selectedValue() const { + foreach (const Enumerator & e, enum_list) + if (e.name == selected) + return e.value; + return 0; +} + + +bool QAD::Enum::selectValue(int v) { + foreach (const Enumerator & e, enum_list) + if (e.value == v) { + selected = e.name; + return true; + } + return false; +} + + +bool QAD::Enum::selectName(const QString & n) { + foreach (const Enumerator & e, enum_list) + if (e.name == n) { + selected = e.name; + return true; + } + return false; +} + + +int QAD::Enum::value(const QString & n) const { + foreach (const Enumerator & e, enum_list) + if (e.name == n) + return e.value; + return 0; +} + + +QString QAD::Enum::name(int v) const { + foreach (const Enumerator & e, enum_list) + if (e.value == v) + return e.name; + return QString(); +} + + +QList QAD::Enum::values() const { + QList ret; + foreach (const Enumerator & e, enum_list) + ret << e.value; + return ret; +} + + +QStringList QAD::Enum::names() const { + QStringList ret; + foreach (const Enumerator & e, enum_list) + ret << e.name; + return ret; +} + + +QAD::Enum & QAD::Enum::operator <<(const QAD::Enumerator & v) { + enum_list << v; + return *this; +} + + +QAD::Enum & QAD::Enum::operator <<(const QString & v) { + enum_list << Enumerator(enum_list.size(), v); + return *this; +} + + +QAD::Enum & QAD::Enum::operator <<(const QStringList & v) { + foreach (const QString & s, v) + (*this) << s; + return *this; +} + + + + +QString QAD::IODevice::toString() const { + QString s; + if (__QADTypesRegistrator__::instance()->toString_funcs.contains(qMetaTypeId())) { + QVariant v; + v.setValue(*this); + (*(__QADTypesRegistrator__::instance()->toString_funcs[qMetaTypeId()]))(v, s); + return s; + } else { +// s += "IODevice(" + prefix + ", mode="; +// int rwc = 0; +// if (mode & QIODevice::ReadOnly) {s += "r"; ++rwc;} +// if (mode & QIODevice::WriteOnly) {s += "w"; ++rwc;} +// if (rwc == 1) s += "o"; +// if (options != 0) { +// s += ", flags="; +// if (options & 1) +// s += "br"; +// if (options & 2) +// if (options & 1) +// s+= "|"; +// s += "bw"; +// } +// PropertyStorage ps = props; +// foreach (const PropertyStorage::Property & p, ps) { +// QString vs = p.value.toString(); +// if (p.value.type() == QVariant::StringList) +// vs = p.value.toStringList().join(";"); +// s += ", " + p.name + "=\"" + vs + "\""; +// } +// s += ")"; + return s; + } +} + + + + +QVariant::Type typeFromLetter(const QString & l) { + if (l.isEmpty()) return QVariant::String; + QString ft = l.left(1); + if (ft == "l") return QVariant::StringList; + if (ft == "b") return QVariant::Bool; + if (ft == "n") return QVariant::Int; + if (ft == "f") return QVariant::Double; + if (ft == "c") return QVariant::Color; + if (ft == "r") return QVariant::Rect; + if (ft == "a") return QVariant::RectF; + if (ft == "p") return QVariant::Point; + if (ft == "v") return QVariant::PointF; + if (ft == "e") return (QVariant::Type)qMetaTypeId(); + if (ft == "F") return (QVariant::Type)qMetaTypeId(); + if (ft == "D") return (QVariant::Type)qMetaTypeId(); + if (ft == "d") return (QVariant::Type)qMetaTypeId(); + if (ft == "V") return (QVariant::Type)qMetaTypeId(); + if (ft == "M") return (QVariant::Type)qMetaTypeId(); + return QVariant::String; +} + + +QString uniqueName(QString n, const QStringList & names) { + if (!names.contains(n)) + return n; + QString num; + while (!n.isEmpty()) { + if (n.right(1)[0].isDigit()) { + num.push_front(n.right(1)); + n.chop(1); + } else break; + } + if (!n.endsWith('_')) n += '_'; + int in = num.toInt() + 1; + QString nn = n + QString::number(in).rightJustified(3, '0'); + while (names.contains(nn)) + nn = n + QString::number(++in).rightJustified(3, '0'); + return nn; +} + + +int fontHeight(const QWidget * w) { +#ifdef Q_OS_ANDROID + static int ret = QApplication::fontMetrics().size(0, "0").height(); + return ret; +#else +# if QT_VERSION >= 0x050000 + //qDebug() << "fontHeight" << w; + if (w) { + QWidget * pw = w->window(); + if (pw) { + /*QWindow * wnd = pw->windowHandle(); + //qDebug() << "wnd" << wnd; + if (wnd) { + QScreen * s = wnd->screen(); + qDebug() << "s" << s; + if (s) { + qDebug() << "scales:"; + qDebug() << QApplication::fontMetrics().size(0, "0").height() << QApplication::fontMetrics().xHeight(); + qDebug() << s->logicalDotsPerInch() << s->logicalDotsPerInch()/96.*QApplication::font().pointSizeF(); + } + }*/ + return QFontMetrics(QApplication::font(), pw).size(0, "0").height(); + } + } +# endif +#endif + return QApplication::fontMetrics().size(0, "0").height(); +} + + +int lineThickness(const QWidget * w) { + return qMax(qRound(fontHeight(w) / 15.), 1); +} + + +QSize preferredIconSize(float x, const QWidget * w) { + int s = qMax(8, qRound(fontHeight(w) * x)); +#ifdef Q_OS_MACOS + s /= 1.25; +#endif + return QSize(s, s); +} + + +double appScale(const QWidget * w) { + return qMax(fontHeight(w) / 15., 1.); +} diff --git a/test/qad/utils/qad_types.h b/test/qad/utils/qad_types.h new file mode 100644 index 0000000..3803578 --- /dev/null +++ b/test/qad/utils/qad_types.h @@ -0,0 +1,148 @@ +#ifndef QAD_TYPES_H +#define QAD_TYPES_H + +#include "propertystorage.h" + +class QMetaEnum; + +namespace QAD { + + struct Enumerator { + Enumerator(int v = 0, const QString & n = QString()): value(v), name(n) {} + int value; + QString name; + }; + + struct Enum { + Enum(const QString & n = QString()): enum_name(n) {} + Enum(const QMetaEnum & meta, int selected = 0); + QString toString() const {return selected;} // obsolete, use selectedName() + int selectedValue() const; + QString selectedName() const {return selected;} + bool selectValue(int v); + bool selectName(const QString & n); + int value(const QString & n) const; + QString name(int v) const; + QList values() const; + QStringList names() const; + int size() const {return enum_list.size();} + QString enum_name; + QString selected; + QList enum_list; + Enum & operator <<(const Enumerator & v); + Enum & operator <<(const QString & v); + Enum & operator <<(const QStringList & v); + }; + + struct File { + File(const QString & p = QString(), const QString & f = QString(), bool abs = false): file(p), filter(f), is_abs(abs) {} + QString toString() const {return file;} + QString file; + QString filter; + bool is_abs; + }; + + struct Dir { + Dir(const QString & d = QString(), bool abs = false): dir(d), is_abs(abs) {} + QString toString() const {return dir;} + QString dir; + bool is_abs; + }; + + struct IODevice { + IODevice(const QString & device_prefix = QString(), const PropertyStorage & device_properties = PropertyStorage(), + int open_mode = QIODevice::ReadWrite, int device_options = 0) + : prefix(device_prefix), mode(open_mode), options(device_options), props(device_properties) {} + QString toString() const; + bool isValid() const {return !prefix.isEmpty();} + QString prefix; + int mode; + int options; + PropertyStorage props; + }; + + struct MathVector { + MathVector(const QVector & vec = QVector()) {v = vec;} + QVector v; + }; + + struct MathMatrix { + MathMatrix(const QVector > & mat = QVector > ()) {m = mat;} + QVector > m; // [Row][Column] + }; + +} + +Q_DECLARE_METATYPE(QAD::Enumerator) +inline QDataStream & operator <<(QDataStream & s, const QAD::Enumerator & v) {s << v.value << v.name; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::Enumerator & v) {s >> v.value >> v.name; return s;} +inline QDebug operator <<(QDebug s, const QAD::Enumerator & v) {s.nospace() << v.name << "(" << v.value << ")"; return s.space();} + +Q_DECLARE_METATYPE(QAD::Enum) +inline QDataStream & operator <<(QDataStream & s, const QAD::Enum & v) {s << v.enum_name << v.selected << v.enum_list; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::Enum & v) {s >> v.enum_name >> v.selected >> v.enum_list; return s;} +inline QDebug operator <<(QDebug s, const QAD::Enum & v) {s.nospace() << v.selected; return s.space();} + +Q_DECLARE_METATYPE(QAD::File) +inline QDataStream & operator <<(QDataStream & s, const QAD::File & v) {s << v.file << v.filter << v.is_abs; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::File & v) {s >> v.file >> v.filter >> v.is_abs; return s;} +inline QDebug operator <<(QDebug s, const QAD::File & v) {s.nospace() << v.file; return s.space();} + +Q_DECLARE_METATYPE(QAD::Dir) +inline QDataStream & operator <<(QDataStream & s, const QAD::Dir & v) {s << v.dir << v.is_abs; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::Dir & v) {s >> v.dir >> v.is_abs; return s;} +inline QDebug operator <<(QDebug s, const QAD::Dir & v) {s.nospace() << v.dir; return s.space();} + +Q_DECLARE_METATYPE(QAD::IODevice) +inline QDataStream & operator <<(QDataStream & s, const QAD::IODevice & v) {s << v.prefix << v.mode << v.options << v.props; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::IODevice & v) {s >> v.prefix >> v.mode >> v.options >> v.props; return s;} +inline QDebug operator <<(QDebug s, const QAD::IODevice & v) {s.nospace() << v.toString(); return s.space();} + +Q_DECLARE_METATYPE(QAD::MathVector) +inline QDataStream & operator <<(QDataStream & s, const QAD::MathVector & v) {s << v.v; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::MathVector & v) {s >> v.v; return s;} +inline QDebug operator <<(QDebug s, const QAD::MathVector & v) {s.nospace() << "Vector " << v.v; return s.space();} + +Q_DECLARE_METATYPE(QAD::MathMatrix) +inline QDataStream & operator <<(QDataStream & s, const QAD::MathMatrix & v) {s << v.m; return s;} +inline QDataStream & operator >>(QDataStream & s, QAD::MathMatrix & v) {s >> v.m; return s;} +inline QDebug operator <<(QDebug s, const QAD::MathMatrix & v) {s.nospace() << "Matrix " << v.m; return s.space();} + + +class __QADTypesRegistrator__ { +public: + __QADTypesRegistrator__(int); + static __QADTypesRegistrator__ * instance(); + QMap toString_funcs; +private: + __QADTypesRegistrator__(); + static bool _inited; +}; + + +inline qreal quantize(qreal x, qreal q = 10.f) {return qRound(x / q) * q;} +inline QPointF quantize(QPointF x, qreal q = 10.f) {return QPointF(quantize(x.x(), q), quantize(x.y(), q));} + +inline qreal distPointToLine(const QPointF & lp0, const QPointF & lp1, const QPointF & p) { + QLineF a(lp0, lp1), b(lp0, p), c(lp1, p); + qreal f = qAbs(a.dx()*b.dy() - a.dy()*b.dx()) / a.length(), s = b.length() + c.length() - a.length(); + return qMax(f, s); +} +inline QPointF nearestPointOnLine(const QPointF & lp0, const QPointF & lp1, const QPointF & p) { + QLineF a(lp0, lp1), b(lp0, p); + return a.pointAt(b.length() / a.length()); +} +inline QRectF enlargedRect(const QRectF & r, qreal dx, qreal dy, qreal v) { + return QRectF(r.left() - v + dx, r.top() - v + dy, r.width() + v+v, r.height() + v+v); +} + +QVariant::Type typeFromLetter(const QString & l); +QString uniqueName(QString n, const QStringList & names); + +int fontHeight(const QWidget * w = 0); +int lineThickness(const QWidget * w = 0); +QSize preferredIconSize(float x = 1.f, const QWidget * w = 0); +double appScale(const QWidget * w = 0); + + +#endif // QAD_TYPES_H diff --git a/test/qad/utils/qad_utils.qrc b/test/qad/utils/qad_utils.qrc new file mode 100644 index 0000000..12a8c2d --- /dev/null +++ b/test/qad/utils/qad_utils.qrc @@ -0,0 +1,17 @@ + + + ../icons/edit-clear.png + ../icons/document-save.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/item-add.png + ../icons/item.png + ../icons/node-add.png + ../icons/node.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/document-open_16.png + + diff --git a/test/qad/utils/qpiconfig.cpp b/test/qad/utils/qpiconfig.cpp new file mode 100644 index 0000000..bbd1cd2 --- /dev/null +++ b/test/qad/utils/qpiconfig.cpp @@ -0,0 +1,825 @@ +#include "qpiconfig.h" +#include +#include + + +int QString2int(const QString & string) { + QString s = string.trimmed(); + if (s.left(2) == "0x") return s.mid(2).toLongLong(0, 16); + if (s.left(1) == "0") return s.mid(1).toLongLong(0, 8); + return s.toLongLong(); +} + + +QRect QString2QRect(const QString & string) { + int sl, st, sw, sh; + int s = 0, e; + e = string.indexOf(";"); + sl = string.mid(s, e - s).toInt(); + s = e + 1; + e = string.indexOf(";", s); + st = string.mid(s, e - s).toInt(); + s = e + 1; + e = string.indexOf(";", s); + sw = string.mid(s, e - s).toInt(); + s = e + 1; + e = string.length(); + sh = string.mid(s, e - s).toInt(); + return QRect(sl, st, sw, sh); +} + + +QRectF QString2QRectF(const QString & string) { + double sl, st, sw, sh; + int s = 0, e; + e = string.indexOf(";"); + sl = string.mid(s, e - s).toDouble(); + s = e + 1; + e = string.indexOf(";", s); + st = string.mid(s, e - s).toDouble(); + s = e + 1; + e = string.indexOf(";", s); + sw = string.mid(s, e - s).toDouble(); + s = e + 1; + e = string.length(); + sh = string.mid(s, e - s).toDouble(); + return QRectF(sl, st, sw, sh); +} + + +QPoint QString2QPoint(const QString & string) { + int sx, sy; + int s = 0, e; + e = string.indexOf(";"); + sx = string.mid(s, e - s).toInt(); + s = e + 1; + e = string.length(); + sy = string.mid(s, e - s).toInt(); + return QPoint(sx, sy); +} + + +QPointF QString2QPointF(const QString & string) { + double sx, sy; + int s = 0, e; + e = string.indexOf(";"); + sx = string.mid(s, e - s).toDouble(); + s = e + 1; + e = string.length(); + sy = string.mid(s, e - s).toDouble(); + return QPointF(sx, sy); +} + + +QString QRect2QString(const QRect & rect) { + return QString::number(rect.left()) + ";" + + QString::number(rect.top()) + ";" + + QString::number(rect.width()) + ";" + + QString::number(rect.height()); +} + + +QString QRectF2QString(const QRectF & rect) { + return QString::number(rect.left()) + ";" + + QString::number(rect.top()) + ";" + + QString::number(rect.width()) + ";" + + QString::number(rect.height()); +} + + +QPIConfig::Entry QPIConfig::Branch::_empty; +QPIConfig::Entry QPIConfig::Entry::_empty; + + +QPIConfig::Branch QPIConfig::Branch::allLeaves() { + Branch b; + b.delim = delim; + foreach (Entry * i, *this) { + if (i->isLeaf()) b << i; + else allLeaves(b, i); + } + return b; +} + + +QPIConfig::Entry & QPIConfig::Branch::getValue(const QString & vname, const QString & def, bool * exist) { + if (vname.isEmpty()) { + _empty.clear(); + _empty.delim = delim; + if (exist != 0) *exist = false; + return _empty; + } + QStringList tree = vname.split(delim); + QString name = tree.front(); + tree.pop_front(); + Entry * ce = 0; + foreach (Entry * i, *this) + if (i->_name == name) { + ce = i; + break; + } + if (ce == 0) { + _empty._name = vname; + _empty._value = def; + _empty.delim = delim; + if (exist != 0) *exist = false; + return _empty; + } + foreach (QString i, tree) { + ce = ce->findChild(i); + if (ce == 0) { + _empty._name = vname; + _empty._value = def; + _empty.delim = delim; + if (exist != 0) *exist = false; + return _empty; + } + } + if (exist != 0) *exist = true; + return *ce; +} + + +QPIConfig::Branch QPIConfig::Branch::getValues(const QString & name) { + Branch b; + b.delim = delim; + foreach (Entry * i, *this) { + if (i->isLeaf()) { + if (i->_name.indexOf(name) >= 0) + b << i; + } else { + foreach (Entry * j, i->_children) + if (j->_name.indexOf(name) >= 0) + b << j; + } + } + return b; +} + + +QPIConfig::Branch QPIConfig::Branch::getLeaves() { + Branch b; + b.delim = delim; + foreach (Entry * i, *this) + if (i->isLeaf()) + b << i; + return b; +} + + +QPIConfig::Branch QPIConfig::Branch::getBranches() { + Branch b; + b.delim = delim; + foreach (Entry * i, *this) + if (!i->isLeaf()) + b << i; + return b; +} + + +QPIConfig::Branch & QPIConfig::Branch::filter(const QString & f) { + for (int i = 0; i < size(); ++i) { + if (at(i)->_name.indexOf(f) < 0) { + remove(i); + --i; + } + } + return *this; +} + + +bool QPIConfig::Branch::entryExists(const Entry * e, const QString & name) const { + if (e->_children.isEmpty()) { + if (e->_name == name) return true; + else return false; + } + foreach (Entry * i, e->_children) + if (entryExists(i, name)) return true; + return false; +} + + +QPIConfig::Entry & QPIConfig::Entry::getValue(const QString & vname, const QString & def, bool * exist) { + QStringList tree = vname.split(delim); + Entry * ce = this; + foreach (QString i, tree) { + ce = ce->findChild(i); + if (ce == 0) { + _empty._name = vname; + _empty._value = def; + _empty.delim = delim; + if (exist != 0) *exist = false; + return _empty; + } + } + if (exist != 0) *exist = true; + return *ce; +} + + +QPIConfig::Branch QPIConfig::Entry::getValues(const QString & vname) { + Branch b; + b.delim = delim; + foreach (Entry * i, _children) + if (i->_name.indexOf(vname) >= 0) + b << i; + return b; +}; + + +bool QPIConfig::Entry::entryExists(const Entry * e, const QString & name) const { + if (e->_children.isEmpty()) { + if (e->_name == name) return true; + else return false; + } + foreach (Entry * i, e->_children) + if (entryExists(i, name)) return true; + return false; +} + + +QPIConfig::QPIConfig(const QString & path, QIODevice::OpenMode mode, QPIConfig::FileType type_): QFile(path) { + init(); + type = type_; + open(mode); + //stream.setDevice(this); + parse(); +} + + +QPIConfig::QPIConfig(const QString & path, QIODevice::OpenMode mode): QFile(path) { + init(); + type = Config; + open(mode); + //stream.setDevice(this); + parse(); +} + + +QPIConfig::QPIConfig(const QString & path, QPIConfig::FileType type_): QFile(path) { + init(); + type = type_; + open(QIODevice::ReadWrite); + //stream.setDevice(this); + parse(); +} + + +QPIConfig::QPIConfig(QString * str, QPIConfig::FileType type_) { + init(); + type = type_; + buffer = str; + //stream.setDevice(this); + parse(); +} + + +QPIConfig::QPIConfig(const QString & path, QStringList dirs) { + init(); + type = Config; + internal = true; + dev = new QFile(path); + dev->open(QIODevice::ReadOnly); + incdirs = dirs; + incdirs << QFileInfo(path).absoluteDir().path(); + QString cp = path; + while (!dev->isOpen()) { + if (dirs.isEmpty()) break; + cp = dirs.back(); + if (cp.endsWith("/") || cp.endsWith("\\")) cp.chop(1); + cp += "/" + path; + dev->setFileName(cp); + dev->open(QIODevice::ReadOnly); + dirs.pop_back(); + } + if (!dev->isOpen()) { + delete dev; + dev = 0; + return; + } + dev->close(); + setFileName(cp); + open(QIODevice::ReadOnly); + parse(); +} + + +QPIConfig::~QPIConfig() { + stream.setDevice(0); + root.deleteBranch(); + foreach (QPIConfig * c, inc_devs) + delete c; + inc_devs.clear(); + includes.clear(); +} + + +void QPIConfig::init() { + internal = false; + buffer = 0; + dev = 0; + delim = "."; + root._name = "root"; + root.delim = delim; + empty.delim = delim; + empty._parent = 0; +} + + +void QPIConfig::setFile(const QString & path, QIODevice::OpenMode mode) { + buffer = 0; + setFileName(path); + if (open(mode)) + parse(); +} + + +void QPIConfig::setString(QString * str) { + buffer = str; + parse(); +} + + +QPIConfig::Entry & QPIConfig::getValue(const QString & vname, const QString & def, bool * exist) { + QStringList tree = vname.split(delim); + Entry * ce = &root; + foreach (QString i, tree) { + ce = ce->findChild(i); + if (ce == 0) { + if (exist != 0) *exist = false; + empty._name = vname; + empty._value = def; + empty.delim = delim; + return empty; + } + } + if (exist != 0) *exist = true; + return *ce; +} + + +QPIConfig::Branch QPIConfig::getValues(const QString & vname) { + Branch b; + b.delim = delim; + foreach (Entry * i, root._children) + if (i->_name.indexOf(vname) >= 0) + b << i; + return b; +} + + +QPIConfig::Entry & QPIConfig::addEntry(const QString & name, const QString & value, const QString & type, bool write, bool node) { + if (getValue(name)._parent != 0) + return empty; + QString sn = name, tn; + bool toRoot = false; + while (sn.indexOf(delim) == 0) sn.remove(0, delim.length()); + QStringList tree = sn.split(delim); + QString ename = tree.back(); + tn = tree.front(); + tree.pop_back(); + Entry * te, * ce, * entry = &root; + if (tree.isEmpty()) toRoot = true; + foreach (QString i, tree) { + te = entry->findChild(i); + if (te == 0) { + ce = new Entry(); + ce->delim = delim; + ce->_tab = entry->_tab; + ce->_line = entry->_line; + ce->_name = i; + ce->_parent = entry; + //qDebug() << " [QPIC] add " + tn; + ce->_full_name = tn; + entry->_children << ce; + entry = ce; + } else entry = te; + tn += delim + i; + } + QPIConfig::Branch ch = entry->_children; + qSort(ch.begin(), ch.end(), QPIConfig::Entry::compare); + te = (entry->isLeaf() ? 0 : ch.back()); + ce = new Entry(); + ce->delim = delim; + ce->_name = ename; + if (!node) ce->_value = value; + ce->_type = type; + if (te == 0) { + //qDebug() << "[QPIC] te == 0"; + ce->_tab = entry->_tab; + if (toRoot) ce->_line = other.size(); + else { + ch = entry->_parent->_children; + qSort(ch.begin(), ch.end(), QPIConfig::Entry::compare); + ce->_line = ch.back()->_line + 1; + } + } else { + ce->_tab = te->_tab; + if (toRoot) ce->_line = other.size(); + else ce->_line = te->_line + 1; + } + //qDebug() << "[QPIC] add " + sn + " at line " << ce->_line << ", parent " << entry->_name; + ce->_full_name = sn; + ce->_parent = entry; + entry->_children << ce; + //qDebug() << "[QPIC] children " << entry->childCount(); + if (!node) { + other.insert(ce->_line, ""); + Branch b = allLeaves(); + //qDebug() << "[QPIC] allLeaves " << b.size(); + bool found = false; + for (int i = 0; i < b.size(); ++i) { + if (found) { + b[i]->_line++; + continue; + } + if (b[i] == ce) { + found = true; + if (i > 0) + if (b[i - 1]->_line == b[i]->_line) + b[i - 1]->_line++; + } + //qDebug() << b[i]->_line; + } + } + //qDebug() << "[QPIC] add " + sn + " at line " << ce->_line << ", parent " + entry->_name; + if (write) writeAll(); + return *ce; +} + + +void QPIConfig::setValue(const QString & name, const QString & value, const QString & type, bool write) { + Entry & e(getValue(name)); + if (&e == &empty) { + addEntry(name, value, type, write); + return; + } + e._value = value; + e._type = type; + if (write) writeAll(); +} + + +int QPIConfig::entryIndex(const QString & name) { + QStringList tree = name.split(delim); + Entry * ce = &root; + foreach (QString i, tree) { + ce = ce->findChild(i); + if (ce == 0) + return -1; + } + Branch b = allLeaves(); + return allLeaves().indexOf(ce); +} + + +void QPIConfig::setValue(uint number, const QString & value, bool write) { + Entry & e(entryByIndex(number)); + if (&e == &empty) return; + e._value = value; + if (write) writeAll(); +} + + +void QPIConfig::setName(uint number, const QString & name, bool write) { + Entry & e(entryByIndex(number)); + if (&e == &empty) return; + e._name = name; + if (write) writeAll(); +} + + +void QPIConfig::setType(uint number, const QString & type, bool write) { + Entry & e(entryByIndex(number)); + if (&e == &empty) return; + e._type = type; + if (write) writeAll(); +} + + +void QPIConfig::setComment(uint number, const QString & comment, bool write) { + Entry & e(entryByIndex(number)); + if (&e == &empty) return; + e._comment = comment; + if (write) writeAll(); +} + + +void QPIConfig::removeEntry(const QString & name, bool write) { + Entry & e(getValue(name)); + if (&e == &empty) return; + Branch b = allLeaves(); + removeEntry(b, &e); + if (write) writeAll(); +} + + +void QPIConfig::removeEntry(uint number, bool write) { + Entry & e(entryByIndex(number)); + if (&e == &empty) return; + Branch b = allLeaves(); + removeEntry(b, &e); + if (write) writeAll(); +} + + +void QPIConfig::removeEntry(Branch & b, QPIConfig::Entry * e) { + bool leaf = true; + //qDebug() << " before " << b.size(); + if (e->isLeaf()) other.removeAt(e->_line); + if (!e->isLeaf() && !e->_value.isEmpty()) { + e->_value.clear(); + //leaf = false; + } else { + int cc = e->_children.size(); + for (int i = 0; i < cc; ++i) + removeEntry(b, e->_children.back()); + } + bool found = false; + for (int i = 0; i < b.size(); ++i) { + if (found) { + b[i]->_line--; + continue; + } + if (b[i] == e) found = true; + } + if (!leaf) return; + int ti = e->_parent->_children.indexOf(e); + if (ti >= 0) e->_parent->_children.remove(ti); + ti = b.indexOf(e); + if (ti >= 0) b.remove(ti); + //qDebug() << " after " << b.size(); + delete e; +} + + +void QPIConfig::writeAll() { + if (buffer == 0) { + stream.setDevice(this); + resize(0); + } else { + stream.setString(buffer); + buffer->clear(); + } + stream.seek(0); + //writeEntry(&root); + buildFullNames(&root); + Branch b = allLeaves(); + QString prefix, tprefix; + bool isPrefix; + int j = 0; + for (int i = 0; i < other.size(); ++i) { + //cout << j << endl; + if (j >= 0 && j < b.size()) { + if (b[j]->_line == i) { + b[j]->buildLine(); + stream << b[j]->_all << '\n'; + ++j; + } else { + stream << other[i]; + tprefix = getPrefixFromLine(other[i], &isPrefix); + if (isPrefix) { + prefix = tprefix; + if (!prefix.isEmpty()) + prefix += delim; + } + if (i < other.size() - 1) stream << '\n'; + } + } else { + stream << other[i]; + tprefix = getPrefixFromLine(other[i], &isPrefix); + if (isPrefix) { + prefix = tprefix; + if (!prefix.isEmpty()) + prefix += delim; + } + if (i < other.size() - 1) stream << '\n'; + } + } + if (buffer == 0) + flush(); + readAll(); +} + + +QString QPIConfig::getPrefixFromLine(QString line, bool * exists) { + line = line.trimmed(); + if (line.left(1) == "#") {if (exists) *exists = false; return QString();} + int ci = line.indexOf("#"); + if (ci >= 0) line = line.left(ci).trimmed(); + if (line.indexOf("=") >= 0) {if (exists) *exists = false; return QString();} + if (line.indexOf("[") >= 0 && line.indexOf("]") >= 0) { + if (exists) *exists = true; + line.remove(0, 1); + return line.left(line.lastIndexOf("]")).trimmed(); + } + if (exists) *exists = false; + return QString(); +} + + +QString QPIConfig::writeAllToString() { + QString str; + QTextStream s(&str); + //writeEntry(&root); + buildFullNames(&root); + Branch b = allLeaves(); + int j = 0; + for (int i = 0; i < other.size(); ++i) { + //cout << j << endl; + if (j >= 0 && j < b.size()) { + if (b[j]->_line == i) { + b[j]->buildLine(); + s << b[j]->_all << '\n'; + ++j; + } else { + s << other[i]; + if (i < other.size() - 1) s << '\n'; + } + } else { + s << other[i]; + if (i < other.size() - 1) s << '\n'; + } + } + return str; +} + + +void QPIConfig::readAllFromString(const QString & str) { + parse(str); +} + + +void QPIConfig::clear() { + root.deleteBranch(); + root.clear(); + other.clear(); + if (buffer == 0) + resize(0); + else + buffer->clear(); + stream.seek(0); +} + + +void QPIConfig::readAll() { + if (buffer == 0) + flush(); + parse(); +} + + +bool QPIConfig::entryExists(const Entry * e, const QString & name) const { + if (e->_children.isEmpty()) { + if (e->_name == name) return true; + else return false; + } + foreach (Entry * i, e->_children) + if (entryExists(i, name)) return true; + return false; +} + + +void QPIConfig::updateIncludes() { + if (internal) return; + all_includes.clear(); + foreach (QPIConfig * c, includes) + all_includes << c->allLeaves(); +} + + +QString QPIConfig::parseLine(QString v) { + int i = -1, l = 0; + while (1) { + i = v.indexOf("${"); + if (i < 0) break; + l = v.indexOf("}", i + 1); + QString w = v.mid(i + 2, l - i - 2), r; + l = w.length() + 3; + w = parseLine(w); + w = w.trimmed(); + bool ex = false; + QPIConfig::Entry & me = getValue(w, "", &ex); + if (ex) { + r = me._value; + } else { + foreach (QPIConfig::Entry * e, all_includes) { + if (e->_full_name == w) { + r = e->_value; + break; + } + } + } + v.replace(i, l, r); + } + return v; +} + + +void QPIConfig::parse(QString content) { + root.deleteBranch(); + root.clear(); + QString src, str, tab, comm, all, name, type, prefix, tprefix; + QStringList tree; + Entry * entry, * te, * ce; + int ind, sind; + bool isNew, isPrefix; + foreach (QPIConfig * c, inc_devs) + delete c; + inc_devs.clear(); + includes.clear(); + if (content.isEmpty()) { + if (buffer == 0) { + if (!isOpen()) return; + stream.setDevice(this); + } else + stream.setString(buffer); + } else { + stream.setString(&content); + } + stream.seek(0); + other.clear(); + lines = centry = 0; + while (!stream.atEnd()) { + other.push_back(QString()); + src = str = parseLine(stream.readLine()); + tprefix = getPrefixFromLine(src, &isPrefix); + if (isPrefix) { + prefix = tprefix; + if (!prefix.isEmpty()) + prefix += delim; + } + //cout << str << endl; + tab = str.left(str.indexOf(str.trimmed().left(1))); + str = str.trimmed(); + all = str; + ind = str.indexOf('='); + if ((ind > 0) && !(str[0] == '#')) { + sind = str.indexOf('#'); + if (sind > 0) { + comm = str.right(str.length() - sind - 1).trimmed(); + if (comm.length() > 0) type = comm[0]; + else type = "s"; + comm = comm.right(comm.length() - 1).trimmed(); + str = str.left(sind); + } else { + type = "s"; + comm = ""; + } + //name = str.left(ind).trimmed(); + tree = (prefix + str.left(ind).trimmed()).split(delim); + if (tree.front() == "include") { + name = str.right(str.length() - ind - 1).trimmed(); + QPIConfig * iconf = new QPIConfig(name, incdirs); + if (!iconf->dev) { + delete iconf; + } else { + inc_devs << iconf; + includes << iconf << iconf->includes; + updateIncludes(); + } + //piCout << "includes" << includes; + other.back() = src; + } else { + name = tree.back(); + tree.pop_back(); + entry = &root; + foreach (QString i, tree) { + te = entry->findChild(i); + if (te == 0) { + ce = new Entry(); + ce->delim = delim; + ce->_tab = tab; + ce->_line = lines; + ce->_name = i; + ce->_parent = entry; + entry->_children << ce; + entry = ce; + } else entry = te; + } + isNew = false; + ce = entry->findChild(name); + if (ce == 0) { + ce = new Entry(); + isNew = true; + } + ce->delim = delim; + ce->_tab = tab; + ce->_name = name; + ce->_value = str.right(str.length() - ind - 1).trimmed(); + ce->_type = type; + ce->_comment = comm; + ce->_line = lines; + ce->_all = all; + if (isNew) { + ce->_parent = entry; + entry->_children << ce; + } + } + } else other.back() = src; + lines++; + } + setEntryDelim(&root, delim); + buildFullNames(&root); + //if (content.isEmpty()) stream.setDevice(this); +} diff --git a/test/qad/utils/qpiconfig.h b/test/qad/utils/qpiconfig.h new file mode 100644 index 0000000..1c8a267 --- /dev/null +++ b/test/qad/utils/qpiconfig.h @@ -0,0 +1,280 @@ +#ifndef QPICONFIG_H +#define QPICONFIG_H + +#include "QFile" +#include "QVector" +#include "QStringList" +#include "QTextStream" + +#include +#include +#include +#include +#include + + +inline QByteArray QString2QByteArray(const QString & string) {return string.isEmpty() ? QByteArray() : qUncompress(QByteArray::fromBase64(string.toLatin1()));} +int QString2int(const QString & string); +inline QColor QString2QColor(const QString & string) {return (string.left(1) == "#" ? QColor(string.right(string.length() - 1).toInt(0, 16)) : QColor(QString2int(string)));} +QRect QString2QRect(const QString & string); +QRectF QString2QRectF(const QString & string); +QPoint QString2QPoint(const QString & string); +QPointF QString2QPointF(const QString & string); + +inline QString QColor2QString(const QColor & color) {QString s = color.name(); return "0x" + QString::number(color.alpha(), 16).rightJustified(2, '0') + s.right(s.length() - 1);} +inline QString QPoint2QString(const QPoint & point) {return QString::number(point.x()) + ";" + QString::number(point.y());} +inline QString QPointF2QString(const QPointF & point) {return QString::number(point.x()) + ";" + QString::number(point.y());} +inline QString QByteArray2QString(const QByteArray & array) {return array.isEmpty() ? QString() : QString(qCompress(array, 9).toBase64());} +QString QRect2QString(const QRect & rect); +QString QRectF2QString(const QRectF & rect); + +#define QPICONFIG_GET_VALUE \ + Entry & getValue(const QString & vname, const char * def, bool * exist = 0) {return getValue(vname, QString(def), exist);} \ + Entry & getValue(const QString & vname, const QStringList & def, bool * exist = 0) {return getValue(vname, def.join("%|%"), exist);} \ + Entry & getValue(const QString & vname, const bool def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const short def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const int def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const long def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const uchar def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const ushort def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const uint def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const ulong def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const float def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const double def, bool * exist = 0) {return getValue(vname, QString::number(def), exist);} \ + Entry & getValue(const QString & vname, const QColor & def, bool * exist = 0) {return getValue(vname, QColor2QString(def), exist);} \ + Entry & getValue(const QString & vname, const Qt::GlobalColor def, bool * exist = 0) {return getValue(vname, QColor(def), exist);} \ + Entry & getValue(const QString & vname, const QRect & def, bool * exist = 0) {return getValue(vname, QRect2QString(def), exist);} \ + Entry & getValue(const QString & vname, const QRectF & def, bool * exist = 0) {return getValue(vname, QRectF2QString(def), exist);} \ + Entry & getValue(const QString & vname, const QPoint & def, bool * exist = 0) {return getValue(vname, QPoint2QString(def), exist);} \ + Entry & getValue(const QString & vname, const QPointF & def, bool * exist = 0) {return getValue(vname, QPointF2QString(def), exist);} \ + Entry & getValue(const QString & vname, const QByteArray & def, bool * exist = 0) {return getValue(vname, QByteArray2QString(def), exist);} + +class QPIConfig: public QFile +{ + friend class Entry; + friend class Branch; + friend class QPIConfigWidget; +public: + enum FileType {Config, XML}; + + QPIConfig(const QString & path, QIODevice::OpenMode mode, QPIConfig::FileType type); + QPIConfig(const QString & path, QIODevice::OpenMode mode = QIODevice::ReadWrite); + QPIConfig(const QString & path, QPIConfig::FileType type); + QPIConfig(QString * str, QPIConfig::FileType type = QPIConfig::Config); + ~QPIConfig(); + + void setFile(const QString & path, QIODevice::OpenMode mode = QIODevice::ReadWrite); + void setString(QString * str); + + class Entry; + + class Branch: public QVector { + friend class QPIConfig; + friend class Entry; + public: + Branch() {;} + + Entry & getValue(const QString & vname, const QString & def = QString(), bool * exist = 0); + QPICONFIG_GET_VALUE + + Branch allLeaves(); + Branch getValues(const QString & name); + Branch getLeaves(); + Branch getBranches(); + Branch & filter(const QString & f); + bool isEntryExists(const QString & name) const {foreach (const Entry * i, *this) if (entryExists(i, name)) return true; return false;} + int indexOf(const Entry * e) {for (int i = 0; i < size(); ++i) if (at(i) == e) return i; return -1;} + + //void clear() {foreach (Entry * i, *this) delete i; QVector::clear();} + + private: + bool entryExists(const Entry * e, const QString & name) const; + void allLeaves(Branch & b, Entry * e) {foreach (Entry * i, e->_children) {if (i->isLeaf()) b << i; else allLeaves(b, i);}} + + static Entry _empty; + QString delim; + + }; + + class Entry { + friend class QPIConfig; + friend class Branch; + friend class QPIConfigWidget; + friend class ConfigValueWidget; + public: + Entry() {_parent = 0; _line = -1;} + + Entry * parent() const {return _parent;} + int childCount() {return _children.size();} + Branch & children() {_children.delim = delim; return _children;} + Entry * child(const int index) const {return _children[index];} + const Entry * findChild(const QString & name) const {foreach (const Entry * i, _children) if (i->_name == name) return i; return 0;} + Entry * findChild(const QString & name) {foreach (Entry * i, _children) if (i->_name == name) return i; return 0;} + bool isLeaf() const {return _children.empty();} + + const QString & name() const {return _name;} + const QString & value() const {return _value;} + const QString & type() const {return _type;} + const QString & comment() const {return _comment;} + + Entry & setName(const QString & value) {_name = value; return *this;} + Entry & setType(const QString & value) {_type = value; return *this;} + Entry & setComment(const QString & value) {_comment = value; return *this;} + Entry & setValue(const QString & value) {_value = value; return *this;} + Entry & setValue(const QStringList & value) {setValue(value.join("%|%")); setType("l"); return *this;} + Entry & setValue(const char * value) {setValue(QString(value)); setType("s"); return *this;} + Entry & setValue(const bool value) {setValue(QString::number(value)); setType("b"); return *this;} + Entry & setValue(const short value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const int value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const long value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const uchar value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const ushort value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const uint value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const ulong value) {setValue(QString::number(value)); setType("n"); return *this;} + Entry & setValue(const float value) {setValue(QString::number(value)); setType("f"); return *this;} + Entry & setValue(const double value) {setValue(QString::number(value)); setType("f"); return *this;} + Entry & setValue(const QColor & value) {setValue(QColor2QString(value)); setType("c"); return *this;} + Entry & setValue(const Qt::GlobalColor & value) {setValue(QColor(value)); return *this;} + Entry & setValue(const QRect & value) {setValue(QRect2QString(value)); setType("r"); return *this;} + Entry & setValue(const QRectF & value) {setValue(QRectF2QString(value)); setType("a"); return *this;} + Entry & setValue(const QPoint & value) {setValue(QPoint2QString(value)); setType("p"); return *this;} + Entry & setValue(const QPointF & value) {setValue(QPointF2QString(value)); setType("v"); return *this;} + Entry & setValue(const QByteArray & value) {setValue(QByteArray2QString(value)); setType("s"); return *this;} + + Entry & getValue(const QString & vname, const QString & def = QString(), bool * exist = 0); + QPICONFIG_GET_VALUE + + Branch getValues(const QString & vname); + + bool isEntryExists(const QString & name) const {return entryExists(this, name);} + const QString & stringValue() const {return _value;} + + operator bool() {return (_value.toLower().trimmed() == "true" || _value.toLower().trimmed() == "yes" || _value.toLower().trimmed() == "on" || _value.toInt() > 0);} + operator char() {return (_value.isEmpty() ? 0 : _value[0].toLatin1());} + operator short() {return _value.toShort();} + operator int() {return QString2int(_value);} + operator long() {return QString2int(_value);} + operator uchar() {return QString2int(_value);} + operator ushort() {return QString2int(_value);} + operator uint() {return QString2int(_value);} + operator ulong() {return QString2int(_value);} + operator float() {return _value.toFloat();} + operator double() {return _value.toDouble();} + operator QString() {return _value;} + operator QStringList() {return _value.split("%|%");} + operator QColor() {return QString2QColor(_value);} + operator QRect() {return QString2QRect(_value);} + operator QRectF() {return QString2QRectF(_value);} + operator QPoint() {return QString2QPoint(_value);} + operator QPointF() {return QString2QPointF(_value);} + operator QByteArray() {return QString2QByteArray(_value);} + + private: + static bool compare(const QPIConfig::Entry * f, const QPIConfig::Entry * s) {return f->_line < s->_line;} + bool entryExists(const Entry * e, const QString & name) const; + void buildLine() {_all = _tab + _full_name + " = " + _value + " #" + _type + " " + _comment;} + void clear() {_children.clear(); _name = _value = _type = _comment = _all = QString(); _line = 0; _parent = 0;} + void deleteBranch() {foreach (Entry * i, _children) {i->deleteBranch(); delete i;}} + + static Entry _empty; + Entry * _parent; + Branch _children; + QString _tab; + QString _name; + QString _value; + QString _type; + QString _comment; + QString _all; + QString _full_name; + QString delim; + int _line; + }; + + Entry & getValue(const QString & vname, const QString & def = QString(), bool * exist = 0); + QPICONFIG_GET_VALUE + + Branch getValues(const QString & vname); + + void setValue(const QString & name, const QString & value, const QString & type = "s", bool write = true); + void setValue(const QString & name, const QStringList & value, bool write = true) {setValue(name, value.join("%|%"), "l", write);} + void setValue(const QString & name, const char * value, bool write = true) {setValue(name, QString(value), "s", write);} + void setValue(const QString & name, const bool value, bool write = true) {setValue(name, QString::number(value), "b", write);} + void setValue(const QString & name, const short value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const int value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const long value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const uchar value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const ushort value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const uint value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const ulong value, bool write = true) {setValue(name, QString::number(value), "n", write);} + void setValue(const QString & name, const float value, bool write = true) {setValue(name, QString::number(value), "f", write);} + void setValue(const QString & name, const double value, bool write = true) {setValue(name, QString::number(value), "f", write);} + void setValue(const QString & name, const QColor & value, bool write = true) {setValue(name, QColor2QString(value), "c", write);} + void setValue(const QString & name, const Qt::GlobalColor & value, bool write = true) {setValue(name, QColor(value), write);} + void setValue(const QString & name, const QRect & value, bool write = true) {setValue(name, QRect2QString(value), "r", write);} + void setValue(const QString & name, const QRectF & value, bool write = true) {setValue(name, QRectF2QString(value), "a", write);} + void setValue(const QString & name, const QPoint & value, bool write = true) {setValue(name, QPoint2QString(value), "p", write);} + void setValue(const QString & name, const QPointF & value, bool write = true) {setValue(name, QPointF2QString(value), "v", write);} + void setValue(const QString & name, const QByteArray & value, bool write = true) {setValue(name, QByteArray2QString(value), "s", write);} + + Entry & rootEntry() {return root;} + int entriesCount() const {return childCount(&root);} + bool isEntryExists(const QString & name) const {return entryExists(&root, name);} + + Branch allTree() {Branch b; foreach (Entry * i, root._children) b << i; return b;} + Branch allLeaves() {Branch b; allLeaves(b, &root); qSort(b.begin(), b.end(), Entry::compare); return b;} + int entryIndex(const QString & name); + + QString getName(uint number) {return entryByIndex(number)._name;} + QString getValue(uint number) {return entryByIndex(number)._value;} + QChar getType(uint number) {return entryByIndex(number)._type[0];} + QString getComment(uint number) {return entryByIndex(number)._comment;} + + QPIConfig::Entry & addEntry(const QString & name, const QString & value, const QString & type = "s", bool write = true, bool node = false); + void setName(uint number, const QString & name, bool write = true); + void setValue(uint number, const QString & value, bool write = true); + void setType(uint number, const QString & type, bool write = true); + void setComment(uint number, const QString & comment, bool write = true); + + void removeEntry(const QString & name, bool write = true); + void removeEntry(uint number, bool write = true); + + void clear(); + void readAll(); + void writeAll(); + QString writeAllToString(); + void readAllFromString(const QString & str); + + const QString & delimiter() const {return delim;} + void setDelimiter(const QString & d) {delim = d; setEntryDelim(&root, d); readAll();} + +private: + QPIConfig(const QString & path, QStringList dirs); + void init(); + int childCount(const Entry * e) const {int c = 0; foreach (const Entry * i, e->_children) c += childCount(i); c += e->_children.size(); return c;} + bool entryExists(const Entry * e, const QString & name) const; + void buildFullNames(Entry * e) {foreach (Entry * i, e->_children) {if (e != &root) i->_full_name = e->_full_name + delim + i->_name; else i->_full_name = i->_name; buildFullNames(i);}} + void allLeaves(Branch & b, Entry * e) {foreach (Entry * i, e->_children) {if ((!i->_value.isEmpty() && !i->isLeaf()) || i->isLeaf()) b << i; allLeaves(b, i);}} + void setEntryDelim(Entry * e, const QString & d) {foreach (Entry * i, e->_children) setEntryDelim(i, d); e->delim = d;} + inline Entry & entryByIndex(const int index) {Branch b = allLeaves(); if (index < 0 || index >= b.size()) return empty; return *(b[index]);} + void removeEntry(Branch & b, Entry * e); + void deleteEntry(Entry * e) {foreach (Entry * i, e->_children) deleteEntry(i); delete e;} + QString getPrefixFromLine(QString line, bool * exists); + void updateIncludes(); + QString parseLine(QString v); + void parse(QString content = QString()); + + int centry; + bool internal; + QVector includes, inc_devs; + Branch all_includes; + QFile * dev; + QStringList incdirs; + QString delim, * buffer; + Entry root, empty; + uint lines; + QStringList other; + QTextStream stream; + FileType type; +}; + +#endif // QPICONFIG_H diff --git a/test/qad/utils/qpievaluator.cpp b/test/qad/utils/qpievaluator.cpp new file mode 100644 index 0000000..d03fd77 --- /dev/null +++ b/test/qad/utils/qpievaluator.cpp @@ -0,0 +1,1047 @@ +#include "qpievaluator.h" + + +QPIEvaluatorContent::QPIEvaluatorContent() { + addFunction("arcsin", 1); + addFunction("arccos", 1); + addFunction("arctg", 1); + addFunction("arcctg", 1); + addFunction("random", 2); + addFunction("sin", 1); + addFunction("cos", 1); + addFunction("ctg", 1); + addFunction("tg", 1); + addFunction("exp", 1); + addFunction("cth", 1); + addFunction("sh", 1); + addFunction("ch", 1); + addFunction("th", 1); + addFunction("sqrt", 1); + addFunction("sqr", 1); + addFunction("pow", 2); + addFunction("abs", 1); + addFunction("ln", 1); + addFunction("lg", 1); + addFunction("log", 2); + addFunction("im", 1); + addFunction("re", 1); + addFunction("arg", 1); + addFunction("len", 1); + addFunction("conj", 1); + addFunction("sign", 1); + addFunction("rad", 1); + addFunction("deg", 1); + clearCustomVariables(); + //addVariable("n", 0.); + //addVariable("x1", 123); +} + + +bool QPIEvaluatorContent::setVariableValue(int index, complexd new_value) { + if (index < 0 || index >= variables.size()) return false; + variables[index].value = new_value; + return true; +} + + +bool QPIEvaluatorContent::setVariableName(int index, const QString & new_name) { + if (index < 0 || index >= variables.size()) return false; + variables[index].name = new_name; + return true; +} + + +void QPIEvaluatorContent::clearCustomVariables() { + var_index.clear(); + func_index.clear(); + variables.clear(); + addVariable("i", complexd_i); + addVariable("pi", atan(1.) * 4.); + addVariable("e", exp(1.)); + cv_count = variables.size(); +} + + +void QPIEvaluatorContent::sortVariables() { + var_index.clear(); + qSort(variables.begin(), variables.end()); + for (int i = 0; i < variables.size(); i++) + var_index[variables[i].name] = i; + /* + qDebug() << "---"; + for (int i = 0; i < variables.size(); i++) { + qDebug() << variables[i].name; + } + */ +} + + +QPIEvaluatorTypes::BaseFunctions QPIEvaluatorContent::getBaseFunction(const QString & name) { + if (name == "sin") return QPIEvaluatorTypes::bfSin; + if (name == "cos") return QPIEvaluatorTypes::bfCos; + if (name == "tg") return QPIEvaluatorTypes::bfTg; + if (name == "ctg") return QPIEvaluatorTypes::bfCtg; + if (name == "arcsin") return QPIEvaluatorTypes::bfArcsin; + if (name == "arccos") return QPIEvaluatorTypes::bfArccos; + if (name == "arctg") return QPIEvaluatorTypes::bfArctg; + if (name == "arcctg") return QPIEvaluatorTypes::bfArcctg; + if (name == "exp") return QPIEvaluatorTypes::bfExp; + if (name == "random") return QPIEvaluatorTypes::bfRandom; + if (name == "sh") return QPIEvaluatorTypes::bfSh; + if (name == "ch") return QPIEvaluatorTypes::bfCh; + if (name == "th") return QPIEvaluatorTypes::bfTh; + if (name == "cth") return QPIEvaluatorTypes::bfCth; + if (name == "sqrt") return QPIEvaluatorTypes::bfSqrt; + if (name == "sqr") return QPIEvaluatorTypes::bfSqr; + if (name == "pow") return QPIEvaluatorTypes::bfPow; + if (name == "abs") return QPIEvaluatorTypes::bfAbs; + if (name == "ln") return QPIEvaluatorTypes::bfLn; + if (name == "lg") return QPIEvaluatorTypes::bfLg; + if (name == "log") return QPIEvaluatorTypes::bfLog; + if (name == "im") return QPIEvaluatorTypes::bfIm; + if (name == "re") return QPIEvaluatorTypes::bfRe; + if (name == "arg") return QPIEvaluatorTypes::bfArg; + if (name == "len") return QPIEvaluatorTypes::bfLen; + if (name == "conj") return QPIEvaluatorTypes::bfConj; + if (name == "sign") return QPIEvaluatorTypes::bfSign; + if (name == "rad") return QPIEvaluatorTypes::bfRad; + if (name == "deg") return QPIEvaluatorTypes::bfDeg; + return QPIEvaluatorTypes::bfUnknown; +} + +const QString & QPIEvaluator::prepare(const QString & string) { + currentString = string.trimmed(); + if (currentString.isEmpty()) currentString = "0"; + replaceOperators(); + removeSpaces(); + checkBrackets(); + while (fillElements()) checkBrackets(); + while (setSignes()) fillElements(); + removeJunk(); + findUnknownVariables(); + return currentString; +} + + +void QPIEvaluator::removeSpaces() { + QString tmps = currentString; + for (int i = 0; i < tmps.length(); i++) { + if (tmps[i] == ' ' || tmps[i] == '\t') { + tmps.remove(i, 1); + i--; + } + } + currentString = tmps; +} + + +void QPIEvaluator::removeJunk() { + QChar cc; + bool junk = true; + int bcnt; + while (junk) { + if (currentString.left(1) != "(" || currentString.right(1) != ")") return; + bcnt = 1; + junk = false; + for (int i = 1; i < currentString.length(); i++) { + cc = currentString[i]; + if (cc == '(') bcnt++; + if (cc == ')') bcnt--; + if (bcnt == 0) { + if (i == currentString.length() - 1) { + currentString = currentString.mid(1, currentString.length() - 2); + elements.pop_front(); + elements.pop_back(); + junk = true; + break; + } else break; + } + } + } +} + + +void QPIEvaluator::replaceOperators() { + currentString.replace("==", "="); + currentString.replace("!=", ":"); + currentString.replace(">=", "}"); + currentString.replace("<=", "{"); + currentString.replace("&&", "&"); + currentString.replace("||", "|"); +} + + +void QPIEvaluator::makeOutput(QString & string) { + string.replace(":", "≠"); + string.replace("}", "≥"); + string.replace("{", "≤"); + string.replace("&", "⋀"); + string.replace("|", "⋁"); +} + + +void QPIEvaluator::findUnknownVariables() { + QString cvar; + unknownVars.clear(); + for (int i = 0; i < currentString.length(); i++) { + if (elements[i].var_num == -666) cvar += currentString[i]; + else { + if (cvar.length() == 0) continue; + unknownVars << cvar; + cvar = ""; + } + } + if (cvar.length() > 0) unknownVars << cvar; + unknownVars.removeDuplicates(); +} + + +bool QPIEvaluator::isSign(const QChar & ch) { + return ch == '+' || ch == '-' || + ch == '*' || ch == '/' || + ch == '%' || ch == '^' || + ch == '=' || ch == ':' || + ch == '>' || ch == '<' || + ch == '}' || ch == '{' || + ch == '&' || ch == '|'; +} + + +void QPIEvaluator::checkBrackets() { + QString tmps = currentString; + QChar fc, sc; + int bcnt = 0, bpos = 0, inserted = 0; + currentString = tmps; + for (int i = 0; i < tmps.length(); i++) { + if (tmps[i] == '(') { + if (bcnt == 0) bpos = i; + bcnt++; + } + if (tmps[i] == ')') { + if (bcnt == 0) { + currentString.insert(bpos + inserted, "("); + inserted++; + } else bcnt--; + } + } + if (bcnt > 0) currentString += QString(bcnt, ')'); + tmps = currentString; + for (int i = 0; i < tmps.length() - 1; i++) { + fc = tmps[i].toLower(); + sc = tmps[i + 1].toLower(); + if ((fc == ')' && sc == '(') || + (fc == ')' && sc >= '0' && sc <= '9') || + (fc == ')' && sc >= 'a' && sc <= 'z') ) tmps.insert(++i, '*'); + } + currentString = tmps; +} + + +bool QPIEvaluator::fillElements() { + int fstart, flen, cnum = 0, cpart = 0, cfunc; + QChar cc, nc, pc, fc = '!'; + bool numFound = false; + QString curfind, tmps = currentString; + elements.resize(tmps.length()); + for (int i = 0; i < elements.size(); i++) { + elements[i].type = QPIEvaluatorTypes::etVariable; + elements[i].var_num = -666; + } + currentVariables.clear(); + //qDebug().nospace() << "search for functions ..."; + for (int i = 0; i < content.functionsCount(); i++) { + curfind = content.function(i).identifier; + cfunc = i; //(int)content.function(i).type; + flen = curfind.length(); + fstart = 0; + while (fstart >= 0) { + fstart = tmps.indexOf(curfind, fstart); + if (fstart < 0) break; + if (tmps[fstart + flen] != '(') { + //currentString.insert(fstart + flen, "("); + fstart++; + continue; + } + for (int j = fstart; j < fstart + flen; j++) { + elements[j].set(QPIEvaluatorTypes::etFunction, cnum, cfunc); + tmps.replace(j, 1, fc); + } + cnum++; + } + } + cnum = 0; + //qDebug().nospace() << "search for variables ..."; + for (int i = 0; i < content.variablesCount(); i++) { + curfind = content.variable(i).name; + flen = curfind.length(); + fstart = 0; + while (fstart >= 0) { + fstart = tmps.indexOf(curfind, fstart); + if (fstart < 0) break; + for (int j = fstart; j < fstart + flen; j++) { + elements[j].set(QPIEvaluatorTypes::etVariable, cnum, i); + tmps.replace(j, 1, fc); + } + cnum++; + } + } + curfind = ""; + cnum = 1; + //qDebug().nospace() << "search for numbers ..."; + for (int i = 0; i < tmps.length(); i++) { + cc = tmps[i]; + /*if (cc == " " || cc == "(" || cc == ")") { + curfind = ""; + cpart = 0; + numFound = false; + continue; + }*/ + switch (cpart) { + case 0: + if ((cc >= '0' && cc <= '9')) {// || cc == '-' || cc == '+') { + curfind += cc; + cpart = 1; + continue; + } + if (cc == '.') { + curfind += cc; + cpart = 2; + continue; + } + if (cc == 'E') { + curfind += cc; + cpart = 3; + continue; + } + break; + case 1: + if (cc >= '0' && cc <= '9') { + curfind += cc; + continue; + } + if (cc == '.') { + curfind += cc; + cpart = 2; + continue; + } + if (cc == 'E') { + curfind += cc; + cpart = 3; + continue; + } + numFound = true; + break; + case 2: + if (cc >= '0' && cc <= '9') { + curfind += cc; + continue; + } + if (cc == 'E') { + curfind += cc; + cpart = 3; + continue; + } + numFound = true; + break; + case 3: + if ((cc >= '0' && cc <= '9') || cc == '-' || cc == '+') { + curfind += cc; + cpart = 4; + continue; + } + numFound = true; + break; + case 4: + if (cc >= '0' && cc <= '9') { + curfind += cc; + continue; + } + numFound = true; + break; + } + if (numFound) { + //qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble(); + currentVariables.push_back(QPIEvaluatorTypes::Variable("tmp" + QString::number(cnum), curfind.toDouble())); + for (int j = i - curfind.length(); j < i; j++) { + elements[j].set(QPIEvaluatorTypes::etNumber, cnum, -cnum); + tmps.replace(j, 1, fc); + } + curfind = ""; + cnum++; + cpart = 0; + numFound = false; + } + } + if (cpart > 0) { + //qDebug().nospace() << "add " << cnum << ": " << curfind << " = " << curfind.toDouble(); + currentVariables.push_back(QPIEvaluatorTypes::Variable("tmp" + QString::number(cnum), curfind.toDouble())); + for (int j = tmps.length() - curfind.length(); j < tmps.length(); j++) { + elements[j].set(QPIEvaluatorTypes::etNumber, cnum, -cnum); + tmps.replace(j, 1, fc); + } + } + cc = nc = fc; + //qDebug().nospace() << "search for signes ..."; + for (int i = 0; i < tmps.length(); i++) { + cc = tmps[i]; + if (i > 0) pc = tmps[i - 1]; + else pc = fc; + if (i < tmps.length() - 1) nc = tmps[i + 1]; + else nc = fc; + if (cc == '(' || cc == ')' || cc == ',') { + elements[i].set(QPIEvaluatorTypes::etOperator, -1); + continue; + } + if (cc == '-' || cc == '+') { + elements[i].set(QPIEvaluatorTypes::etOperator, -1); + if (i < tmps.length() - 1) if (elements[i + 1].type == QPIEvaluatorTypes::etVariable || + elements[i + 1].type == QPIEvaluatorTypes::etFunction) continue; + if ((pc == '(' || isSign(pc) || i == 0) && i < tmps.length() - 1) { + if (elements[i + 1].type != QPIEvaluatorTypes::etOperator) { + cnum = elements[i + 1].num; + elements[i].set(QPIEvaluatorTypes::etNumber, cnum); + tmps.replace(i, 1, fc); + ///cout << "found sign " << cc << " :" << cnum - 1 << endl; + if (cc == '-' && currentVariables.size() >= cnum) + currentVariables[cnum - 1].value = -currentVariables[cnum - 1].value; + //i++; + continue; + } + } + } + if (isSign(cc)) { + elements[i].set(QPIEvaluatorTypes::etOperator, -1); + continue; + } + } + /* + qDebug().nospace() << tmps; + cout << " "; + for (int i = 0; i < elements.size(); i++) { + switch (elements[i].type) { + case etFunction: cout << "f"; break; + case etNumber: cout << "n"; break; + case etOperator: cout << "o"; break; + case etVariable: cout << "v"; break; + } + } + cout << endl; + */ + return false; + //for (int i = 0; i < currentVariables.size(); i++) qDebug() << "var " << i << ": " << currentVariables[i].value.real(); +} + + +bool QPIEvaluator::setSignes() { + int inserted = 0, ni, pi = 0, needInsert = 0; + QChar fc, sc, pc; + QString tmps = currentString; + for (int i = 0; i < tmps.length() - 1; i++) { + needInsert = 0; + ni = i + 1; + if (i > 0) pi = i - 1; + fc = tmps[i].toLower(); + sc = tmps[ni].toLower(); + pc = tmps[pi].toLower(); + //if (elements[i].type == etOperator || elements[ni].type == etVariable) continue; + if (fc == ',' || sc == ',') continue; + if (elements[i].type == QPIEvaluatorTypes::etOperator && elements[ni].type == QPIEvaluatorTypes::etOperator) continue; + if (fc == ')' && (elements[ni].type == QPIEvaluatorTypes::etNumber || elements[ni].type == QPIEvaluatorTypes::etVariable || elements[ni].type == QPIEvaluatorTypes::etFunction)) needInsert = 1; + if (sc == '(' && (elements[i].type == QPIEvaluatorTypes::etNumber || elements[i].type == QPIEvaluatorTypes::etVariable)) needInsert = 1; + if (elements[i].type == QPIEvaluatorTypes::etNumber && elements[ni].type == QPIEvaluatorTypes::etNumber && elements[i].num != elements[ni].num) needInsert = 1; + if (elements[i].type == QPIEvaluatorTypes::etVariable && elements[ni].type == QPIEvaluatorTypes::etVariable && elements[i].num != elements[ni].num) needInsert = 1; + if ((elements[i].type == QPIEvaluatorTypes::etNumber && elements[ni].type == QPIEvaluatorTypes::etVariable) || (elements[i].type == QPIEvaluatorTypes::etVariable && elements[ni].type == QPIEvaluatorTypes::etNumber)) needInsert = 1; + if ((elements[i].type == QPIEvaluatorTypes::etNumber || elements[i].type == QPIEvaluatorTypes::etVariable) && elements[ni].type == QPIEvaluatorTypes::etFunction) needInsert = 1; + if (elements[i].type == QPIEvaluatorTypes::etFunction && elements[ni].type == QPIEvaluatorTypes::etFunction && elements[i].num != elements[ni].num) needInsert = 2; + if (elements[i].type == QPIEvaluatorTypes::etFunction && elements[ni].type != QPIEvaluatorTypes::etFunction && sc != '(') needInsert = 2; + if (elements[pi].type == QPIEvaluatorTypes::etOperator && (elements[ni].type == QPIEvaluatorTypes::etFunction || elements[ni].type == QPIEvaluatorTypes::etVariable) && fc == '-') needInsert = 3; + switch (needInsert) { + case 1: + currentString.insert(ni + inserted, "*"); + elements.insert(ni + inserted, QPIEvaluatorTypes::Element(QPIEvaluatorTypes::etOperator, -1)); + //inserted++; + //i++; + return true; + /*case 2: + currentString.insert(ni + inserted, ")"); + currentString.insert(ni + inserted, "("); + elements.insert(ni + inserted, Element(etOperator, -1)); + elements.insert(ni + inserted, Element(etOperator, -1)); + inserted++; + i++; + return true;*/ + case 3: + currentString.insert(ni + inserted, "1*"); + elements.insert(ni + inserted, QPIEvaluatorTypes::Element(QPIEvaluatorTypes::etOperator, -1)); + //inserted; + //i++; + return true; + } + } + /*if (elements[tmps.length() - 1].type == etFunction) { + currentString.insert(tmps.length() + inserted, ")"); + currentString.insert(tmps.length() + inserted, "("); + elements.insert(tmps.length() + inserted, Element(etOperator, -1)); + elements.insert(tmps.length() + inserted, Element(etOperator, -1)); + return true; + }*/ + return false; +} + + +void QPIEvaluator::convert() { + int j; + QPIEvaluatorTypes::Element ce, pe; + for (int i = 0; i < currentString.length(); i++) { + pe = elements[i]; + if (pe.type != QPIEvaluatorTypes::etFunction) continue; + j = i + 1; + while (j < currentString.length()) { + ce = elements[j]; + if (ce != pe) break; + j++; + } + currentString.replace(i, j - i, " "); + for (int k = i + 1; k < j; k++) elements.remove(i); + //i++; + } + for (int i = 0; i < currentString.length(); i++) { + pe = elements[i]; + if (pe.type != QPIEvaluatorTypes::etNumber) continue; + j = i + 1; + while (j < currentString.length()) { + ce = elements[j]; + if (ce != pe) break; + j++; + } + currentString.replace(i, j - i, " "); + for (int k = i + 1; k < j; k++) elements.remove(i); + //i++; + } + for (int i = 0; i < currentString.length(); i++) { + pe = elements[i]; + if (pe.type != QPIEvaluatorTypes::etVariable) continue; + j = i + 1; + while (j < currentString.length()) { + ce = elements[j]; + if (ce != pe) break; + j++; + } + currentString.replace(i, j - i, " "); + for (int k = i + 1; k < j; k++) elements.remove(i); + //i++; + } + /*qDebug().nospace() << currentString; + cout << " "; + for (int i = 0; i < elements.size(); i++) { + switch (elements[i].type) { + case etFunction: cout << "f"; break; + case etNumber: cout << "n"; break; + case etOperator: cout << "o"; break; + case etVariable: cout << "v"; break; + } + } + cout << endl;*/ +} + + + +const QString & QPIEvaluator::preprocess(const QString & string) { + static QString ret; + int lind; + ret = prepare(string); + convert(); + instructions.clear(); + //qDebug() << preproc->currentString; + variables = currentVariables; + lind = parse(currentString); + if (instructions.size() == 0) { + variables.push_back(QPIEvaluatorTypes::Variable()); + instructions.push_back(QPIEvaluatorTypes::Instruction(QPIEvaluatorTypes::oNone, QVector(1, lind), -variables.size())); + } + kvars = &(content.variables); + /* + cout << endl << "variables:" << endl; + for (int i = 0; i < variables.size(); i++) + cout << i << " value = " << variables[i].value << endl; + + cout << endl << "instructions:" << endl; + for (int i = 0; i < instructions.size(); i++) { + cout << i << endl; + cout << " operation " << instructions[i].operation << endl; + cout << " operators: "; + for (int j = 0; j < instructions[i].operators.size(); j++) + cout << instructions[i].operators[j] << "; "; + cout << endl << " function " << instructions[i].function << endl; + cout << " out " << instructions[i].out << endl; + } + */ + makeOutput(ret); + return ret; +} + + +QPIEvaluatorTypes::Operation QPIEvaluator::operationInOrder(const int & index) { + switch (index) { + case 0: return QPIEvaluatorTypes::oPower; + case 1: return QPIEvaluatorTypes::oMultiply; + case 2: return QPIEvaluatorTypes::oDivide; + case 3: return QPIEvaluatorTypes::oResidue; + case 4: return QPIEvaluatorTypes::oAdd; + case 5: return QPIEvaluatorTypes::oSubtract; + case 6: return QPIEvaluatorTypes::oEqual; + case 7: return QPIEvaluatorTypes::oNotEqual; + case 8: return QPIEvaluatorTypes::oGreaterEqual; + case 9: return QPIEvaluatorTypes::oSmallerEqual; + case 10: return QPIEvaluatorTypes::oGreater; + case 11: return QPIEvaluatorTypes::oSmaller; + case 12: return QPIEvaluatorTypes::oAnd; + case 13: return QPIEvaluatorTypes::oOr; + default: return QPIEvaluatorTypes::oNone; + } +} + + +int QPIEvaluator::parse(const QString & string, int offset) { + int slen = string.length(), /*facnt,*/ farg, bcnt, k; + QChar cc; + QPIEvaluatorTypes::Element ce; + QPIEvaluatorTypes::Function cfunc; + QPIEvaluatorTypes::Operation coper; + QString sbrackets, carg; + QVector args, atmp; + QVector opers; + + ///qDebug() << "to parse :" + string; + ///cout << " "; for (int i = 0; i < slen; i++) cout << preproc->elements[i + offset].type; cout << endl; + + for (int i = 0; i < slen; i++) { + ce = elements[i + offset]; + cc = string[i]; + switch (ce.type) { + case QPIEvaluatorTypes::etNumber: + args.push_back(ce.var_num); + continue; + case QPIEvaluatorTypes::etVariable: + args.push_back(ce.var_num); + continue; + case QPIEvaluatorTypes::etFunction: + i++; + cfunc = content.function(ce.var_num); + //facnt = cfunc.arguments; + atmp.clear(); + bcnt = farg = 1; + ///qDebug() << "function: " + cfunc.identifier; + //for (int k = 0; k < facnt; k++) { + carg = ""; + k = i + 1; + //if (string.size() <= k || k < 0) return -666; + while (bcnt > 0) { + //if (k < facnt - 1) fcomma = string.indexOf(',', j); + cc = string[k]; + switch (cc.toLatin1()) { + case '(': bcnt++; break; + case ')': + bcnt--; + if (bcnt == 0) { + ///qDebug() << "arument: " << carg; + atmp.push_back(parse(carg, k + offset - carg.length())); + k++; + carg = ""; + if (atmp.size() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back(); + continue; + } + break; + case ',': + if (bcnt == 1) { + ///qDebug() << "arument: " << carg; + atmp.push_back(parse(carg, k + offset - carg.length())); + k++; + carg = ""; + if (atmp.size() > 0) if (atmp.back() < 0 && farg > 0) farg = atmp.back(); + continue; + } + break; + } + carg += cc; + k++; + } + i = k - 1; + if (farg > 0) { + variables.push_back(QPIEvaluatorTypes::Variable()); + farg = -variables.size(); + } + instructions.push_back(QPIEvaluatorTypes::Instruction(QPIEvaluatorTypes::oFunction, atmp, farg, ce.var_num)); + args.push_back(farg); + //for (int i = 0; i < args.size(); i++) cout << preproc->currentVariables[-args[i]].value << endl; + //i = j + 1; + continue; + case QPIEvaluatorTypes::etOperator: + //qDebug() << "operator: " << cc; + if (cc == '(') { + sbrackets = inBrackets(string.right(slen - i)); + args.push_back(parse(sbrackets, i + offset + 1)); + i += sbrackets.length() + 1; + continue; + } + if (cc == '+') {opers.push_back(QPIEvaluatorTypes::oAdd); continue;} + if (cc == '-') {opers.push_back(QPIEvaluatorTypes::oSubtract); continue;} + if (cc == '*') {opers.push_back(QPIEvaluatorTypes::oMultiply); continue;} + if (cc == '/') {opers.push_back(QPIEvaluatorTypes::oDivide); continue;} + if (cc == '%') {opers.push_back(QPIEvaluatorTypes::oResidue); continue;} + if (cc == '^') {opers.push_back(QPIEvaluatorTypes::oPower); continue;} + if (cc == '=') {opers.push_back(QPIEvaluatorTypes::oEqual); continue;} + if (cc == ':') {opers.push_back(QPIEvaluatorTypes::oNotEqual); continue;} + if (cc == '}') {opers.push_back(QPIEvaluatorTypes::oGreaterEqual); continue;} + if (cc == '{') {opers.push_back(QPIEvaluatorTypes::oSmallerEqual); continue;} + if (cc == '>') {opers.push_back(QPIEvaluatorTypes::oGreater); continue;} + if (cc == '<') {opers.push_back(QPIEvaluatorTypes::oSmaller); continue;} + if (cc == '&') {opers.push_back(QPIEvaluatorTypes::oAnd); continue;} + if (cc == '|') {opers.push_back(QPIEvaluatorTypes::oOr); continue;} + } + } + /* + cout << "stack: " << endl << "args: "; + for (int i = 0; i < args.size(); i++) cout << args[i] << ", "; + cout << endl << "opers: "; + for (int i = 0; i < opers.size(); i++) cout << opers[i] << ", "; + */ + if (opers.size() == 0) { + if (args.size() > 0) return args.back(); + else return -666; + } + for (int i = 0; i < QPIEvaluatorTypes::operationCount; i++) { + coper = operationInOrder(i); + for (int j = 0; j < opers.size(); j++) { + if (coper == QPIEvaluatorTypes::oDivide || coper == QPIEvaluatorTypes::oMultiply) { + if (opers[j] != QPIEvaluatorTypes::oDivide && opers[j] != QPIEvaluatorTypes::oMultiply) continue; + } else { + if (opers[j] != coper) continue; + } + atmp.clear(); + if (j < args.size() && j >= 0) atmp.push_back(args[j]); + else atmp.push_back(-666); + if (j + 1 < args.size() && j >= -1) atmp.push_back(args[j + 1]); + else atmp.push_back(-666); + farg = 1; + if (atmp[0] < 0) farg = atmp[0]; + else { + if (atmp[1] < 0) farg = atmp[1]; + else { + variables.push_back(QPIEvaluatorTypes::Variable()); + farg = -variables.size(); + } + } + instructions.push_back(QPIEvaluatorTypes::Instruction(opers[j], atmp, farg)); + if (j >= 0 && j < args.size()) { + args.remove(j); + if (j < args.size()) args[j] = farg; + } + opers.remove(j); + j--; + } + } + return instructions.back().out; + ///cout << endl; +} + + +bool QPIEvaluator::check() { + QPIEvaluatorTypes::Instruction ci; + bool error; + if (unknownVars.size() > 0) { + lastError = "Unknown variables: \"" + unknownVars.join("\", \"") + "\""; + return false; + } + for (int i = 0; i < instructions.size(); i++) { + error = false; + ci = instructions[i]; + switch (ci.operation) { + case QPIEvaluatorTypes::oNone: break; + case QPIEvaluatorTypes::oFunction: + for (int j = 0; j < ci.operators.size(); j++) { + if (ci.operators[j] == -666) { //(ci.operators[j] < -variables.size() || ci.operators[j] >= kvars->size()) { + error = true; + break; + } + } + if (ci.operators.size() != content.function(ci.function).arguments || error) { + lastError = "Invalid arguments count for function \"" + content.function(ci.function).identifier + "\""; + return false; + } + break; + default: + if (ci.operators[0] == -666 || ci.operators[1] == -666) error = true; + if (ci.operators.size() != 2 || error) { + lastError = "Invalid arguments count for operation \" " + operationChar(ci.operation) + " \""; + return false; + } + break; + } + if (ci.out < -variables.size()) { + lastError = "Invalid variable index \"" + QString::number(ci.out) + "\""; + return false; + } + for (int j = 0; j < ci.operators.size(); j++) { + if (ci.operators[j] < -variables.size() || ci.operators[j] >= kvars->size()) { + lastError = "Invalid variable index \"" + QString::number(ci.operators[j]) + "\""; + return false; + } + } + } + return true; +} + + +QString QPIEvaluator::inBrackets(const QString & string) { + int slen = string.length(), bcnt = 0; + QChar cc; + for (int i = 0; i < slen; i++) { + cc = string[i]; + if (cc == '(') bcnt++; + if (cc == ')') { + bcnt--; + if (bcnt == 0) return string.mid(1, i - 1); + } + } + return QString(); +} + + +QString QPIEvaluator::operationChar(const QPIEvaluatorTypes::Operation & operation) { + switch (operation) { + case QPIEvaluatorTypes::oAdd: return "+"; + case QPIEvaluatorTypes::oSubtract: return "-"; + case QPIEvaluatorTypes::oMultiply: return "*"; + case QPIEvaluatorTypes::oDivide: return "/"; + case QPIEvaluatorTypes::oPower: return "^"; + case QPIEvaluatorTypes::oResidue: return "%"; + case QPIEvaluatorTypes::oEqual: return "="; + case QPIEvaluatorTypes::oNotEqual: return ("≠"); + case QPIEvaluatorTypes::oGreaterEqual: return ("≥"); + case QPIEvaluatorTypes::oSmallerEqual: return ("≤"); + case QPIEvaluatorTypes::oGreater: return ">"; + case QPIEvaluatorTypes::oSmaller: return "<"; + case QPIEvaluatorTypes::oAnd: return ("⋀"); + case QPIEvaluatorTypes::oOr: return ("⋁"); + default: return "???"; + } +} + + +inline complexd QPIEvaluator::residue(const complexd & f, const complexd & s) { + complexd ret; + if (s.real() != 0.) ret = complexd(f.real() - ((int)(f.real() / s.real())) * s.real(), 0.); + if (s.imag() != 0.) ret = complexd(ret.real(), f.imag() - ((int)(f.imag() / s.imag())) * s.imag()); + return ret; +} + + +inline void QPIEvaluator::execFunction(const QPIEvaluatorTypes::Instruction & ci) { + QPIEvaluatorTypes::Function cfunc = content.function(ci.function); + int oi = -ci.out - 1; + complexd tmp, stmp; + double ldtmp; + //qDebug() << "function " << (int)cfunc.type; + switch (cfunc.type) { + case QPIEvaluatorTypes::bfSin: + tmpvars[oi].value = sin(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfCos: + tmpvars[oi].value = cos(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfTg: + tmpvars[oi].value = tan(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfCtg: + tmp = tan(value(ci.operators[0])); + if (tmp == complexd_0) tmpvars[oi].value = 0.; + else tmpvars[oi].value = complexd_1 / tmp; + break; + case QPIEvaluatorTypes::bfArcsin: + tmpvars[oi].value = asinc_qpie(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfArccos: + tmpvars[oi].value = acosc_qpie(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfArctg: + tmpvars[oi].value = atanc_qpie(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfArcctg: + tmp = atanc_qpie(value(ci.operators[0])); + if (tmp == complexd_0) tmpvars[oi].value = 0.; + else tmpvars[oi].value = complexd_1 / tmp; + break; + case QPIEvaluatorTypes::bfSh: + tmpvars[oi].value = sinh(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfCh: + tmpvars[oi].value = cosh(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfTh: + tmpvars[oi].value = tanh(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfCth: + tmp = tanh(value(ci.operators[0])); + if (tmp == complexd_0) tmpvars[oi].value = 0.; + else tmpvars[oi].value = complexd_1 / tmp; + break; + case QPIEvaluatorTypes::bfAbs: + tmpvars[oi].value = abs(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfSqrt: + tmpvars[oi].value = sqrt(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfSqr: + tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[0]); + break; + case QPIEvaluatorTypes::bfExp: + tmpvars[oi].value = exp(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfPow: + tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1])); + break; + case QPIEvaluatorTypes::bfLn: + tmpvars[oi].value = log(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfLg: + tmpvars[oi].value = log10(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfLog: + tmp = log(value(ci.operators[1])); + if (tmp == complexd_0) tmpvars[oi].value = 0.; + else tmpvars[oi].value = log(value(ci.operators[0])) / tmp; + break; + case QPIEvaluatorTypes::bfRe: + tmpvars[oi].value = value(ci.operators[0]).real(); + break; + case QPIEvaluatorTypes::bfIm: + tmpvars[oi].value = value(ci.operators[0]).imag(); + break; + case QPIEvaluatorTypes::bfArg: + tmpvars[oi].value = arg(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfLen: + tmpvars[oi].value = abs(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfConj: + tmpvars[oi].value = conj(value(ci.operators[0])); + break; + case QPIEvaluatorTypes::bfSign: + ldtmp = value(ci.operators[0]).real(); + tmpvars[oi].value = ldtmp >= 0. ? complexd_1 : -complexd_1; + break; + case QPIEvaluatorTypes::bfRad: + tmpvars[oi].value = value(ci.operators[0]) * complexd(deg2rad_qpie, 0.); + break; + case QPIEvaluatorTypes::bfDeg: + tmpvars[oi].value = value(ci.operators[0]) * complexd(rad2deg_qpie, 0.); + break; + case QPIEvaluatorTypes::bfRandom: + tmp = static_cast(qrand()) / RAND_MAX; + stmp = value(ci.operators[1]) - value(ci.operators[0]); + tmpvars[oi].value = value(ci.operators[0]) + tmp * stmp; + break; + default: break; + } +} + + +inline bool QPIEvaluator::execInstructions() { + QPIEvaluatorTypes::Instruction ci; + int oi; + complexd tmp; + tmpvars = variables; + //cout << "var count " << tmpvars.size() << endl; + for (int i = 0; i < instructions.size(); i++) { + ci = instructions[i]; + oi = -ci.out - 1; + //cout << value(ci.operators[0]) << operationChar(ci.operation) << value(ci.operators[1]) << ", " << oi << endl; + switch (ci.operation) { + case QPIEvaluatorTypes::oAdd: + tmpvars[oi].value = value(ci.operators[0]) + value(ci.operators[1]); + break; + case QPIEvaluatorTypes::oSubtract: + tmpvars[oi].value = value(ci.operators[0]) - value(ci.operators[1]); + break; + case QPIEvaluatorTypes::oMultiply: + tmpvars[oi].value = value(ci.operators[0]) * value(ci.operators[1]); + break; + case QPIEvaluatorTypes::oDivide: + tmp = value(ci.operators[1]); + if (tmp == complexd(0., 0.)) tmpvars[oi].value = 0.; + else tmpvars[oi].value = value(ci.operators[0]) / tmp; + break; + case QPIEvaluatorTypes::oResidue: + tmpvars[oi].value = residue(value(ci.operators[0]), value(ci.operators[1])); + break; + case QPIEvaluatorTypes::oPower: + tmpvars[oi].value = pow(value(ci.operators[0]), value(ci.operators[1])); + break; + case QPIEvaluatorTypes::oEqual: + tmpvars[oi].value = value(ci.operators[0]) == value(ci.operators[1]); + break; + case QPIEvaluatorTypes::oNotEqual: + tmpvars[oi].value = value(ci.operators[0]) != value(ci.operators[1]); + break; + case QPIEvaluatorTypes::oGreaterEqual: + tmpvars[oi].value = value(ci.operators[0]).real() >= value(ci.operators[1]).real(); + break; + case QPIEvaluatorTypes::oSmallerEqual: + tmpvars[oi].value = value(ci.operators[0]).real() <= value(ci.operators[1]).real(); + break; + case QPIEvaluatorTypes::oGreater: + tmpvars[oi].value = value(ci.operators[0]).real() > value(ci.operators[1]).real(); + break; + case QPIEvaluatorTypes::oSmaller: + tmpvars[oi].value = value(ci.operators[0]).real() < value(ci.operators[1]).real(); + break; + case QPIEvaluatorTypes::oAnd: + tmpvars[oi].value = value(ci.operators[0]).real() > 0. && value(ci.operators[1]).real() > 0.; + break; + case QPIEvaluatorTypes::oOr: + tmpvars[oi].value = value(ci.operators[0]).real() > 0. || value(ci.operators[1]).real() > 0.; + break; + case QPIEvaluatorTypes::oFunction: + execFunction(ci); + break; + case QPIEvaluatorTypes::oNone: + tmpvars[oi].value = value(ci.operators[0]); + break; + } + } + if (!instructions.isEmpty()) + out = value(instructions.back().out); + return true; +} + + +bool QPIEvaluator::check(const QString & string) { + currentString = preprocess(string); + correct = check(); + if (!correct) + return false; + lastError = "Correct"; + return true; +} + + +int QPIEvaluator::setVariable(const QString & name, complexd value) { + int i = content.findVariable(name); + if (i < 0) { + content.addVariable(name, value); + return content.findVariable(name); + } else { + content.setVariableValue(i, value); + return i; + } + return -1; +} + + +complexd QPIEvaluator::evaluate() { + if (!execInstructions()) out = 0.; + if (fabs(out.real()) < 1E-300) out = complexd(0., out.imag()); + if (fabs(out.imag()) < 1E-300) out = complexd(out.real(), 0.); + return out; +} diff --git a/test/qad/utils/qpievaluator.h b/test/qad/utils/qpievaluator.h new file mode 100644 index 0000000..d8213ad --- /dev/null +++ b/test/qad/utils/qpievaluator.h @@ -0,0 +1,178 @@ +#ifndef QPIEVALUATOR_H +#define QPIEVALUATOR_H + +#include +#include +#include +#include +#include +#include + +#ifndef PIP_MATH_COMPLEX +#define QPIEVALUATOR_COMPLEX +typedef std::complex complexd; +const complexd complexd_i(0., 1.); +const complexd complexd_0(0.); +const complexd complexd_1(1.); +#endif +const double deg2rad_qpie = atan(1.) / 45.; +const double rad2deg_qpie = 45. / atan(1.); + +inline complexd round_qpie(const complexd & c) {return complexd(round(c.real()), round(c.imag()));} +inline complexd floor_qpie(const complexd & c) {return complexd(floor(c.real()), floor(c.imag()));} +inline complexd ceil_qpie(const complexd & c) {return complexd(ceil(c.real()), ceil(c.imag()));} +inline complexd atanc_qpie(const complexd & c) {return -complexd(-0.5, 1.) * log((complexd_1 + complexd_i * c) / (complexd_1 - complexd_i * c));} +inline complexd asinc_qpie(const complexd & c) {return -complexd_i * log(complexd_i * c + sqrt(complexd_1 - c * c));} +inline complexd acosc_qpie(const complexd & c) {return -complexd_i * log(c + complexd_i * sqrt(complexd_1 - c * c));} + +namespace QPIEvaluatorTypes { + static const int operationCount = 14; + + enum eType {etNumber, etOperator, etVariable, etFunction}; + enum Operation {oNone, oAdd, oSubtract, oMultiply, oDivide, oResidue, oPower, + oEqual, oNotEqual, oGreater, oSmaller, oGreaterEqual, oSmallerEqual, + oAnd, oOr, oFunction}; + enum BaseFunctions {bfUnknown, bfSin, bfCos, bfTg, bfCtg, + bfArcsin, bfArccos, bfArctg, bfArcctg, + bfExp, bfRandom, bfSh, bfCh, bfTh, bfCth, + bfSqrt, bfSqr, bfPow, bfAbs, + bfLn, bfLg, bfLog, bfSign, + bfIm, bfRe, bfArg, bfLen, bfConj, + bfRad, bfDeg}; + + struct Instruction { + Instruction() {;} + Instruction(Operation oper, QVector opers, int out_ind, int func = -1) { + operation = oper; operators = opers; out = out_ind; function = func;} + Operation operation; + QVector operators; + int out; + int function;}; + struct Element { + Element() {;} + Element(eType new_type, int new_num, int new_var_num = -1) {set(new_type, new_num, new_var_num);} + void set(eType new_type, int new_num, int new_var_num = -1) {type = new_type; num = new_num; var_num = new_var_num;} + eType type; + int num; + int var_num;}; + struct Function { + Function() {arguments = 0; type = bfUnknown;} + Function(const QString & name, int args, BaseFunctions ftype) {identifier = name; arguments = args; type = ftype;} + QString identifier; + BaseFunctions type; + int arguments;}; + struct Variable { + Variable() {value = 0.;} + Variable(const QString & var_name, complexd val) {name = var_name; value = val;} + QString name; + complexd value;}; + inline bool operator <(const Variable & s1, const Variable & s2) { + if (s1.name.size() != s2.name.size()) + return s1.name.size() > s2.name.size(); + return s1.name > s2.name; + } +}; +/* + ≠ : + ≥ } + ≤ { + ⋀ & + ⋁ | +*/ +class QPIEvaluatorContent +{ + friend class QPIEvaluator; +public: + QPIEvaluatorContent(); + ~QPIEvaluatorContent() {;} + + void addFunction(const QString & name, int args = 1) {functions.push_back(QPIEvaluatorTypes::Function(name, args, getBaseFunction(name)));} + void addVariable(const QString & name, const complexd & val = 0., bool sort = true) {variables.push_back(QPIEvaluatorTypes::Variable(name, val)); if (sort) sortVariables();} + int functionsCount() const {return functions.size();} + int variablesCount() const {return variables.size();} + int customVariablesCount() const {return variables.size() - cv_count;} + int findFunction(const QString & name) const {return func_index.value(name, -1);} + int findVariable(const QString & name) const {return var_index.value(name, -1);} + QPIEvaluatorTypes::Function function(int index) {if (index < 0 || index >= functions.size()) return QPIEvaluatorTypes::Function(); return functions[index];} + QPIEvaluatorTypes::Variable variable(int index) {if (index < 0 || index >= variables.size()) return QPIEvaluatorTypes::Variable(); return variables[index];} + QPIEvaluatorTypes::Function function(const QString & name) {return function(findFunction(name));} + QPIEvaluatorTypes::Variable variable(const QString & name) {return variable(findVariable(name));} + QPIEvaluatorTypes::Variable customVariable(int index) {if (index < cv_count || index >= variables.size() + cv_count) return QPIEvaluatorTypes::Variable(); return variables[index + cv_count];} + bool setVariableValue(int index, complexd new_value); + bool setVariableName(int index, const QString & new_name); + bool setVariableValue(const QString & var_name, const complexd & new_value) {return setVariableValue(findVariable(var_name), new_value);} + bool setVariableName(const QString & var_name, const QString & new_name) {return setVariableName(findVariable(var_name), new_name);} + void removeVariable(int index) {variables.remove(index);} + void removeVariable(const QString & var_name) {removeVariable(findVariable(var_name));} + void clearCustomVariables(); + void sortVariables(); + QPIEvaluatorTypes::BaseFunctions getBaseFunction(const QString & name); + +private: + QVector functions; + QVector variables; + QMap var_index, func_index; + int cv_count; + +}; + +class QPIEvaluator +{ +public: + QPIEvaluator() {correct = false ;} + ~QPIEvaluator() {;} + + bool check(const QString & string); + bool isCorrect() const {return correct;} + int setVariable(const QString & name, complexd value = 0.); + void setVariable(int index, complexd value = 0.) {if (index >= 0 && index < content.variablesCount()) content.setVariableValue(index, value);} + void setCustomVariableValue(int index, complexd value = 0.) {content.variables[index + content.cv_count].value = value;} + complexd evaluate(); + void removeVariable(const QString & name) {content.removeVariable(name);} + void clearCustomVariables() {content.clearCustomVariables();} + int variableIndex(const QString & name) const {return content.findVariable(name);} + const QStringList & unknownVariables() const {return unknownVars;} + const QString & expression() const {return currentString;} + const QString & error() const {return lastError;} + const complexd & lastResult() const {return out;} + static QString inBrackets(const QString & string); + + QPIEvaluatorContent content; + +private: + const QString & prepare(const QString & string); + const QString & preprocess(const QString & string); + int parse(const QString & string, int offset = 0); + void convert(); + void checkBrackets(); + void removeSpaces(); + void findUnknownVariables(); + void removeJunk(); + void replaceOperators(); + void makeOutput(QString & string); + bool fillElements(); + bool setSignes(); + bool isSign(const QChar & ch); + QString inverse(const QString & string) {int len = string.length(); QString s; for (int i = 0; i < len; i++) s += string[len - i - 1]; return s;} + bool check(); + bool execInstructions(); + QString operationChar(const QPIEvaluatorTypes::Operation & operation); + QPIEvaluatorTypes::Operation operationInOrder(const int & index); + complexd value(const int & index) {if (index < 0) return tmpvars[-index - 1].value; else return kvars->at(index).value;} + inline complexd residue(const complexd & f, const complexd & s); + inline void execFunction(const QPIEvaluatorTypes::Instruction & ci); + + QVector elements; + QVector currentVariables, variables, tmpvars, * kvars; + QVector instructions; + QStringList unknownVars; + QString currentString, lastError; + complexd out; + bool correct; + +}; + +inline bool operator ==(QPIEvaluatorTypes::Element e1, QPIEvaluatorTypes::Element e2) {return (e1.type == e2.type && e1.num == e2.num);} +inline bool operator !=(QPIEvaluatorTypes::Element e1, QPIEvaluatorTypes::Element e2) {return (e1.type != e2.type || e1.num != e2.num);} + +#endif // QPIEVALUATOR_H diff --git a/test/qad/widgets/CMakeLists.txt b/test/qad/widgets/CMakeLists.txt new file mode 100644 index 0000000..6d5872d --- /dev/null +++ b/test/qad/widgets/CMakeLists.txt @@ -0,0 +1 @@ +qad_project(widgets "Gui;Widgets" "qad_utils") diff --git a/test/qad/widgets/chardialog.cpp b/test/qad/widgets/chardialog.cpp new file mode 100644 index 0000000..992564f --- /dev/null +++ b/test/qad/widgets/chardialog.cpp @@ -0,0 +1,151 @@ +#include "chardialog.h" +#include "ui_chardialog.h" + + +CharDialog::CharDialog(QWidget * parent): QDialog(parent) { + ui = new Ui::CharDialog(); + ui->setupUi(this); + QChar c; + int k; + for (int i = 0; i < 256; ++i) { + for (int j = 0; j < 256; ++j) { + c = QChar(j, i); + k = c.category(); + if (chars.size() <= k) + chars.resize(k + 1); + if (!c.isPrint()) continue; + + chars[k].push_back(c); + } + } + size = 30; + QStringList cat; + cat << tr("No Category") << tr("Mark NonSpacing") << tr("Mark SpacingCombining") << tr("Mark Enclosing") + << tr("Number DecimalDigit") << tr("Number Letter") << tr("Number Other") << tr("Separator Space") + << tr("Separator Line") << tr("Separator Paragraph") << tr("Other Control") << tr("Other Format") + << tr("Other Surrogate") << tr("Other PrivateUse") << tr("Other NotAssigned") << tr("Letter Uppercase") + << tr("Letter Lowercase") << tr("Letter Titlecase") << tr("Letter Modifier") << tr("Letter Other") + << tr("Punctuation Connector") << tr("Punctuation Dash") << tr("Punctuation Open") << tr("Punctuation Close") + << tr("Punctuation InitialQuote") << tr("Punctuation FinalQuote") << tr("Punctuation Other") << tr("Symbol Math") + << tr("Symbol Currency") << tr("Symbol Modifier") << tr("Symbol Other"); + ui->comboCategory->addItems(cat); + ui->spinSize->setValue(size); + ui->comboCategory->setCurrentIndex(27); + ui->tableChars->viewport()->installEventFilter(this); +} + + +CharDialog::~CharDialog() { + delete ui; +} + + +void CharDialog::setCharFont(const QFont & f) { + QFont fnt = ui->tableChars->font(); + fnt.setFamily(f.family()); + ui->tableChars->setFont(fnt); +} + + +void CharDialog::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + QStringList cat; + cat << tr("No Category") << tr("Mark NonSpacing") << tr("Mark SpacingCombining") << tr("Mark Enclosing") + << tr("Number DecimalDigit") << tr("Number Letter") << tr("Number Other") << tr("Separator Space") + << tr("Separator Line") << tr("Separator Paragraph") << tr("Other Control") << tr("Other Format") + << tr("Other Surrogate") << tr("Other PrivateUse") << tr("Other NotAssigned") << tr("Letter Uppercase") + << tr("Letter Lowercase") << tr("Letter Titlecase") << tr("Letter Modifier") << tr("Letter Other") + << tr("Punctuation Connector") << tr("Punctuation Dash") << tr("Punctuation Open") << tr("Punctuation Close") + << tr("Punctuation InitialQuote") << tr("Punctuation FinalQuote") << tr("Punctuation Other") << tr("Symbol Math") + << tr("Symbol Currency") << tr("Symbol Modifier") << tr("Symbol Other"); + int pi = ui->comboCategory->currentIndex(); + ui->comboCategory->clear(); + ui->comboCategory->addItems(cat); + ui->comboCategory->setCurrentIndex(pi); + return; + } + QDialog::changeEvent(e); +} + + +bool CharDialog::eventFilter(QObject * o, QEvent * e) { + if (o == ui->tableChars->viewport()) { + if (e->type() != QEvent::Wheel) + return QDialog::eventFilter(o, e); + qApp->sendEvent(ui->verticalScroll, e); + } + return QDialog::eventFilter(o, e); +} + + +void CharDialog::resizeEvent(QResizeEvent * ) { + int r = ui->tableChars->contentsRect().height() / csize, c = ui->tableChars->contentsRect().width() / csize; + ui->tableChars->setRowCount(r); + ui->tableChars->setColumnCount(c); + for (int i = 0; i < r; ++i) { + for (int j = 0; j < c; ++j) { + if (ui->tableChars->item(i, j) == 0) { + ui->tableChars->setItem(i, j, new QTableWidgetItem()); + ui->tableChars->item(i, j)->setTextAlignment(Qt::AlignCenter); + } + } + } + on_comboCategory_currentIndexChanged(ui->comboCategory->currentIndex()); +} + + +void CharDialog::clear() { + for (int i = 0; i < ui->tableChars->rowCount(); ++i) + for (int j = 0; j < ui->tableChars->columnCount(); ++j) + ui->tableChars->item(i, j)->setText(QString()); +} + + +void CharDialog::on_comboCategory_currentIndexChanged(int index) { + if (index < 0) return; + int r = ui->tableChars->rowCount(), c = ui->tableChars->columnCount(), m; + cur = &chars[index]; + if (r == 0) return; + m = (cur->size() - 2) / c - r + 1; + if (m < 0) m = 0; + ui->verticalScroll->setMinimum(0); + ui->verticalScroll->setMaximum(m); + on_verticalScroll_valueChanged(ui->verticalScroll->value()); +} + + +void CharDialog::on_verticalScroll_valueChanged(int index) { + int ci = ui->tableChars->columnCount() * index; + for (int i = 0; i < ui->tableChars->rowCount(); ++i) { + for (int j = 0; j < ui->tableChars->columnCount(); ++j) { + ++ci; + if (cur->size() > ci) ui->tableChars->item(i, j)->setText(cur->at(ci)); + else ui->tableChars->item(i, j)->setText(QString()); + } + } +} + + +void CharDialog::on_spinSize_valueChanged(int index) { + size = index; + csize = size * 2; + ui->tableChars->horizontalHeader()->setDefaultSectionSize(csize); + ui->tableChars->verticalHeader()->setDefaultSectionSize(csize); + on_comboCategory_currentIndexChanged(ui->comboCategory->currentIndex()); + QFont font = ui->tableChars->font(); + font.setPointSize(size); + ui->tableChars->setFont(font); + resizeEvent(0); +} + + +void CharDialog::on_tableChars_cellPressed(int row, int column) { + sel_char = ui->tableChars->item(row, column)->text()[0]; +} + + +void CharDialog::on_buttonBox_accepted() { + emit charSelected(sel_char); + accept(); +} diff --git a/test/qad/widgets/chardialog.h b/test/qad/widgets/chardialog.h new file mode 100644 index 0000000..675b611 --- /dev/null +++ b/test/qad/widgets/chardialog.h @@ -0,0 +1,50 @@ +#ifndef CHARDIALOG_H +#define CHARDIALOG_H + +#include +#include +#include + +namespace Ui { + class CharDialog; +}; + +class CharDialog: public QDialog +{ + Q_OBJECT +public: + explicit CharDialog(QWidget * parent = 0); + ~CharDialog(); + + QChar selectedChar() {return sel_char;} + void setCharFont(const QFont & f); + +public slots: + +private: + void changeEvent(QEvent * e); + virtual bool eventFilter(QObject * o, QEvent * e); + virtual void resizeEvent(QResizeEvent * ); + void clear(); + + Ui::CharDialog * ui; + QVector > chars; + QVector * cur; + QChar sel_char; + int size, csize; + +private slots: + void on_comboCategory_currentIndexChanged(int index); + void on_verticalScroll_valueChanged(int index); + void on_spinSize_valueChanged(int index); + void on_tableChars_cellPressed(int row, int column); + void on_tableChars_cellDoubleClicked(int , int ) {on_buttonBox_accepted();} + void on_buttonBox_accepted(); + void on_buttonBox_rejected() {reject();} + +signals: + void charSelected(QChar ch); + +}; + +#endif // CHARDIALOG_H diff --git a/test/qad/widgets/chardialog.ui b/test/qad/widgets/chardialog.ui new file mode 100644 index 0000000..7548b62 --- /dev/null +++ b/test/qad/widgets/chardialog.ui @@ -0,0 +1,92 @@ + + + CharDialog + + + + 0 + 0 + 467 + 448 + + + + Choose symbol + + + + :/icons/icons/mbricks_128.png:/icons/icons/mbricks_128.png + + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + false + + + 24 + + + false + + + 24 + + + + + + + 0 + + + Qt::Vertical + + + + + + + 1 + + + 200 + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + diff --git a/test/qad/widgets/clineedit.cpp b/test/qad/widgets/clineedit.cpp new file mode 100644 index 0000000..bf0845c --- /dev/null +++ b/test/qad/widgets/clineedit.cpp @@ -0,0 +1,67 @@ +#include "clineedit.h" +#include "qad_types.h" + + +CLineEdit::CLineEdit(QWidget * parent): QLineEdit(parent) { + cw = new QWidget(this); + clear_im.load(":/icons/edit-clear-locationbar-rtl.png"); + cw->setCursor(Qt::ArrowCursor); + cw->setToolTip(tr("Clear")); + cw->hide(); + cw->installEventFilter(this); + connect(this, SIGNAL(textChanged(QString)), this, SLOT(textChanged_(QString))); + int is = fontHeight(); + int m0, m1, m2, m3; + getTextMargins(&m0, &m1, &m2, &m3); + setTextMargins(m0, m1, m2 + (is * 1.2), m3); + //connect(cw, SIGNAL(mouseReleaseEvent(QMouseEvent * )), this, SLOT(clearMouseRelease(QMouseEvent * ))); +} + + +bool CLineEdit::eventFilter(QObject * o, QEvent * e) { + switch (e->type()) { + case QEvent::MouseButtonRelease: + clearMouseRelease((QMouseEvent * )e); + break; + case QEvent::Paint: + cwPaintEvent(); + break; + default : break; + } + return QLineEdit::eventFilter(o, e); +} + + +void CLineEdit::resizeEvent(QResizeEvent * e) { + QLineEdit::resizeEvent(e); + int is = fontHeight(), tm = (height() - is) / 2; + cw->setGeometry(width() - is - tm, tm, is, is); +} + + +void CLineEdit::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + cw->setToolTip(tr("Clear")); + return; + } + QLineEdit::changeEvent(e); +} + + +void CLineEdit::cwPaintEvent() { + QPainter p(cw); + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.drawImage(cw->rect(), clear_im); +} + + +void CLineEdit::setDefaultText(const QString & t, bool set_text) { + dt = t; + if (set_text) { + setText(t); + emit textEdited(t); + cw->hide(); + return; + } + textChanged_(text()); +} diff --git a/test/qad/widgets/clineedit.h b/test/qad/widgets/clineedit.h new file mode 100644 index 0000000..2ee32b1 --- /dev/null +++ b/test/qad/widgets/clineedit.h @@ -0,0 +1,45 @@ +#ifndef CLINEEDIT_H +#define CLINEEDIT_H + +#include +#include +#include +#include + + +class CLineEdit: public QLineEdit +{ + Q_OBJECT + Q_PROPERTY(QString defaultText READ defaultText WRITE setDefaultText) + +public: + explicit CLineEdit(QWidget * parent = 0); + ~CLineEdit() {delete cw;} + + const QString & defaultText() const {return dt;} + +protected: + QWidget * cw; + QString dt; + QImage clear_im; + +private: + bool eventFilter(QObject * o, QEvent * e); + void resizeEvent(QResizeEvent * ); + void changeEvent(QEvent * e); + void cwPaintEvent(); + +private slots: + void clearMouseRelease(QMouseEvent * e) {if (cw->rect().contains(e->pos())) clearClick();} + void textChanged_(QString text) {cw->setVisible(text != dt);} + +public slots: + void clearClick() {if (!isEnabled()) return; setText(dt); emit cleared(); emit textEdited(dt);} + void setDefaultText(const QString & t, bool set_text = false); + +signals: + void cleared(); + +}; + +#endif // CLINEEDIT_H diff --git a/test/qad/widgets/colorbutton.cpp b/test/qad/widgets/colorbutton.cpp new file mode 100644 index 0000000..215a05f --- /dev/null +++ b/test/qad/widgets/colorbutton.cpp @@ -0,0 +1,112 @@ +#include "colorbutton.h" +#include +#include +#include + +ColorButton::ColorButton(QWidget * parent): QPushButton(parent) { + frame = false; + options = QColorDialog::ShowAlphaChannel; + back = new QWidget(this); + back->setAutoFillBackground(true); + back->show(); + pal = back->palette(); + pal.setBrush(back->backgroundRole(), QBrush(QImage(":/icons/alpha.png"))); + back->setPalette(pal); + label = new QFrame(this); + label->setAutoFillBackground(true); + label->setFrameStyle(QFrame::Panel | QFrame::Sunken); + label->show(); + pal = label->palette(); + menu.addAction(QIcon(":/icons/edit-copy.png"), tr("Copy"), this, SLOT(copy())); + menu.addAction(QIcon(":/icons/edit-paste.png"), tr("Paste"), this, SLOT(paste())); + menu.addSeparator(); + menu.addAction(tr("Mix with clipboard"), this, SLOT(mix())); + setAcceptDrops(true); + connect(this, SIGNAL(clicked(bool)), this, SLOT(clicked())); +} + + +ColorButton::~ColorButton() { + delete label; +} + + +void ColorButton::resizeEvent(QResizeEvent * ) { + if (frame) back->setGeometry(rect()); + else back->setGeometry(8, 5, width() - 16, height() - 12); + label->setGeometry(back->geometry()); +} + + +void ColorButton::mousePressEvent(QMouseEvent * e) { + pp = e->pos(); + if (e->buttons().testFlag(Qt::RightButton)) { + menu.popup(e->globalPos()); + return; + } + QPushButton::mousePressEvent(e); +} + + +void ColorButton::mouseMoveEvent(QMouseEvent * e) { + if (e->buttons().testFlag(Qt::LeftButton)) { + if ((e->pos() - pp).manhattanLength() > QApplication::startDragDistance()) { + setDown(false); + QDrag * drag = new QDrag(this); + QMimeData * data = new QMimeData(); + data->setColorData(color()); + drag->setMimeData(data); + drag->exec(Qt::CopyAction); + return; + } + } + QPushButton::mouseMoveEvent(e); +} + + +void ColorButton::dragEnterEvent(QDragEnterEvent * e) { + e->accept(); + QPushButton::dragEnterEvent(e); +} + + +void ColorButton::dropEvent(QDropEvent * e) { + const QMimeData * data = e->mimeData(); + QColor c = qvariant_cast(data->colorData()); + if (c.isValid()) { + setColor(c); + return; + } + c = QColor(data->text()); + if (c.isValid()) { + setColor(c); + return; + } + QPushButton::dropEvent(e); +} + + +void ColorButton::clicked() { + QColor ret = QColorDialog::getColor(color(), this, tr("Choose color"), options); + if (!ret.isValid()) return; + setColor(ret); +} + + +void ColorButton::mix() { + QColor c(QApplication::clipboard()->text()); + if (!c.isValid()) return; + QColor sc = color(); + setColor(QColor((c.red() + sc.red()) / 2, (c.green() + sc.green()) / 2, (c.blue() + sc.blue()) / 2, (c.alpha() + sc.alpha()) / 2)); +} + + +void ColorButton::setColor(const QColor & col) { + if (pal.color(label->backgroundRole()) == col) return; + if (options.testFlag(QColorDialog::ShowAlphaChannel)) + pal.setColor(label->backgroundRole(), col); + else + pal.setColor(label->backgroundRole(), QColor(col.red(), col.green(), col.blue())); + label->setPalette(pal); + emit colorChanged(color()); +} diff --git a/test/qad/widgets/colorbutton.h b/test/qad/widgets/colorbutton.h new file mode 100644 index 0000000..37eb4e9 --- /dev/null +++ b/test/qad/widgets/colorbutton.h @@ -0,0 +1,63 @@ +#ifndef COLORBUTTON_H +#define COLORBUTTON_H + +#include +#include +#include +#include +#include +#include +#include +#include + + +class ColorButton: public QPushButton +{ + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor) + Q_PROPERTY(bool useNativeDialog READ useNativeDialog WRITE setUseNativeDialog) + Q_PROPERTY(bool useAlphaChannel READ useAlphaChannel WRITE setUseAlphaChannel) + Q_PROPERTY(bool frameOnly READ frameOnly WRITE setFrameOnly) + +public: + explicit ColorButton(QWidget * parent = 0); + ~ColorButton(); + + QColor color() const {return pal.color(label->backgroundRole());} + bool useNativeDialog() const {return !options.testFlag(QColorDialog::DontUseNativeDialog);} + bool useAlphaChannel() const {return options.testFlag(QColorDialog::ShowAlphaChannel);} + bool frameOnly() const {return frame;} + +public slots: + void setColor(const QColor & col); + void setUseNativeDialog(bool yes) {if (yes) options &= ~QColorDialog::DontUseNativeDialog; else options |= QColorDialog::DontUseNativeDialog;} + void setUseAlphaChannel(bool yes) {if (yes) options |= QColorDialog::ShowAlphaChannel; else options &= ~QColorDialog::ShowAlphaChannel;} + void setFrameOnly(bool yes) {frame = yes; setFlat(frame); resizeEvent(0);} + +private: + void mousePressEvent(QMouseEvent * e); + void mouseMoveEvent(QMouseEvent * e); + void resizeEvent(QResizeEvent * ); + void dragEnterEvent(QDragEnterEvent * e); + void dropEvent(QDropEvent * e); + + QFrame * label; + QWidget * back; + QPalette pal; + QPoint pp; + QMenu menu; + QColorDialog::ColorDialogOptions options; + bool frame; + +private slots: + void clicked(); + void copy() {QApplication::clipboard()->setText(color().name());} + void paste() {QColor c(QApplication::clipboard()->text()); if (c.isValid()) setColor(c);} + void mix(); + +signals: + void colorChanged(QColor); + +}; + +#endif // COLORBUTTON_H diff --git a/test/qad/widgets/ecombobox.cpp b/test/qad/widgets/ecombobox.cpp new file mode 100644 index 0000000..30def57 --- /dev/null +++ b/test/qad/widgets/ecombobox.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include "ecombobox.h" +#include "qad_types.h" + + +class EModel: public QStandardItemModel { +public: + EModel(QObject * parent = 0): QStandardItemModel(parent) { +#if QT_VERSION < 0x050000 + setSupportedDragActions(Qt::MoveAction); +#endif + } +protected: + virtual Qt::ItemFlags flags(const QModelIndex & index) const { + Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + if (!index.isValid()) f |= Qt::ItemIsDropEnabled; + return f; + } +#if QT_VERSION >= 0x050000 + Qt::DropActions supportedDragActions() const {return Qt::MoveAction;} +#endif +}; + + +EComboBox::EComboBox(QWidget * parent): QComboBox(parent) { + setView(&iv); + setModel(new EModel()); + iv.setTextElideMode(Qt::ElideMiddle); + iv.setRootIsDecorated(false); + iv.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + iv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + iv.setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + iv.setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + iv.setDragDropMode(QAbstractItemView::InternalMove); + iv.setMinimumHeight(100); + icon.setPixmap(QPixmap(":/icons/edit-find.png")); + icon.setScaledContents(true); + icon.setFixedSize(preferredIconSize(1.2, this)); + ifont = nfont = font(); + ifont.setItalic(true); +#if QT_VERSION >= 0x040700 + filter.setPlaceholderText(tr("Filter")); + filter.setFont(ifont); +#endif + header.setAutoFillBackground(true); + header.setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); + header.layout()->setSpacing(2); + header.layout()->setContentsMargins(2, 0, 0, 0); + header.layout()->addWidget(&icon); + header.layout()->addWidget(&filter); + header.setParent(iv.header()); + connect(&filter, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); +} + + +QSize EComboBox::sizeHint() const { + QSize s = QComboBox::sizeHint(); + s.setWidth(s.width() + 16); + return s; +} + + +void EComboBox::showPopup() { + filterChanged(filter.text(), true); + QComboBox::showPopup(); + QRect r = iv.header()->rect(); + header.setGeometry(r.x(), r.y(), r.width(), r.height() - 1); + filter.setFocus(); +} + + +void EComboBox::filterChanged(const QString & text, bool first) { + if (filter.text().isEmpty()) filter.setFont(ifont); + else filter.setFont(nfont); + iv.hide(); + QModelIndex pi = iv.rootIndex(); + if (text.isEmpty()) { + for (int i = 0; i < iv.model()->rowCount(); ++i) { + iv.setRowHidden(i, pi, false); + iv.model()->setData(iv.model()->index(i, 0), iv.model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole); + } + iv.show(); + if (first) return; + hidePopup(); + showPopup(); + qApp->processEvents(); + QRect r = iv.header()->rect(); + header.setGeometry(r.x(), r.y(), r.width(), r.height() - 1); + return; + } + for (int i = 0; i < iv.model()->rowCount(); ++i) { + iv.setRowHidden(i, pi, !iv.model()->index(i, 0, pi).data().toString().contains(QRegExp(text, Qt::CaseInsensitive))); + iv.model()->setData(iv.model()->index(i, 0), iv.model()->index(i, 0, pi).data().toString(), Qt::ToolTipRole); + } + iv.show(); + qApp->processEvents(); + QRect r = iv.header()->rect(); + header.setGeometry(r.x(), r.y(), r.width(), r.height() - 1); +} diff --git a/test/qad/widgets/ecombobox.h b/test/qad/widgets/ecombobox.h new file mode 100644 index 0000000..4ad832a --- /dev/null +++ b/test/qad/widgets/ecombobox.h @@ -0,0 +1,36 @@ +#ifndef ECOMBOBOX_H +#define ECOMBOBOX_H + +#include +#include +#include +#include +#include "clineedit.h" + + +class EComboBox: public QComboBox +{ + Q_OBJECT +public: + explicit EComboBox(QWidget * parent = 0); + + QSize sizeHint() const; + +public slots: + virtual void showPopup(); + +private: + QTreeView iv; + QWidget header; + QLabel icon; + CLineEdit filter; + QFont nfont, ifont; + +private slots: + void filterChanged(const QString & text, bool first = false); + +signals: + +}; + +#endif // ECOMBOBOX_H diff --git a/test/qad/widgets/evalspinbox.cpp b/test/qad/widgets/evalspinbox.cpp new file mode 100644 index 0000000..6bf2c8d --- /dev/null +++ b/test/qad/widgets/evalspinbox.cpp @@ -0,0 +1,337 @@ +#include "evalspinbox.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "qad_types.h" + + +EvalSpinBox::EvalSpinBox(QWidget * parent): QAbstractSpinBox(parent) { + status = new QWidget(lineEdit()); + cw = new QWidget(lineEdit()); + label = new QLabel(lineEdit()); +// label->hide(); + clear_im.load(":/icons/edit-clear-locationbar-rtl.png"); + icon_ok.load(":/icons/dialog-ok-apply.png"); + icon_fail.load(":/icons/dialog-warning.png"); + icon_calc.load(":/icons/tools-wizard.png"); + icon = icon_ok; + status->setCursor(Qt::ArrowCursor); + status->setToolTip("OK -> 0"); + status->hide(); + cw->setCursor(Qt::ArrowCursor); + cw->setToolTip(tr("Clear")); + cw->hide(); + cw_visible = false; + //lineEdit()->setStyleSheet("color: darkgreen;"); + //lineEdit()->setText(eval.expression() + " -> " + QString::number(value(), 'G', 10)); + cw->installEventFilter(this); + status->installEventFilter(this); + connect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(textChanged_(QString))); + connect(this, SIGNAL(editingFinished()), this, SLOT(setExpression_())); + label->setText("0"); + //connect(cw, SIGNAL(mouseReleaseEvent(QMouseEvent * )), this, SLOT(clearMouseRelease(QMouseEvent * ))); +} + + +EvalSpinBox::~EvalSpinBox() { + delete cw; + delete status; + delete label; +} + + +bool EvalSpinBox::eventFilter(QObject * o, QEvent * e) { + switch (e->type()) { + case QEvent::MouseButtonRelease: + if (o == cw) clearMouseRelease((QMouseEvent * )e); + break; + case QEvent::Paint: + if (o == status) statusPaintEvent(); + if (o == cw) cwPaintEvent(); + break; + default : break; + } + return QAbstractSpinBox::eventFilter(o, e); +} + + +void EvalSpinBox::resizeIcons() { + int is = fontHeight(); + int tm = (lineEdit()->height() - is + 1) / 2; + QStyleOptionFrame so; + so.initFrom(lineEdit()); + QRect r = style()->subElementRect(QStyle::SE_LineEditContents, &so, lineEdit()); + int m0, m1, m2, m3; + lineEdit()->getTextMargins(&m0, &m1, &m2, &m3); + //label->setGeometry(m0 + r.x() + 2, m1 + r.y() + (r.height() - fontMetrics().height() + 1) / 2, lineEdit()->width() - 2*tm - (is * 1.2) * ((status->isVisible() ? 1 : 0) + (cw->isVisible() ? 1 : 0)), lineEdit()->height() - 2*tm); + int lwh = label->sizeHint().width(); + label->setGeometry(lineEdit()->width() - m0 - lwh + r.x() - 2, + m1 + r.y() + (r.height() - fontMetrics().height() + 1) / 2, + lwh,// - 2*tm - (is * 1.2) * ((status->isVisible() ? 1 : 0) + (cw->isVisible() ? 1 : 0)), + lineEdit()->height() - 2*tm); + status->setGeometry(lineEdit()->width() - (is + tm) * (cw->isVisible() ? 2 : 1), tm, is, is); + cw->setGeometry(lineEdit()->width() - (is + tm) * 1, tm, is, is); + lineEdit()->setTextMargins(m0, m1, (is * 1.2) * ((status->isVisible() ? 1 : 0) + (cw->isVisible() ? 1 : 0)), m3); +} + + +void EvalSpinBox::resizeEvent(QResizeEvent * e) { + QAbstractSpinBox::resizeEvent(e); + resizeIcons(); +} + + +void EvalSpinBox::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + cw->setToolTip(tr("Clear")); + return; + } + QAbstractSpinBox::changeEvent(e); +} + + +void EvalSpinBox::cwPaintEvent() { + QPainter p(cw); + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.drawImage(cw->rect(), clear_im); +} + + +void EvalSpinBox::statusPaintEvent() { + QPainter p(status); + p.setRenderHint(QPainter::SmoothPixmapTransform); + p.drawImage(status->rect(), icon); +} + + +void EvalSpinBox::clearMouseRelease(QMouseEvent * e) { + if (cw->rect().contains(e->pos())) clear(); +} + + +void EvalSpinBox::textChanged_(const QString & text) { + double pv = value(); + QString t = text; + cw->setVisible(text != dt && cw_visible); + bool td = false; + if (t.endsWith('=')) { + td = true; + t.chop(1); + } + bool ok = eval.check(t); + if (ok) { + eval.evaluate(); + if (td) { + icon = icon_calc; + status->setToolTip("Enter to calc -> "+QString::number(value(), 'G', 10)); + } else { + icon = icon_ok; + status->setToolTip("OK -> "+QString::number(value(), 'G', 10)); + } +// qDebug() << "value =" << value(); + if (pv != value()) emit valueChanged(value()); + } else { + icon = icon_fail; + status->setToolTip(eval.error()); + } + resizeIcons(); +} + + +void EvalSpinBox::setExpression_() { + bool td = false; + double pv = value(); + QString t = text(); + if (t.endsWith('=')) { + td = true; + t.chop(1); + } + if (eval.check(t)) { + /*if (eval.expression() == "0") lineEdit()->clear(); + else*/ lineEdit()->setText(eval.expression()); + eval.evaluate(); + if (td) lineEdit()->setText(QString::number(value(), 'G', 10)); + status->setToolTip("OK -> " + QString::number(value(), 'G', 10)); + icon = icon_ok; + } else { + icon = icon_fail; + status->setToolTip(eval.error()); +// qDebug() << eval.expression(); + } + if (!label->isHidden()) { +// if (eval.expression() != QString::number(value(), 'G', 10) && eval.expression() != QString::number(value(), 'G', 11) && eval.isCorrect()) +// label->setText("

" + eval.expression() + " -> " + QString::number(value(), 'G', 10) + "

"); +// else +// label->setText(eval.expression()); + if (eval.expression() != QString::number(value(), 'G', 10) && eval.expression() != QString::number(value(), 'G', 11) && eval.isCorrect()) + label->setText("

-> " + QString::number(value(), 'G', 10) + "

"); + else + label->setText(""); + lineEdit()->blockSignals(true); + if (!eval.isCorrect()) { + lineEdit()->setStyleSheet("color: darkred;"); + status->show(); + } else { + lineEdit()->setStyleSheet(""); + status->hide(); + } + // lineEdit()->setText(eval.expression() + " -> " + QString::number(value(), 'G', 10)); + //lineEdit()->setText(""); + lineEdit()->blockSignals(false); + } + +// qDebug() << "value =" << value(); + if (pv != value()) emit valueChanged(value()); +} + + +void EvalSpinBox::setExpression(const QString & expr) { + lineEdit()->setText(expr); + //if (eval.expression() == "0") lineEdit()->clear(); + cw->setVisible(text() != dt && cw_visible); + setExpression_(); +} + + +void EvalSpinBox::setValue(double val) { + lineEdit()->setText(QString::number(val, 'G', 16)); + //if (val == 0) lineEdit()->clear(); + cw->setVisible(text() != dt && cw_visible); + setExpression_(); +} + + +void EvalSpinBox::stepBy(int steps) { + stepByDouble(steps); +} + + +void EvalSpinBox::clear() { + lineEdit()->setText(dt); + setExpression_(); + cw->hide(); + resizeIcons(); + emit cleared(); +} + + +double EvalSpinBox::value() const { + if (eval.isCorrect()) { + return eval.lastResult().real(); + } + return 0.; +} + + +const QString & EvalSpinBox::expression() const { + return eval.expression(); +} + + +bool EvalSpinBox::isCleared() const { + return (dt == eval.expression() || (value() == 0 && dt.isEmpty())); +} + + +QSize EvalSpinBox::sizeHint() const { + QSize s = QAbstractSpinBox::sizeHint(); + s.setWidth(120); + return s; +} + + +QAbstractSpinBox::StepEnabled EvalSpinBox::stepEnabled() const { + return StepUpEnabled | StepDownEnabled; +} + + +void EvalSpinBox::focusInEvent(QFocusEvent * event) { +// qDebug() << "focus_in"; + label->hide(); + status->show(); + lineEdit()->blockSignals(true); + lineEdit()->setStyleSheet(""); + if (eval.expression() == "0") lineEdit()->clear(); + lineEdit()->blockSignals(false); + QAbstractSpinBox::focusInEvent(event); + resizeIcons(); +} + + +void EvalSpinBox::focusOutEvent(QFocusEvent * event) { + QAbstractSpinBox::focusOutEvent(event); +// qDebug() << eval.expression() << QString::number(value(), 'G', 10); +// if (eval.expression() != QString::number(value(), 'G', 10) && eval.expression() != QString::number(value(), 'G', 11) && eval.isCorrect()) +// label->setText("

" + eval.expression() + " -> " + QString::number(value(), 'G', 10) + "

"); +// else +// label->setText(eval.expression()); + if (eval.expression() != QString::number(value(), 'G', 10) && eval.expression() != QString::number(value(), 'G', 11) && eval.isCorrect()) + label->setText("

-> " + QString::number(value(), 'G', 10) + "

"); + else + label->setText(""); + label->show(); + lineEdit()->blockSignals(true); + if (!eval.isCorrect()) lineEdit()->setStyleSheet("color: darkred;"); + else status->hide(); +// lineEdit()->setText(eval.expression() + " -> " + QString::number(value(), 'G', 10)); + //lineEdit()->clear(); + lineEdit()->blockSignals(false); + resizeIcons(); +} + + +void EvalSpinBox::wheelEvent(QWheelEvent * event) { + if (event->modifiers().testFlag(Qt::ShiftModifier)) + stepByDouble(event->delta() > 0 ? 0.1 : -0.1); + else + QAbstractSpinBox::wheelEvent(event); +} + + +void EvalSpinBox::stepByDouble(double steps) { + //qDebug() << "step" << steps; + if (isReadOnly()) return; + QString t = text(); + if (eval.check(t)) { + t = eval.expression(); + //QRegExp re("(\\-?\\d+)"); + QRegExp re("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)"); + int pos = 0; + if ((pos = re.indexIn(t)) != -1) { + double v = t.mid(pos, re.matchedLength()).toDouble(); + v += steps; + t.remove(pos, re.matchedLength()); + t.insert(pos, QString::number(v)); + } else { + double v = steps; + t = QString::number(v) + t; + } + eval.check(t); + lineEdit()->setText(eval.expression()); + } +} + + +void EvalSpinBox::setDefaultText(const QString & t) { +// bool def = (!cw->isHidden()); + dt = t; +// if (def) { +// lineEdit()->setText(dt); +// setExpression_(); +// } + //if (t == eval.expression() || (value() == 0 && t.isEmpty())) clear(); + cw->setVisible((eval.expression() != dt || (dt.isEmpty() && eval.expression() == "0")) && cw_visible); + resizeIcons(); +} + + +void EvalSpinBox::setClearButtonVisible(bool visible) { + cw_visible = visible; + cw->setVisible((eval.expression() != dt || (dt.isEmpty() && eval.expression() == "0")) && cw_visible); + resizeIcons(); +} diff --git a/test/qad/widgets/evalspinbox.h b/test/qad/widgets/evalspinbox.h new file mode 100644 index 0000000..43ad094 --- /dev/null +++ b/test/qad/widgets/evalspinbox.h @@ -0,0 +1,79 @@ +#ifndef EVALSPINBOX_H +#define EVALSPINBOX_H + +#include +#include +#include "qpievaluator.h" + +class QLabel; + +class EvalSpinBox: public QAbstractSpinBox +{ + Q_OBJECT + Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged USER true) + Q_PROPERTY(QString expression READ expression WRITE setExpression USER true) + Q_PROPERTY(QString defaultText READ defaultText WRITE setDefaultText) + Q_PROPERTY(bool clearButtonVisible READ isClearButtonVisible WRITE setClearButtonVisible) + +public: + explicit EvalSpinBox(QWidget * parent = 0); + ~EvalSpinBox(); + + double value() const; + const QString & expression() const; + const QString & defaultText() const {return dt;} + bool isClearButtonVisible() const {return cw_visible;} + bool isCleared() const; + + virtual void stepBy(int steps); + virtual void clear(); + virtual QSize sizeHint() const; + +public slots: + void setExpression(const QString & expr); + void setValue(double val); + void setDefaultText(const QString & t); + void setClearButtonVisible(bool visible); + +protected: + QString text() const {return QAbstractSpinBox::text();} + + virtual StepEnabled stepEnabled() const; + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void wheelEvent(QWheelEvent *event); + + void stepByDouble(double steps); + + QWidget * status; + QWidget * cw; + QPIEvaluator eval; + QLabel * label; + QImage icon_ok; + QImage icon_fail; + QImage icon_calc; + QImage icon; + QImage clear_im; + QString dt; + bool cw_visible; + +private: + bool eventFilter(QObject * o, QEvent * e); + void resizeEvent(QResizeEvent * ); + void changeEvent(QEvent * e); + void statusPaintEvent(); + void cwPaintEvent(); + +private slots: + void clearMouseRelease(QMouseEvent * e); + void textChanged_(const QString & text); + void setExpression_(); + void resizeIcons(); + +signals: + void valueChanged(double val); + void cleared(); + +}; + +#endif // EVALSPINBOX_H diff --git a/test/qad/widgets/iconedlabel.cpp b/test/qad/widgets/iconedlabel.cpp new file mode 100644 index 0000000..e374b6c --- /dev/null +++ b/test/qad/widgets/iconedlabel.cpp @@ -0,0 +1,59 @@ +#include "iconedlabel.h" +#include "qad_types.h" +#include +#include + + +IconedLabel::IconedLabel(QWidget * parent): QFrame(parent) { + label_.setAlignment(Qt::AlignCenter); + icon_.setAlignment(Qt::AlignCenter); + icon_.setScaledContents(true); + setIconSize(QSize()); + setDirection(RightToLeft); +} + + +QIcon IconedLabel::icon() const { + return icon_.pixmap() == 0 ? QIcon() : QIcon(*icon_.pixmap()); +} + + +bool IconedLabel::event(QEvent * e) { + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) + setIconSize(iconSize()); + return QFrame::event(e); +} + + +QSize IconedLabel::realIconSize() const { + return size_.isValid() ? size_ : preferredIconSize(1.f, this); +} + + +void IconedLabel::setIcon(const QIcon & i) { + sicon_ = i; + setIconSize(iconSize()); +} + + +void IconedLabel::setIconSize(const QSize & s) { + size_ = s; + QSize sz = realIconSize(); + icon_.setPixmap(sicon_.pixmap(sz)); + icon_.setFixedSize(sz); +} + + +void IconedLabel::setDirection(IconedLabel::Direction d) { + dir_ = d; + if (layout() != 0) + delete layout(); + QLayout * lay = new QBoxLayout((QBoxLayout::Direction)dir_); + lay->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding)); + lay->addWidget(&label_); + lay->addWidget(&icon_); + lay->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding)); + lay->setContentsMargins(0, 0, 0, 0); + setLayout(lay); + update(); +} diff --git a/test/qad/widgets/iconedlabel.h b/test/qad/widgets/iconedlabel.h new file mode 100644 index 0000000..ab2317d --- /dev/null +++ b/test/qad/widgets/iconedlabel.h @@ -0,0 +1,53 @@ + #ifndef ICONEDLABEL_H +#define ICONEDLABEL_H + +#include +#include +#include "qad_types.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class IconedLabel: public QFrame +{ + Q_OBJECT + Q_ENUMS(Direction) + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QIcon icon READ icon WRITE setIcon) + Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) + Q_PROPERTY(Direction direction READ direction WRITE setDirection) +public: + enum Direction {LeftToRight = 0, RightToLeft = 1, TopToBottom = 2, BottomToTop = 3}; + + explicit IconedLabel(QWidget * parent = 0); + + QString text() const {return label_.text();} + QIcon icon() const; + QSize iconSize() const {return size_;} + Direction direction() const {return dir_;} + +protected: + virtual bool event(QEvent * e); + QSize realIconSize() const; + + QLabel label_, icon_; + QIcon sicon_; + QSize size_; + Direction dir_; + +public slots: + void setText(const QString & t) {label_.setText(t);} + void setIcon(const QIcon & i); + void setIconSize(const QSize & s); + void setDirection(Direction d); + +signals: + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ICONEDLABEL_H diff --git a/test/qad/widgets/image_view.cpp b/test/qad/widgets/image_view.cpp new file mode 100644 index 0000000..bb72fd0 --- /dev/null +++ b/test/qad/widgets/image_view.cpp @@ -0,0 +1,134 @@ +#include "image_view.h" +#include +#include +#include +#include +#include +#include +#include + + +ImageView::ImageView(QWidget * parent): QGraphicsView(parent) { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setDragMode(QGraphicsView::NoDrag); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setScene(new QGraphicsScene()); + item.setTransformationMode(Qt::SmoothTransformation); + item.setFlags(0); + scene()->addItem(&item); + viewport()->setAutoFillBackground(false); + viewport()->installEventFilter(this); + autofit_ = true; +} + + +ImageView::~ImageView() { +} + + +QPixmap ImageView::pixmap() const { + return item.pixmap(); +} + + +void ImageView::setPixmap(QPixmap pixmap) { + item.setPixmap(pixmap); + adjustView(); +} + + +void ImageView::setImage(const QImage & i) { + im_data.clear(); + if (i.isNull()) { + item.setPixmap(QPixmap()); + return; + } + QBuffer b(&im_data); b.open(QIODevice::ReadWrite); + i.save(&b, "png"); + b.close(); + item.setPixmap(QPixmap::fromImage(i)); + adjustView(); +} + + +void ImageView::setImage(const QByteArray & i) { + im_data = i; + if (i.isEmpty()) { + item.setPixmap(QPixmap()); + return; + } + item.setPixmap(QPixmap::fromImage(QImage::fromData(i))); + adjustView(); + +} + + +void ImageView::clear() { + im_data.clear(); + item.setPixmap(QPixmap()); +} + + +void ImageView::mouseDoubleClickEvent(QMouseEvent * e) { + autofit(); +} + + +void ImageView::mousePressEvent(QMouseEvent * e) { + QGraphicsView::mousePressEvent(e); + emit clicked(mapToScene(e->pos()), e->buttons()); +} + + +void ImageView::mouseMoveEvent(QMouseEvent * e) { + //if (e->buttons().testFlag(Qt::RightButton) && !autofit_ && isInteractive()) return;; + QGraphicsView::mouseMoveEvent(e); +} + + +void ImageView::wheelEvent(QWheelEvent * e) { + if (!e->modifiers().testFlag(Qt::ControlModifier) || !isInteractive()) return; + double scl = 1. + e->delta() / 500.; + //autofit_ = false; + //scale(scl, scl); +} + + +bool ImageView::eventFilter(QObject * o, QEvent * e) { + QMouseEvent * me = (QMouseEvent *)e; + switch (e->type()) { + case QEvent::Resize: + adjustView(); + break; + case QEvent::MouseButtonPress: + prev_pos = me->pos(); + break; + case QEvent::MouseMove: + if (!me->buttons().testFlag(Qt::RightButton) || autofit_ || !isInteractive()) break; + { + double scl = 1. / qSqrt(transform().determinant()); + QPointF dp = QPointF(me->pos() - prev_pos) * scl; + //qDebug() << dp; + //translate(0.00001, 0); + prev_pos = me->pos(); + } + break; + default: break; + } + return QGraphicsView::eventFilter(o, e); +} + + +void ImageView::adjustView() { + if (!autofit_) return; + setSceneRect(item.boundingRect()); + fitInView(&item, Qt::KeepAspectRatio); + centerOn(&item); +} + + +void ImageView::autofit() { + autofit_ = true; + adjustView(); +} diff --git a/test/qad/widgets/image_view.h b/test/qad/widgets/image_view.h new file mode 100644 index 0000000..475b799 --- /dev/null +++ b/test/qad/widgets/image_view.h @@ -0,0 +1,46 @@ +#ifndef IMAGE_VIEW_H +#define IMAGE_VIEW_H + +#include +#include + + +class ImageView: public QGraphicsView +{ + Q_OBJECT + Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) +public: + ImageView(QWidget * parent = 0); + ~ImageView(); + + void setImage(const QImage & i); + void setImage(const QByteArray & i); + QByteArray image() const {return im_data;} + QPixmap pixmap() const; + + void clear(); + +private: + void mouseDoubleClickEvent(QMouseEvent * e); + void mousePressEvent(QMouseEvent * e); + void mouseMoveEvent(QMouseEvent * e); + void wheelEvent(QWheelEvent * e); + bool eventFilter(QObject * o, QEvent * e); + void adjustView(); + + QGraphicsPixmapItem item; + QByteArray im_data; + QPoint prev_pos; + bool autofit_; + + +public slots: + void autofit(); + void setPixmap(QPixmap pixmap); + +signals: + void clicked(QPointF, Qt::MouseButtons); + +}; + +#endif // IMAGE_VIEW_H diff --git a/test/qad/widgets/lang/qad_widgets_ru.ts b/test/qad/widgets/lang/qad_widgets_ru.ts new file mode 100644 index 0000000..3c725ff --- /dev/null +++ b/test/qad/widgets/lang/qad_widgets_ru.ts @@ -0,0 +1,607 @@ + + + + + CLineEdit + + + + Clear + Сбросить + + + + CharDialog + + + Choose symbol + Выбрать символ + + + + + No Category + Вне категории + + + + + Mark NonSpacing + + + + + + Mark SpacingCombining + + + + + + Mark Enclosing + + + + + + Number DecimalDigit + + + + + + Number Letter + + + + + + Number Other + + + + + + Separator Space + + + + + + Separator Line + + + + + + Separator Paragraph + + + + + + Other Control + + + + + + Other Format + + + + + + Other Surrogate + + + + + + Other PrivateUse + + + + + + Other NotAssigned + + + + + + Letter Uppercase + + + + + + Letter Lowercase + + + + + + Letter Titlecase + + + + + + Letter Modifier + + + + + + Letter Other + + + + + + Punctuation Connector + + + + + + Punctuation Dash + + + + + + Punctuation Open + + + + + + Punctuation Close + + + + + + Punctuation InitialQuote + + + + + + Punctuation FinalQuote + + + + + + Punctuation Other + + + + + + Symbol Math + + + + + + Symbol Currency + + + + + + Symbol Modifier + + + + + + Symbol Other + + + + + ColorButton + + + Copy + Копировать + + + + Paste + Вставить + + + + Mix with clipboard + Смешать со скопированным + + + + Choose color + Выбрать цвет + + + + EComboBox + + + Filter + Фильтр + + + + PathEdit + + + All files(*) + Все файлы(*) + + + + + Choose + Выберите + + + + Select directory + Выберите директорию + + + + Select file + Выберите файл + + + + QCodeEdit + + + Press F1 for details + Нажмите F1 для справочной информации + + + + QPIConfigNewDialog + + + Dialog + + + + + Type + + + + + string + + + + + s + + + + + integer + + + + + n + + + + + float + + + + + f + + + + + string list + + + + + l + + + + + boolean + + + + + b + + + + + color + + + + + c + + + + + rectangle + + + + + r + + + + + area + + + + + a + + + + + point + + + + + p + + + + + vector + + + + + v + + + + + ip + + + + + i + + + + + Name: + + + + + Comment: + + + + + Value: + + + + + QPIConfigWidget + + + Name + Имя + + + + Value + Значение + + + + Type + Тип + + + + Comment + Описание + + + + string + + + + + string list + + + + + integer + + + + + float + + + + + boolean + + + + + color + + + + + rectangle + + + + + area + + + + + point + + + + + vector + + + + + ip + + + + + Add item ... + + + + + Add node ... + + + + + Convert to item + + + + + Convert to node + + + + + Remove + Удалить + + + + Expand all + Свернуть все + + + + Collapse all + Развернуть все + + + + QPointEdit + + + + X + + + + + + Y + + + + + QRectEdit + + + + X + + + + + + Y + + + + + + Height + Высота + + + + + Width + Ширина + + + + QVariantEdit + + + Invalid value + Неверное значение + + + + Shortcuts + + + + Command + Команда + + + + + Shortcut + Горячая клавиша + + + + StringListEdit + + + + Add + Добавить + + + + + Remove + Удалить + + + + + Clear + Очистить + + + diff --git a/test/qad/widgets/mathmatrixedit.cpp b/test/qad/widgets/mathmatrixedit.cpp new file mode 100644 index 0000000..f38a772 --- /dev/null +++ b/test/qad/widgets/mathmatrixedit.cpp @@ -0,0 +1,53 @@ +#include "mathmatrixedit.h" +#include "qvariantedit_custom.h" +#include "matrixedit.h" +#include + + +MathMatrixEdit::MathMatrixEdit(QWidget * parent): QWidget(parent) { + edit = new MatrixEdit(); + setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); + layout()->setContentsMargins(0, 0, 0, 0); + layout()->addWidget(edit); + connect(edit, SIGNAL(changed()), this, SIGNAL(valueChanged())); +} + + +MathMatrixEdit::~MathMatrixEdit() { + delete edit; +} + + +QVariant MathMatrixEdit::value() const { + return QVariant::fromValue(QAD::MathMatrix(edit->matrix())); +} + + +bool MathMatrixEdit::isReadOnly() const { + return edit->isReadOnly(); +} + + +void MathMatrixEdit::setValue(const QVariant & v) { + edit->setMatrix(v.value().m); +} + + +void MathMatrixEdit::setReadOnly(bool yes) { + edit->setReadOnly(yes); +} + + + + +class MathMatrixEditFactory: public QVariantEditorFactoryBase { +public: + MathMatrixEditFactory() {} + virtual QWidget * createEditor() {return new MathMatrixEdit();} +}; + + +__MathMatrixEditRegistrator__::__MathMatrixEditRegistrator__() { + QVariantEditorFactories::registerEditorFactory(qMetaTypeId(), new MathMatrixEditFactory()); + //__QADTypesRegistrator__::instance()->toString_funcs.insert(qMetaTypeId(), &QAD_IODevice_toString); +} diff --git a/test/qad/widgets/mathmatrixedit.h b/test/qad/widgets/mathmatrixedit.h new file mode 100644 index 0000000..c4da51a --- /dev/null +++ b/test/qad/widgets/mathmatrixedit.h @@ -0,0 +1,40 @@ +#ifndef MATH_MATRIX_EDIT_H +#define MATH_MATRIX_EDIT_H + +#include +#include "qad_types.h" + +class MatrixEdit; + +class MathMatrixEdit: public QWidget { + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) +public: + explicit MathMatrixEdit(QWidget * parent = 0); + ~MathMatrixEdit(); + + QVariant value() const; + bool isReadOnly() const; + +private: + MatrixEdit * edit; + +public slots: + void setValue(const QVariant & v); + void setReadOnly(bool yes); + +signals: + void valueChanged(); + +}; + + +class __MathMatrixEditRegistrator__ { +public: + __MathMatrixEditRegistrator__(); +}; + +static __MathMatrixEditRegistrator__ __mathmatrixeditregistrator__; + +#endif // MATH_MATRIX_EDIT_H diff --git a/test/qad/widgets/mathvectoredit.cpp b/test/qad/widgets/mathvectoredit.cpp new file mode 100644 index 0000000..d529e1c --- /dev/null +++ b/test/qad/widgets/mathvectoredit.cpp @@ -0,0 +1,53 @@ +#include "mathvectoredit.h" +#include "qvariantedit_custom.h" +#include "matrixedit.h" +#include + + +MathVectorEdit::MathVectorEdit(QWidget * parent): QWidget(parent) { + edit = new MatrixEdit(); + setLayout(new QBoxLayout(QBoxLayout::LeftToRight)); + layout()->setContentsMargins(0, 0, 0, 0); + layout()->addWidget(edit); + connect(edit, SIGNAL(changed()), this, SIGNAL(valueChanged())); +} + + +MathVectorEdit::~MathVectorEdit() { + delete edit; +} + + +QVariant MathVectorEdit::value() const { + return QVariant::fromValue(QAD::MathVector(edit->vector())); +} + + +bool MathVectorEdit::isReadOnly() const { + return edit->isReadOnly(); +} + + +void MathVectorEdit::setValue(const QVariant & v) { + edit->setVector(v.value().v); +} + + +void MathVectorEdit::setReadOnly(bool yes) { + edit->setReadOnly(yes); +} + + + + +class MathVectorEditFactory: public QVariantEditorFactoryBase { +public: + MathVectorEditFactory() {} + virtual QWidget * createEditor() {return new MathVectorEdit();} +}; + + +__MathVectorEditRegistrator__::__MathVectorEditRegistrator__() { + QVariantEditorFactories::registerEditorFactory(qMetaTypeId(), new MathVectorEditFactory()); + //__QADTypesRegistrator__::instance()->toString_funcs.insert(qMetaTypeId(), &QAD_IODevice_toString); +} diff --git a/test/qad/widgets/mathvectoredit.h b/test/qad/widgets/mathvectoredit.h new file mode 100644 index 0000000..fd41066 --- /dev/null +++ b/test/qad/widgets/mathvectoredit.h @@ -0,0 +1,40 @@ +#ifndef MATH_VECTOR_EDIT_H +#define MATH_VECTOR_EDIT_H + +#include +#include "qad_types.h" + +class MatrixEdit; + +class MathVectorEdit: public QWidget { + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly) +public: + explicit MathVectorEdit(QWidget * parent = 0); + ~MathVectorEdit(); + + QVariant value() const; + bool isReadOnly() const; + +private: + MatrixEdit * edit; + +public slots: + void setValue(const QVariant & v); + void setReadOnly(bool yes); + +signals: + void valueChanged(); + +}; + + +class __MathVectorEditRegistrator__ { +public: + __MathVectorEditRegistrator__(); +}; + +static __MathVectorEditRegistrator__ __mathvectoreditregistrator__; + +#endif // MATH_VECTOR_EDIT_H diff --git a/test/qad/widgets/matrixedit.cpp b/test/qad/widgets/matrixedit.cpp new file mode 100644 index 0000000..bed96f2 --- /dev/null +++ b/test/qad/widgets/matrixedit.cpp @@ -0,0 +1,149 @@ +#include "matrixedit.h" +#include "ui_matrixedit.h" +#include + + +MatrixEdit::MatrixEdit(QWidget * parent): QWidget(parent) { + ui = new Ui::MatrixEdit(); + ui->setupUi(this); + connect(ui->table, SIGNAL(cellChanged(int,int)), this, SIGNAL(changed())); + ro = false; +} + + +MatrixEdit::~MatrixEdit() { + delete ui; +} + + +bool MatrixEdit::isReadOnly() const { + return ro; +} + + +void MatrixEdit::setReadOnly(bool yes) { + ro = yes; + /// TODO +} + + +void MatrixEdit::setVectorMode(bool yes) { + if (yes) + ui->spinCols->setValue(1); + ui->labelCols->setHidden(yes); + ui->spinCols->setHidden(yes); + ui->buttonIdentity->setHidden(yes); +} + + +void MatrixEdit::clear(bool ident) { + int cc = ui->table->columnCount(); + for (int r = 0; r < ui->table->rowCount(); ++r) { + for (int c = 0; c < cc; ++c) { + QTableWidgetItem * i = ui->table->item(r, c); + if (!i) { + i = new QTableWidgetItem(); + ui->table->setItem(r, c, i); + } + i->setText((ident && (r == c)) ? "1" : "0"); + } + } + +} + + +QVector MatrixEdit::vector() const { + QVector ret; + if (ui->table->columnCount() < 1) return ret; + ret.resize(ui->table->rowCount()); + ret.fill(0.); + for (int r = 0; r < ret.size(); ++r) { + QTableWidgetItem * i = ui->table->item(r, 0); + if (!i) continue; + ret[r] = i->text().toDouble(); + } + return ret; +} + + +QVector > MatrixEdit::matrix() const { + QVector > ret; + if (ui->table->columnCount() < 1 || ui->table->rowCount() < 1) return ret; + int cc = ui->table->columnCount(); + ret.resize(ui->table->rowCount()); + for (int r = 0; r < ret.size(); ++r) { + ret[r].resize(cc); + ret[r].fill(0.); + for (int c = 0; c < cc; ++c) { + QTableWidgetItem * i = ui->table->item(r, c); + if (!i) continue; + ret[r][c] = i->text().toDouble(); + } + } + return ret; +} + + +void MatrixEdit::setVector(const QVector & v) { + clear(); + if (v.isEmpty()) return; + blockSignals(true); + ui->spinRows->setValue(v.size()); + setVectorMode(true); + for (int r = 0; r < v.size(); ++r) { + QTableWidgetItem * i = ui->table->item(r, 0); + if (!i) { + i = new QTableWidgetItem(); + ui->table->setItem(r, 0, i); + } + i->setText(QString::number(v[r])); + } + blockSignals(false); + emit changed(); +} + + +void MatrixEdit::setMatrix(const QVector > & v) { + clear(); + if (v.isEmpty()) return; + if (v[0].isEmpty()) return; + blockSignals(true); + int cc = v[0].size(); + ui->spinRows->setValue(v.size()); + ui->spinCols->setValue(cc); + setVectorMode(false); + for (int r = 0; r < v.size(); ++r) { + for (int c = 0; c < v[r].size(); ++c) { + QTableWidgetItem * i = ui->table->item(r, c); + if (!i) { + i = new QTableWidgetItem(); + ui->table->setItem(r, c, i); + } + i->setText(QString::number(v[r][c])); + } + } + blockSignals(false); + emit changed(); +} + + +void MatrixEdit::on_spinRows_valueChanged(int cnt) { + ui->table->setRowCount(cnt); + emit changed(); +} + + +void MatrixEdit::on_spinCols_valueChanged(int cnt) { + ui->table->setColumnCount(cnt); + emit changed(); +} + + +void MatrixEdit::on_buttonNull_clicked() { + clear(); +} + + +void MatrixEdit::on_buttonIdentity_clicked() { + clear(true); +} diff --git a/test/qad/widgets/matrixedit.h b/test/qad/widgets/matrixedit.h new file mode 100644 index 0000000..e8c2c8d --- /dev/null +++ b/test/qad/widgets/matrixedit.h @@ -0,0 +1,43 @@ +#ifndef MATRIXEDIT_H +#define MATRIXEDIT_H + +#include + +namespace Ui { + class MatrixEdit; +} + +class MatrixEdit: public QWidget { + Q_OBJECT +public: + explicit MatrixEdit(QWidget * parent = 0); + ~MatrixEdit(); + + bool isReadOnly() const; + void setReadOnly(bool yes); + + QVector vector() const; + QVector > matrix() const; + void setVector(const QVector & v); + void setMatrix(const QVector > & v); + +private: + void setVectorMode(bool yes); + void clear(bool ident = false); + + Ui::MatrixEdit * ui; + bool ro; + +private slots: + void on_spinRows_valueChanged(int cnt); + void on_spinCols_valueChanged(int cnt); + void on_buttonNull_clicked(); + void on_buttonIdentity_clicked(); + +signals: + void changed(); + +}; + + +#endif // MATRIXEDIT_H diff --git a/test/qad/widgets/matrixedit.ui b/test/qad/widgets/matrixedit.ui new file mode 100644 index 0000000..58a136d --- /dev/null +++ b/test/qad/widgets/matrixedit.ui @@ -0,0 +1,116 @@ + + + MatrixEdit + + + + 0 + 0 + 283 + 264 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + Cols: + + + + + + + 65536 + + + + + + + Rows: + + + + + + + 65536 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Null + + + 0 + + + + + + + Identity + + + I + + + + + + + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + search(QString) + searchNext() + searchPrevious() + searchAll() + hideSearch() + + diff --git a/test/qad/widgets/plugin/CMakeLists.txt b/test/qad/widgets/plugin/CMakeLists.txt new file mode 100644 index 0000000..74db060 --- /dev/null +++ b/test/qad/widgets/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(widgets "Gui;Widgets" "") diff --git a/test/qad/widgets/plugin/chardialogplugin.cpp b/test/qad/widgets/plugin/chardialogplugin.cpp new file mode 100644 index 0000000..4ad02db --- /dev/null +++ b/test/qad/widgets/plugin/chardialogplugin.cpp @@ -0,0 +1,69 @@ +#include "chardialog.h" +#include "chardialogplugin.h" +#include + + +CharDialogPlugin::CharDialogPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void CharDialogPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool CharDialogPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * CharDialogPlugin::createWidget(QWidget * parent) { + return new CharDialog(parent); +} + + +QString CharDialogPlugin::name() const { + return QLatin1String("CharDialog"); +} + + +QString CharDialogPlugin::group() const { + return QLatin1String("Dialogs"); +} + + +QIcon CharDialogPlugin::icon() const { + return QIcon(":/icons/chardialog.png"); +} + + +QString CharDialogPlugin::toolTip() const { + return QLatin1String("Character Select Dialog"); +} + + +QString CharDialogPlugin::whatsThis() const { + return QLatin1String("Character Select Dialog"); +} + + +bool CharDialogPlugin::isContainer() const { + return false; +} + + +QString CharDialogPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString CharDialogPlugin::includeFile() const { + return QLatin1String("chardialog.h"); +} + diff --git a/test/qad/widgets/plugin/chardialogplugin.h b/test/qad/widgets/plugin/chardialogplugin.h new file mode 100644 index 0000000..9e624ee --- /dev/null +++ b/test/qad/widgets/plugin/chardialogplugin.h @@ -0,0 +1,36 @@ +#ifndef CHARDIALOGPLUGIN_H +#define CHARDIALOGPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class CharDialogPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + CharDialogPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // CHARDIALOGPLUGIN_H diff --git a/test/qad/widgets/plugin/clineeditplugin.cpp b/test/qad/widgets/plugin/clineeditplugin.cpp new file mode 100644 index 0000000..7315985 --- /dev/null +++ b/test/qad/widgets/plugin/clineeditplugin.cpp @@ -0,0 +1,69 @@ +#include "clineedit.h" +#include "clineeditplugin.h" +#include + + +CLineEditPlugin::CLineEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void CLineEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool CLineEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * CLineEditPlugin::createWidget(QWidget * parent) { + return new CLineEdit(parent); +} + + +QString CLineEditPlugin::name() const { + return QLatin1String("CLineEdit"); +} + + +QString CLineEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon CLineEditPlugin::icon() const { + return QIcon(":/icons/clineedit.png"); +} + + +QString CLineEditPlugin::toolTip() const { + return QLatin1String("Clearable Line Edit"); +} + + +QString CLineEditPlugin::whatsThis() const { + return QLatin1String("Clearable Line Edit"); +} + + +bool CLineEditPlugin::isContainer() const { + return false; +} + + +QString CLineEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString CLineEditPlugin::includeFile() const { + return QLatin1String("clineedit.h"); +} + diff --git a/test/qad/widgets/plugin/clineeditplugin.h b/test/qad/widgets/plugin/clineeditplugin.h new file mode 100644 index 0000000..148461d --- /dev/null +++ b/test/qad/widgets/plugin/clineeditplugin.h @@ -0,0 +1,36 @@ +#ifndef CLINEEDITPLUGIN_H +#define CLINEEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class CLineEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + CLineEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // CLINEEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/colorbuttonplugin.cpp b/test/qad/widgets/plugin/colorbuttonplugin.cpp new file mode 100644 index 0000000..84e8574 --- /dev/null +++ b/test/qad/widgets/plugin/colorbuttonplugin.cpp @@ -0,0 +1,69 @@ +#include "colorbutton.h" +#include "colorbuttonplugin.h" +#include + + +ColorButtonPlugin::ColorButtonPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void ColorButtonPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool ColorButtonPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * ColorButtonPlugin::createWidget(QWidget * parent) { + return new ColorButton(parent); +} + + +QString ColorButtonPlugin::name() const { + return QLatin1String("ColorButton"); +} + + +QString ColorButtonPlugin::group() const { + return QLatin1String("Buttons"); +} + + +QIcon ColorButtonPlugin::icon() const { + return QIcon(":/icons/colorbutton.png"); +} + + +QString ColorButtonPlugin::toolTip() const { + return QLatin1String("Color Button"); +} + + +QString ColorButtonPlugin::whatsThis() const { + return QLatin1String("Color Button"); +} + + +bool ColorButtonPlugin::isContainer() const { + return false; +} + + +QString ColorButtonPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString ColorButtonPlugin::includeFile() const { + return QLatin1String("colorbutton.h"); +} + diff --git a/test/qad/widgets/plugin/colorbuttonplugin.h b/test/qad/widgets/plugin/colorbuttonplugin.h new file mode 100644 index 0000000..82fb9e9 --- /dev/null +++ b/test/qad/widgets/plugin/colorbuttonplugin.h @@ -0,0 +1,36 @@ +#ifndef COLORBUTTONPLUGIN_H +#define COLORBUTTONPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class ColorButtonPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ColorButtonPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // COLORBUTTONPLUGIN_H diff --git a/test/qad/widgets/plugin/ecomboboxplugin.cpp b/test/qad/widgets/plugin/ecomboboxplugin.cpp new file mode 100644 index 0000000..c0079d4 --- /dev/null +++ b/test/qad/widgets/plugin/ecomboboxplugin.cpp @@ -0,0 +1,69 @@ +#include "ecombobox.h" +#include "ecomboboxplugin.h" +#include + + +EComboBoxPlugin::EComboBoxPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void EComboBoxPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool EComboBoxPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * EComboBoxPlugin::createWidget(QWidget * parent) { + return new EComboBox(parent); +} + + +QString EComboBoxPlugin::name() const { + return QLatin1String("EComboBox"); +} + + +QString EComboBoxPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon EComboBoxPlugin::icon() const { + return QIcon(":/icons/ecombobox.png"); +} + + +QString EComboBoxPlugin::toolTip() const { + return QLatin1String("Combo Box with Search"); +} + + +QString EComboBoxPlugin::whatsThis() const { + return QLatin1String("Combo Box with Search"); +} + + +bool EComboBoxPlugin::isContainer() const { + return false; +} + + +QString EComboBoxPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString EComboBoxPlugin::includeFile() const { + return QLatin1String("ecombobox.h"); +} + diff --git a/test/qad/widgets/plugin/ecomboboxplugin.h b/test/qad/widgets/plugin/ecomboboxplugin.h new file mode 100644 index 0000000..afccea3 --- /dev/null +++ b/test/qad/widgets/plugin/ecomboboxplugin.h @@ -0,0 +1,36 @@ +#ifndef ECOMBOBOXPLUGIN_H +#define ECOMBOBOXPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class EComboBoxPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + EComboBoxPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // ECOMBOBOXPLUGIN_H diff --git a/test/qad/widgets/plugin/evalspinboxplugin.cpp b/test/qad/widgets/plugin/evalspinboxplugin.cpp new file mode 100644 index 0000000..7356dc8 --- /dev/null +++ b/test/qad/widgets/plugin/evalspinboxplugin.cpp @@ -0,0 +1,69 @@ +#include "evalspinbox.h" +#include "evalspinboxplugin.h" +#include + + +EvalSpinBoxPlugin::EvalSpinBoxPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void EvalSpinBoxPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool EvalSpinBoxPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * EvalSpinBoxPlugin::createWidget(QWidget * parent) { + return new EvalSpinBox(parent); +} + + +QString EvalSpinBoxPlugin::name() const { + return QLatin1String("EvalSpinBox"); +} + + +QString EvalSpinBoxPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon EvalSpinBoxPlugin::icon() const { + return QIcon(":/icons/evalspinbox.png"); +} + + +QString EvalSpinBoxPlugin::toolTip() const { + return QLatin1String("Evaluation double SpinBox"); +} + + +QString EvalSpinBoxPlugin::whatsThis() const { + return QLatin1String("Evaluation double SpinBox"); +} + + +bool EvalSpinBoxPlugin::isContainer() const { + return false; +} + + +QString EvalSpinBoxPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString EvalSpinBoxPlugin::includeFile() const { + return QLatin1String("evalspinbox.h"); +} + diff --git a/test/qad/widgets/plugin/evalspinboxplugin.h b/test/qad/widgets/plugin/evalspinboxplugin.h new file mode 100644 index 0000000..d0949f0 --- /dev/null +++ b/test/qad/widgets/plugin/evalspinboxplugin.h @@ -0,0 +1,36 @@ +#ifndef EVALSPINBOXPLUGIN_H +#define EVALSPINBOXPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class EvalSpinBoxPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + EvalSpinBoxPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // EVALSPINBOXPLUGIN_H diff --git a/test/qad/widgets/plugin/iconedlabelplugin.h b/test/qad/widgets/plugin/iconedlabelplugin.h new file mode 100644 index 0000000..50ce287 --- /dev/null +++ b/test/qad/widgets/plugin/iconedlabelplugin.h @@ -0,0 +1,36 @@ +#ifndef ICONEDLABEPLUGIN_H +#define ICONEDLABEPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class IconedLabelPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + IconedLabelPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // ICONEDLABEPLUGIN_H diff --git a/test/qad/widgets/plugin/imageviewplugin.cpp b/test/qad/widgets/plugin/imageviewplugin.cpp new file mode 100644 index 0000000..f20adc9 --- /dev/null +++ b/test/qad/widgets/plugin/imageviewplugin.cpp @@ -0,0 +1,69 @@ +#include "image_view.h" +#include "imageviewplugin.h" +#include + + +ImageViewPlugin::ImageViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void ImageViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool ImageViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * ImageViewPlugin::createWidget(QWidget * parent) { + return new ImageView(parent); +} + + +QString ImageViewPlugin::name() const { + return QLatin1String("ImageView"); +} + + +QString ImageViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon ImageViewPlugin::icon() const { + return QIcon(/*":/icons/spinslider.png"*/); +} + + +QString ImageViewPlugin::toolTip() const { + return QLatin1String("Image viewer"); +} + + +QString ImageViewPlugin::whatsThis() const { + return QLatin1String("Image viewer"); +} + + +bool ImageViewPlugin::isContainer() const { + return false; +} + + +QString ImageViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString ImageViewPlugin::includeFile() const { + return QLatin1String("image_view.h"); +} + diff --git a/test/qad/widgets/plugin/imageviewplugin.h b/test/qad/widgets/plugin/imageviewplugin.h new file mode 100644 index 0000000..cc7a704 --- /dev/null +++ b/test/qad/widgets/plugin/imageviewplugin.h @@ -0,0 +1,36 @@ +#ifndef IMAGEVIEWPLUGIN_H +#define IMAGEVIEWPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class ImageViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ImageViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/widgets/plugin/lconedlabelplugin.cpp b/test/qad/widgets/plugin/lconedlabelplugin.cpp new file mode 100644 index 0000000..a24f3e2 --- /dev/null +++ b/test/qad/widgets/plugin/lconedlabelplugin.cpp @@ -0,0 +1,69 @@ +#include "iconedlabel.h" +#include "iconedlabelplugin.h" +#include + + +IconedLabelPlugin::IconedLabelPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void IconedLabelPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool IconedLabelPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * IconedLabelPlugin::createWidget(QWidget * parent) { + return new IconedLabel(parent); +} + + +QString IconedLabelPlugin::name() const { + return QLatin1String("IconedLabel"); +} + + +QString IconedLabelPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon IconedLabelPlugin::icon() const { + return QIcon(); +} + + +QString IconedLabelPlugin::toolTip() const { + return QLatin1String("Label with Icon"); +} + + +QString IconedLabelPlugin::whatsThis() const { + return QLatin1String("Label with Icon"); +} + + +bool IconedLabelPlugin::isContainer() const { + return false; +} + + +QString IconedLabelPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString IconedLabelPlugin::includeFile() const { + return QLatin1String("iconedlabel.h"); +} + diff --git a/test/qad/widgets/plugin/qad_widgets.cpp b/test/qad/widgets/plugin/qad_widgets.cpp new file mode 100644 index 0000000..257bd37 --- /dev/null +++ b/test/qad/widgets/plugin/qad_widgets.cpp @@ -0,0 +1,47 @@ +#include "qad_widgets.h" +#include "spinsliderplugin.h" +#include "colorbuttonplugin.h" +#include "chardialogplugin.h" +#include "shortcutsplugin.h" +#include "clineeditplugin.h" +#include "qipeditplugin.h" +#include "qpointeditplugin.h" +#include "qrecteditplugin.h" +#include "ecomboboxplugin.h" +#include "qpiconsoleplugin.h" +#include "iconedlabelplugin.h" +#include "qcodeeditplugin.h" +#include "qvarianteditplugin.h" +#include "qpiconfigplugin.h" +#include "evalspinboxplugin.h" +#include "imageviewplugin.h" + + +QADWidgets::QADWidgets(QObject * parent): QObject(parent) { + m_widgets.append(new SpinSliderPlugin(this)); + m_widgets.append(new ColorButtonPlugin(this)); + m_widgets.append(new CharDialogPlugin(this)); + m_widgets.append(new ShortcutsPlugin(this)); + m_widgets.append(new CLineEditPlugin(this)); + m_widgets.append(new QIPEditPlugin(this)); + m_widgets.append(new QPointEditPlugin(this)); + m_widgets.append(new QRectEditPlugin(this)); + m_widgets.append(new EComboBoxPlugin(this)); + m_widgets.append(new QPIConsolePlugin(this)); + m_widgets.append(new IconedLabelPlugin(this)); + m_widgets.append(new QCodeEditPlugin(this)); + m_widgets.append(new QVariantEditPlugin(this)); + m_widgets.append(new QPIConfigPlugin(this)); + m_widgets.append(new EvalSpinBoxPlugin(this)); + m_widgets.append(new ImageViewPlugin(this)); +} + + +QList QADWidgets::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_widgets_plugin, QADWidgets) +#endif diff --git a/test/qad/widgets/plugin/qad_widgets.h b/test/qad/widgets/plugin/qad_widgets.h new file mode 100644 index 0000000..e18e8f9 --- /dev/null +++ b/test/qad/widgets/plugin/qad_widgets.h @@ -0,0 +1,23 @@ +#ifndef QAD_WIDGETS_H +#define QAD_WIDGETS_H + +#include +#include + +class QADWidgets: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.widgets") +#endif +public: + explicit QADWidgets(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QAD_WIDGETS_H diff --git a/test/qad/widgets/plugin/qcodeeditplugin.cpp b/test/qad/widgets/plugin/qcodeeditplugin.cpp new file mode 100644 index 0000000..1bd7aaa --- /dev/null +++ b/test/qad/widgets/plugin/qcodeeditplugin.cpp @@ -0,0 +1,69 @@ +#include "qcodeedit.h" +#include "qcodeeditplugin.h" +#include + + +QCodeEditPlugin::QCodeEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QCodeEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QCodeEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QCodeEditPlugin::createWidget(QWidget * parent) { + return new QCodeEdit(parent); +} + + +QString QCodeEditPlugin::name() const { + return QLatin1String("QCodeEdit"); +} + + +QString QCodeEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon QCodeEditPlugin::icon() const { + return QIcon(":/icons/qcodeedit.png"); +} + + +QString QCodeEditPlugin::toolTip() const { + return QLatin1String("QCodeEdit"); +} + + +QString QCodeEditPlugin::whatsThis() const { + return QLatin1String("QCodeEdit"); +} + + +bool QCodeEditPlugin::isContainer() const { + return false; +} + + +QString QCodeEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QCodeEditPlugin::includeFile() const { + return QLatin1String("qcodeedit.h"); +} + diff --git a/test/qad/widgets/plugin/qcodeeditplugin.h b/test/qad/widgets/plugin/qcodeeditplugin.h new file mode 100644 index 0000000..7f733c9 --- /dev/null +++ b/test/qad/widgets/plugin/qcodeeditplugin.h @@ -0,0 +1,36 @@ +#ifndef QCODEEDITPLUGIN_H +#define QCODEEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QCodeEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QCodeEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QCODEEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/qipeditplugin.cpp b/test/qad/widgets/plugin/qipeditplugin.cpp new file mode 100644 index 0000000..7cc94dd --- /dev/null +++ b/test/qad/widgets/plugin/qipeditplugin.cpp @@ -0,0 +1,69 @@ +#include "qipedit.h" +#include "qipeditplugin.h" +#include + + +QIPEditPlugin::QIPEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QIPEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QIPEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QIPEditPlugin::createWidget(QWidget * parent) { + return new QIPEdit(parent); +} + + +QString QIPEditPlugin::name() const { + return QLatin1String("QIPEdit"); +} + + +QString QIPEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon QIPEditPlugin::icon() const { + return QIcon(); +} + + +QString QIPEditPlugin::toolTip() const { + return QLatin1String("IP Edit"); +} + + +QString QIPEditPlugin::whatsThis() const { + return QLatin1String("IP Edit"); +} + + +bool QIPEditPlugin::isContainer() const { + return false; +} + + +QString QIPEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QIPEditPlugin::includeFile() const { + return QLatin1String("qipedit.h"); +} + diff --git a/test/qad/widgets/plugin/qipeditplugin.h b/test/qad/widgets/plugin/qipeditplugin.h new file mode 100644 index 0000000..7ed24b1 --- /dev/null +++ b/test/qad/widgets/plugin/qipeditplugin.h @@ -0,0 +1,36 @@ +#ifndef QIPEDITPLUGIN_H +#define QIPEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QIPEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QIPEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QIPEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/qpiconfigplugin.cpp b/test/qad/widgets/plugin/qpiconfigplugin.cpp new file mode 100644 index 0000000..8fee953 --- /dev/null +++ b/test/qad/widgets/plugin/qpiconfigplugin.cpp @@ -0,0 +1,69 @@ +#include "qpiconfigwidget.h" +#include "qpiconfigplugin.h" +#include + + +QPIConfigPlugin::QPIConfigPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QPIConfigPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QPIConfigPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QPIConfigPlugin::createWidget(QWidget * parent) { + return new QPIConfigWidget(parent, 0, false); +} + + +QString QPIConfigPlugin::name() const { + return QLatin1String("QPIConfigWidget"); +} + + +QString QPIConfigPlugin::group() const { + return QLatin1String("Editor Widgets"); +} + + +QIcon QPIConfigPlugin::icon() const { + return QIcon(); +} + + +QString QPIConfigPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString QPIConfigPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool QPIConfigPlugin::isContainer() const { + return false; +} + + +QString QPIConfigPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QPIConfigPlugin::includeFile() const { + return QLatin1String("qpiconfigwidget.h"); +} + diff --git a/test/qad/widgets/plugin/qpiconfigplugin.h b/test/qad/widgets/plugin/qpiconfigplugin.h new file mode 100644 index 0000000..77a8b9f --- /dev/null +++ b/test/qad/widgets/plugin/qpiconfigplugin.h @@ -0,0 +1,36 @@ +#ifndef QPICONFIGPLUGIN_H +#define QPICONFIGPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QPIConfigPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QPIConfigPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QPICONFIGPLUGIN_H diff --git a/test/qad/widgets/plugin/qpiconsoleplugin.cpp b/test/qad/widgets/plugin/qpiconsoleplugin.cpp new file mode 100644 index 0000000..ad1f615 --- /dev/null +++ b/test/qad/widgets/plugin/qpiconsoleplugin.cpp @@ -0,0 +1,69 @@ +#include "qpiconsole.h" +#include "qpiconsoleplugin.h" +#include + + +QPIConsolePlugin::QPIConsolePlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QPIConsolePlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QPIConsolePlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QPIConsolePlugin::createWidget(QWidget * parent) { + return new QPIConsole(parent); +} + + +QString QPIConsolePlugin::name() const { + return QLatin1String("QPIConsole"); +} + + +QString QPIConsolePlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon QPIConsolePlugin::icon() const { + return QIcon(":/icons/qpiconsole.png"); +} + + +QString QPIConsolePlugin::toolTip() const { + return QLatin1String("QPIConsole"); +} + + +QString QPIConsolePlugin::whatsThis() const { + return QLatin1String("QPIConsole"); +} + + +bool QPIConsolePlugin::isContainer() const { + return false; +} + + +QString QPIConsolePlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QPIConsolePlugin::includeFile() const { + return QLatin1String("qpiconsole.h"); +} + diff --git a/test/qad/widgets/plugin/qpiconsoleplugin.h b/test/qad/widgets/plugin/qpiconsoleplugin.h new file mode 100644 index 0000000..d8a50d7 --- /dev/null +++ b/test/qad/widgets/plugin/qpiconsoleplugin.h @@ -0,0 +1,36 @@ +#ifndef QPICONSOLEPLUGIN_H +#define QPICONSOLEPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QPIConsolePlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QPIConsolePlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QPICONSOLEPLUGIN_H diff --git a/test/qad/widgets/plugin/qpointeditplugin.cpp b/test/qad/widgets/plugin/qpointeditplugin.cpp new file mode 100644 index 0000000..622c78e --- /dev/null +++ b/test/qad/widgets/plugin/qpointeditplugin.cpp @@ -0,0 +1,69 @@ +#include "qpointedit.h" +#include "qpointeditplugin.h" +#include + + +QPointEditPlugin::QPointEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QPointEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QPointEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QPointEditPlugin::createWidget(QWidget * parent) { + return new QPointEdit(parent); +} + + +QString QPointEditPlugin::name() const { + return QLatin1String("QPointEdit"); +} + + +QString QPointEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon QPointEditPlugin::icon() const { + return QIcon(":/icons/qpointedit.png"); +} + + +QString QPointEditPlugin::toolTip() const { + return QLatin1String("Point Edit"); +} + + +QString QPointEditPlugin::whatsThis() const { + return QLatin1String("Point Edit"); +} + + +bool QPointEditPlugin::isContainer() const { + return false; +} + + +QString QPointEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QPointEditPlugin::includeFile() const { + return QLatin1String("qpointedit.h"); +} + diff --git a/test/qad/widgets/plugin/qpointeditplugin.h b/test/qad/widgets/plugin/qpointeditplugin.h new file mode 100644 index 0000000..b69811d --- /dev/null +++ b/test/qad/widgets/plugin/qpointeditplugin.h @@ -0,0 +1,36 @@ +#ifndef QPOINTEDITPLUGIN_H +#define QPOINTEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QPointEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QPointEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QPOINTEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/qrecteditplugin.cpp b/test/qad/widgets/plugin/qrecteditplugin.cpp new file mode 100644 index 0000000..f64f30c --- /dev/null +++ b/test/qad/widgets/plugin/qrecteditplugin.cpp @@ -0,0 +1,69 @@ +#include "qrectedit.h" +#include "qrecteditplugin.h" +#include + + +QRectEditPlugin::QRectEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QRectEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QRectEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QRectEditPlugin::createWidget(QWidget * parent) { + return new QRectEdit(parent); +} + + +QString QRectEditPlugin::name() const { + return QLatin1String("QRectEdit"); +} + + +QString QRectEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon QRectEditPlugin::icon() const { + return QIcon(":/icons/qrectedit.png"); +} + + +QString QRectEditPlugin::toolTip() const { + return QLatin1String("Rect Edit"); +} + + +QString QRectEditPlugin::whatsThis() const { + return QLatin1String("Rect Edit"); +} + + +bool QRectEditPlugin::isContainer() const { + return false; +} + + +QString QRectEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QRectEditPlugin::includeFile() const { + return QLatin1String("qrectedit.h"); +} + diff --git a/test/qad/widgets/plugin/qrecteditplugin.h b/test/qad/widgets/plugin/qrecteditplugin.h new file mode 100644 index 0000000..419fbd3 --- /dev/null +++ b/test/qad/widgets/plugin/qrecteditplugin.h @@ -0,0 +1,36 @@ +#ifndef QRECTEDITPLUGIN_H +#define QRECTEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QRectEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QRectEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QRECTEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/qvarianteditplugin.cpp b/test/qad/widgets/plugin/qvarianteditplugin.cpp new file mode 100644 index 0000000..733eb88 --- /dev/null +++ b/test/qad/widgets/plugin/qvarianteditplugin.cpp @@ -0,0 +1,69 @@ +#include "qvariantedit.h" +#include "qvarianteditplugin.h" +#include + + +QVariantEditPlugin::QVariantEditPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QVariantEditPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QVariantEditPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QVariantEditPlugin::createWidget(QWidget * parent) { + return new QVariantEdit(parent); +} + + +QString QVariantEditPlugin::name() const { + return QLatin1String("QVariantEdit"); +} + + +QString QVariantEditPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon QVariantEditPlugin::icon() const { + return QIcon(":/icons/qvariantedit.png"); +} + + +QString QVariantEditPlugin::toolTip() const { + return QLatin1String("QVariant Edit"); +} + + +QString QVariantEditPlugin::whatsThis() const { + return QLatin1String("QVariant Edit"); +} + + +bool QVariantEditPlugin::isContainer() const { + return false; +} + + +QString QVariantEditPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QVariantEditPlugin::includeFile() const { + return QLatin1String("qvariantedit.h"); +} + diff --git a/test/qad/widgets/plugin/qvarianteditplugin.h b/test/qad/widgets/plugin/qvarianteditplugin.h new file mode 100644 index 0000000..79eb699 --- /dev/null +++ b/test/qad/widgets/plugin/qvarianteditplugin.h @@ -0,0 +1,36 @@ +#ifndef QVARIANTEDITPLUGIN_H +#define QVARIANTEDITPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class QVariantEditPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + QVariantEditPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif // QVARIANTEDITPLUGIN_H diff --git a/test/qad/widgets/plugin/shortcutsplugin.cpp b/test/qad/widgets/plugin/shortcutsplugin.cpp new file mode 100644 index 0000000..774f4e3 --- /dev/null +++ b/test/qad/widgets/plugin/shortcutsplugin.cpp @@ -0,0 +1,69 @@ +#include "shortcuts.h" +#include "shortcutsplugin.h" +#include + + +ShortcutsPlugin::ShortcutsPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void ShortcutsPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool ShortcutsPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * ShortcutsPlugin::createWidget(QWidget * parent) { + return new Shortcuts(parent, false); +} + + +QString ShortcutsPlugin::name() const { + return QLatin1String("Shortcuts"); +} + + +QString ShortcutsPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon ShortcutsPlugin::icon() const { + return QIcon(); +} + + +QString ShortcutsPlugin::toolTip() const { + return QLatin1String("Shortcuts Edit"); +} + + +QString ShortcutsPlugin::whatsThis() const { + return QLatin1String("Shortcuts Edit"); +} + + +bool ShortcutsPlugin::isContainer() const { + return false; +} + + +QString ShortcutsPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString ShortcutsPlugin::includeFile() const { + return QLatin1String("shortcuts.h"); +} + diff --git a/test/qad/widgets/plugin/shortcutsplugin.h b/test/qad/widgets/plugin/shortcutsplugin.h new file mode 100644 index 0000000..7fcc0d0 --- /dev/null +++ b/test/qad/widgets/plugin/shortcutsplugin.h @@ -0,0 +1,36 @@ +#ifndef SHORTCUTSPLUGIN_H +#define SHORTCUTSPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class ShortcutsPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + ShortcutsPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/widgets/plugin/spinsliderplugin.cpp b/test/qad/widgets/plugin/spinsliderplugin.cpp new file mode 100644 index 0000000..683312d --- /dev/null +++ b/test/qad/widgets/plugin/spinsliderplugin.cpp @@ -0,0 +1,69 @@ +#include "spinslider.h" +#include "spinsliderplugin.h" +#include + + +SpinSliderPlugin::SpinSliderPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void SpinSliderPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool SpinSliderPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * SpinSliderPlugin::createWidget(QWidget * parent) { + return new SpinSlider(parent); +} + + +QString SpinSliderPlugin::name() const { + return QLatin1String("SpinSlider"); +} + + +QString SpinSliderPlugin::group() const { + return QLatin1String("Input Widgets"); +} + + +QIcon SpinSliderPlugin::icon() const { + return QIcon(":/icons/spinslider.png"); +} + + +QString SpinSliderPlugin::toolTip() const { + return QLatin1String("Spin with Slider"); +} + + +QString SpinSliderPlugin::whatsThis() const { + return QLatin1String("Spin with Slider"); +} + + +bool SpinSliderPlugin::isContainer() const { + return false; +} + + +QString SpinSliderPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString SpinSliderPlugin::includeFile() const { + return QLatin1String("spinslider.h"); +} + diff --git a/test/qad/widgets/plugin/spinsliderplugin.h b/test/qad/widgets/plugin/spinsliderplugin.h new file mode 100644 index 0000000..858e383 --- /dev/null +++ b/test/qad/widgets/plugin/spinsliderplugin.h @@ -0,0 +1,36 @@ +#ifndef SPINSLIDERPLUGIN_H +#define SPINSLIDERPLUGIN_H + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class SpinSliderPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + SpinSliderPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif diff --git a/test/qad/widgets/propertystorage_editor.cpp b/test/qad/widgets/propertystorage_editor.cpp new file mode 100644 index 0000000..44b31c2 --- /dev/null +++ b/test/qad/widgets/propertystorage_editor.cpp @@ -0,0 +1,72 @@ +#include "propertystorage_editor.h" +#include "qvariantedit.h" +#include +#include + + +PropertyStorageEditor::PropertyStorageEditor(QWidget * parent): QWidget(parent) { + setLayout(new QGridLayout()); + layout()->setContentsMargins(0, 0, 0, 0); + storage = 0; +} + + +PropertyStorageEditor::~PropertyStorageEditor() { + clear(); +} + + +void PropertyStorageEditor::clear() { + qDeleteAll(_widgets); + _widgets.clear(); +} + + +bool PropertyStorageEditor::isEmpty() const { + return ((QGridLayout*)layout())->count() == 0; +} + + +void PropertyStorageEditor::setStorage(PropertyStorage * s) { + clear(); + storage = s; + if (!storage) return; + int r = 0; + QGridLayout * layoutProps = (QGridLayout*)layout(); + foreach (const PropertyStorage::Property & p, *storage) { + QLabel * lbl = new QLabel(p.name); + _widgets << lbl; + lbl->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + layoutProps->addWidget(lbl, r, 0); + QVariantEdit * ve = new QVariantEdit(); + ve->setValue(p.value); + ve->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + ve->setMinimumWidth(50); + connect(ve, SIGNAL(valueChanged(QVariant)), this, SIGNAL(changed())); + layoutProps->addWidget(ve, r, 1); _widgets << ve; + lbl = new QLabel(p.comment); lbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + layoutProps->addWidget(lbl, r, 2); _widgets << lbl; + ++r; + } +} + + +void PropertyStorageEditor::applyProperties() { + if (!storage) return; + QList & props(storage->properties()); + QGridLayout * layoutProps = (QGridLayout*)layout(); + for (int r = 0; r < layoutProps->rowCount(); ++r) { + if (layoutProps->itemAtPosition(r, 0) == 0 || layoutProps->itemAtPosition(r, 1) == 0) continue; + QLabel * lbl = qobject_cast(layoutProps->itemAtPosition(r, 0)->widget()); + QVariantEdit * ve = qobject_cast(layoutProps->itemAtPosition(r, 1)->widget()); + if (lbl == 0 || ve == 0) continue; + QString pn = lbl->text(); + for (int i = 0; i < props.size(); ++i) { + PropertyStorage::Property & p(props[i]); + if (p.name == pn) { + p.value = ve->value(); + break; + } + } + } +} diff --git a/test/qad/widgets/propertystorage_editor.h b/test/qad/widgets/propertystorage_editor.h new file mode 100644 index 0000000..05fa4cf --- /dev/null +++ b/test/qad/widgets/propertystorage_editor.h @@ -0,0 +1,29 @@ +#ifndef PROPERTYSTORAGEEDITOR_H +#define PROPERTYSTORAGEEDITOR_H + +#include +#include "propertystorage.h" + + +class PropertyStorageEditor: public QWidget { + Q_OBJECT +public: + explicit PropertyStorageEditor(QWidget * parent = 0); + ~PropertyStorageEditor(); + + void clear(); + bool isEmpty() const; + void setStorage(PropertyStorage * s); + void applyProperties(); + +private: + QList _widgets; + PropertyStorage * storage; + +signals: + void resetStorageRequest(PropertyStorage * ); + void changed(); + +}; + +#endif // PROPERTYSTORAGEEDITOR_H diff --git a/test/qad/widgets/qad_widgets.qrc b/test/qad/widgets/qad_widgets.qrc new file mode 100644 index 0000000..bc585b9 --- /dev/null +++ b/test/qad/widgets/qad_widgets.qrc @@ -0,0 +1,46 @@ + + + ../icons/go-next.png + ../icons/go-previous.png + lang/qad_widgets_ru.ts + ../icons/dialog-close.png + ../icons/edit-clear.png + ../icons/edit-guides.png + ../icons/view-grid.png + ../icons/zoom-fit-best.png + ../icons/configure.png + ../icons/alpha.png + ../icons/document-save.png + ../icons/edit-clear-locationbar-rtl.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/item-add.png + ../icons/item.png + ../icons/node-add.png + ../icons/node.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/expand_s_x.png + ../icons/expand_s_y.png + ../icons/expand_x.png + ../icons/expand_y.png + ../icons/border-line.png + ../icons/legend.png + ../icons/chardialog.png + ../icons/clineedit.png + ../icons/colorbutton.png + ../icons/ecombobox.png + ../icons/qpiconsole.png + ../icons/spinslider.png + ../icons/etabwidget.png + ../icons/qcodeedit.png + ../icons/qvariantedit.png + ../icons/code-word.png + ../icons/f1.png + ../icons/dialog-ok-apply.png + ../icons/dialog-warning.png + ../icons/tools-wizard.png + ../icons/evalspinbox.png + + diff --git a/test/qad/widgets/qcodeedit.cpp b/test/qad/widgets/qcodeedit.cpp new file mode 100644 index 0000000..a88079e --- /dev/null +++ b/test/qad/widgets/qcodeedit.cpp @@ -0,0 +1,1443 @@ +#include "qcodeedit.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecombobox.h" +#include "qad_types.h" +#include "ui_qcodeedit.h" + +Q_DECLARE_METATYPE(QTextCursor) + +QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) { + qRegisterMetaType(); + ui = new Ui::QCodeEdit(); + ui->setupUi(this); + ui->widgetSearch->hide(); + ui->comboSearch->installEventFilter(this); + ui->comboReplace->installEventFilter(this); + prev_lc = auto_comp_pl = cur_search_ind = pos_press = pos_el_press = -1; + timer = 0; + _ignore_focus_out = _destructor = _replacing = false; + _first = true; + es_line.format.setBackground(QColor(240, 245, 240)); + es_line.format.setProperty(QTextFormat::FullWidthSelection, true); + es_cursor.format.setBackground(QColor(220, 255, 200)); + es_bracket.format.setBackground(QColor(180, 238, 180)); + es_bracket.format.setForeground(Qt::red); + es_search.format.setBackground(QColor(255, 240, 10)); + es_range.format.setBackground(QColor(230, 246, 255)); + es_range.format.setProperty(QTextFormat::FullWidthSelection, true); + widget_help = new QFrame(); + widget_help->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + widget_help->setFocusPolicy(Qt::NoFocus); + widget_help->setFrameShadow(QFrame::Sunken); + widget_help->setFrameShape(QFrame::StyledPanel); + widget_help->setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); + widget_help->layout()->setContentsMargins(0, 0, 0, 0); + for (int i = 0; i < 2; ++i) { + lbl_help[i] = new IconedLabel(); + lbl_help[i]->setFrameShadow(QFrame::Plain); + lbl_help[i]->setFrameShape(QFrame::NoFrame); + lbl_help[i]->setDirection(IconedLabel::RightToLeft); + widget_help->layout()->addWidget(lbl_help[i]); + } + lbl_help[1]->setIcon(QIcon(":/icons/f1.png")); + lbl_help[1]->setText(trUtf8("Press F1 for details")); + completer = new QTreeWidget(); + completer->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + completer->setFocusPolicy(Qt::NoFocus); + completer->setColumnCount(2); + completer->setRootIsDecorated(false); + completer->setHeaderHidden(true); + completer->header()->setDefaultAlignment(Qt::AlignCenter); + completer->header()-> +#if (QT_VERSION >= 0x050000) + setSectionResizeMode +#else + setResizeMode +#endif + (QHeaderView::ResizeToContents); + completer->header()->setStretchLastSection(true); + + ui->textCode->setCursorWidth(qMax(qRound(fontHeight() / 10.), 1)); + ui->textLines->viewport()->setAutoFillBackground(false); + ui->textLines->viewport()->setCursor(Qt::ArrowCursor); + ui->textLines->setFixedWidth(ui->textLines->fontMetrics().width(" ")); + + QAction * a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Shift+Tab")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(deindent())); + a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Ctrl+D")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(deleteLine())); + a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Ctrl+Return")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(newLine())); + a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Ctrl+Up")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(scrollUp())); + a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Ctrl+Down")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(scrollDown())); + a = new QAction(this); ui->textCode->addAction(a); + a->setShortcut(QKeySequence("Ctrl+Shift+Return")); + a->setShortcutContext(Qt::WidgetShortcut); + connect(a, SIGNAL(triggered()), this, SLOT(newLineBefore())); + ui->frame->setFocusProxy(ui->textCode); + QTextOption to = ui->textLines->document()->defaultTextOption(); + to.setAlignment(Qt::AlignTop | Qt::AlignRight); + ui->textLines->document()->setDefaultTextOption(to); + /*to = ui->textCode->document()->defaultTextOption(); + to.setFlags(QTextOption::SuppressColors); + ui->textCode->document()->setDefaultTextOption(to);*/ + setShowSpaces(true); + + a = new QAction(this); + a->setShortcut(QKeySequence("Ctrl+F")); + a->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(a, SIGNAL(triggered(bool)), this, SLOT(search_triggered())); + addAction(a); + + /*a = new QAction(this); + a->setShortcut(QKeySequence("Esc")); + a->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(a, SIGNAL(triggered(bool)), this, SLOT(hideSearch())); + addAction(a);*/ + + connect(completer, SIGNAL(itemDoubleClicked(QTreeWidgetItem * ,int)), this, SLOT(commitCompletition())); + connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), ui->textLines->verticalScrollBar(), SLOT(setValue(int))); + connect(ui->textCode->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideHelp())); + connect(ui->textCode, SIGNAL(textChanged()), this, SLOT(textEdit_textChanged())); + connect(ui->textCode, SIGNAL(textChanged()), this, SIGNAL(textChanged())); + connect(ui->textCode, SIGNAL(cursorPositionChanged()), this, SLOT(textEdit_cursorPositionChanged())); + connect(ui->textCode, SIGNAL(selectionChanged()), this, SLOT(textEdit_selectionChanged())); + connect(ui->comboSearch->lineEdit(), SIGNAL(returnPressed()), this, SLOT(searchNext())); + connect(ui->comboReplace->lineEdit(), SIGNAL(returnPressed()), this, SLOT(on_buttonReplaceSearch_clicked())); + updateLines(); + + registerAutoCompletitionClass(-1, QCodeEdit::Keyword, "Words", QIcon(":/icons/code-word.png")); +} + + +QCodeEdit::~QCodeEdit() { + _destructor = true; + delete completer; + //for (int i = 0; i < 2; ++i) + // delete lbl_help[i]; + delete widget_help; + //delete ui; +} + + +QTextCursor QCodeEdit::textCursor() const { + return ui->textCode->textCursor(); +} + + +QTextDocument * QCodeEdit::document() const { + return ui->textCode->document(); +} + + +void QCodeEdit::setDocument(QTextDocument * doc) { + if (document()) { + document()->setProperty("_cursor", QVariant::fromValue(textCursor())); + document()->setProperty("_vpos", textEdit()->verticalScrollBar()->value()); + } + ui->textCode->setEnabled(doc); + ui->textLines->setEnabled(doc); + documentUnset(); + if (!doc) { + ui->textCode->setDocument(0); + documentChanged(0); + return; + } + if (!qobject_cast(doc->documentLayout())) + doc->setDocumentLayout(new QPlainTextDocumentLayout(doc)); + ui->textCode->setDocument(doc); + ui->textCode->setCursorWidth(qMax(qRound(fontHeight() / 10.), 1)); + setShowSpaces(spaces_); + if (doc->property("_cursor").isValid()) { + setTextCursor(doc->property("_cursor").value()); + textEdit()->verticalScrollBar()->setValue(doc->property("_vpos").toInt()); + } + documentChanged(doc); + doc->setDefaultFont(editorFont()); + updateLines(); +} + + +void QCodeEdit::setTextCursor(const QTextCursor & c) { + ui->textCode->setTextCursor(c); +} + + +void QCodeEdit::centerCursor() { + ui->textCode->centerCursor(); + updateLines(); +} + + +void QCodeEdit::insertText(const QString & text) { + ui->textCode->insertPlainText(text); + updateLines(); +} + + +void QCodeEdit::appendText(const QString & text) { + ui->textCode->appendPlainText(text); + updateLines(); +} + + +void QCodeEdit::setCustomExtraSelection(const QList & es) { + es_custom = es; + applyExtraSelection(); +} + + +QRect QCodeEdit::cursorRect() const { + return ui->textCode->cursorRect(); +} + + +QRect QCodeEdit::cursorRect(const QTextCursor & cursor) const { + return ui->textCode->cursorRect(cursor); +} + + +QString QCodeEdit::text() const { + return ui->textCode->toPlainText(); +} + + +QStringList QCodeEdit::cursorScope() const { + return cursor_scope; +} + + +bool QCodeEdit::showLineNumbers() const { + return ui->textLines->isVisible(); +} + + +void QCodeEdit::setEditorFont(QFont f) { + ui->textCode->setFont(f); + ui->textLines->setFont(f); +} + + +QFont QCodeEdit::editorFont() const { + return ui->textCode->font(); +} + + +QPlainTextEdit * QCodeEdit::textEdit() const { + return ui->textCode; +} + + +int QCodeEdit::skipRange(const QString & s, int pos, QChar oc, QChar cc, QChar sc) { + int cnt = 0; + bool skip = false; + for (int i = pos - 1; i >= 0; --i) { + QChar c = s[i]; + if (skip) {skip = false; continue;} + if (c == sc) {skip = true; continue;} + if (c == cc) {cnt++; continue;} + if (c == oc) {cnt--; if (cnt == 0) return i;} + } + return -1; +} + + +int QCodeEdit::skipCWord(const QString & s, int pos) { + QChar pc(0), c(0); + for (int i = pos - 1; i >= 0; --i) { + pc = c; + c = s[i]; + if (c.isLetterOrNumber() || (c.toLatin1() == '_')) continue; + if (pc.isLetter() || (pc.toLatin1() == '_')) return i + 1; + return -1; + } + return -1; +} + + +bool QCodeEdit::matchWritten(QString s, QString w) { + if (s.isEmpty() || w.isEmpty()) return true; + if (s.contains(w, Qt::CaseInsensitive)) return true; + int sp(0); + for (int i = 0; i < w.size(); ++i, ++sp) { + if (sp >= s.size()) return false; + QChar wc(w[i].toLower()); + bool ns = false, bl = true; + while (sp < s.size()) { + if (ns || s[sp].toLatin1() == '_') { + if (s[sp].toLatin1() == '_') {sp++; bl = false; continue;} + if (s[sp].isLower() && bl) {sp++; continue;} + if (s[sp].toLower() != wc) return false; + } + if (s[sp].toLower() == wc) break; + ns = true; + sp++; + } + if (sp >= s.size()) return false; + } + return true; +} + + +QChar QCodeEdit::pairChar(QChar c) { + switch (c.toLatin1()) { + case '\"': return '\"'; + case '(': return ')'; + case ')': return '('; + case '[': return ']'; + case ']': return '['; + default: break; + } + return QChar(); +} + + +bool QCodeEdit::eventFilter(QObject * o, QEvent * e) { + if (_destructor) return QWidget::eventFilter(o, e); + if (e->type() == QEvent::Destroy) { + completer->removeEventFilter(this); + ui->textCode->removeEventFilter(this); + ui->textCode->viewport()->removeEventFilter(this); + ui->textLines->viewport()->removeEventFilter(this); + return QWidget::eventFilter(o, e); + } + if (ui->textLines) { + if (o == ui->textLines->viewport()) {/* + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease || + e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonDblClick) { +#if (QT_VERSION < 0x050000) + const_cast(((QMouseEvent*)e)->pos()) = QPoint(0, ((QMouseEvent*)e)->pos().y()); +#else + const_cast(((QMouseEvent*)e)->localPos()) = QPointF(0, ((QMouseEvent*)e)->localPos().y()); +#endif + QApplication::sendEvent(ui->textCode->viewport(), e); + return true; + }*/ + QTextCursor tc; + int tcpos = 0; + switch (e->type()) { + case QEvent::MouseButtonPress: + if (!isEnabled()) break; + tc = ui->textCode->cursorForPosition(((QMouseEvent*)e)->pos()); + tc.movePosition(QTextCursor::EndOfLine); + pos_el_press = tc.anchor(); + tc.movePosition(QTextCursor::StartOfLine); + pos_press = tc.anchor(); + if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) + tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + ui->textCode->setTextCursor(tc); + return true; + case QEvent::MouseMove: + if (!isEnabled()) break; + tc = ui->textCode->cursorForPosition(((QMouseEvent*)e)->pos()); + tc.movePosition(QTextCursor::StartOfLine); + if (pos_press == tc.anchor()) { + if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) + tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + ui->textCode->setTextCursor(tc); + return true; + } + if (pos_press < tc.anchor()) { + if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) + tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + } + tcpos = tc.position(); + tc.setPosition(pos_press < tc.anchor() ? pos_press : pos_el_press); + tc.setPosition(tcpos, QTextCursor::KeepAnchor); + ui->textCode->setTextCursor(tc); + return true; + case QEvent::Wheel: + if (!isEnabled()) break; + QApplication::sendEvent(ui->textCode->viewport(), e); + return true; + default: break; + } + } + } + if (o == completer) { + //qDebug() << o << e; + if (e->type() == QEvent::WindowActivate) + _ignore_focus_out = true; + //qDebug() << e; + return QWidget::eventFilter(o, e); + } + if (o == ui->comboSearch || o == ui->comboReplace) { + //qDebug() << o << e; + if (e->type() == QEvent::KeyPress) { + if (((QKeyEvent * )e)->key() == Qt::Key_Escape) { + hideHelp(); + if (completer->isVisible()) + completer->hide(); + else + hideSearch(); + } + } + //qDebug() << e; + return QWidget::eventFilter(o, e); + } + if (ui->textCode) { + if (o == ui->textCode->viewport()) { + if (e->type() == QEvent::MouseButtonPress) { + completer->hide(); + hideHelp(); + } + return QWidget::eventFilter(o, e); + } + if (o == ui->textCode) { + //qDebug() << e; + QMetaObject::invokeMethod(this, "syncScrolls", Qt::QueuedConnection); + QKeyEvent * ke; + QChar kc(0); + switch (e->type()) { + case QEvent::ToolTip: { + QTextCursor tc = ui->textCode->cursorForPosition(((QHelpEvent*)e)->pos()); + tc.select(QTextCursor::WordUnderCursor); + raiseHelp(tc); + } + break; + case QEvent::KeyPress: + ke = (QKeyEvent * )e; + //qDebug() << "key" << ke; + switch (ke->key()) { + case Qt::Key_Space: + if (ke->modifiers().testFlag(Qt::ControlModifier)) { + invokeAutoCompletition(true); + return true; + } + break; + case Qt::Key_Escape: + hideHelp(); + if (completer->isVisible()) + completer->hide(); + else + hideSearch(); + break; + case Qt::Key_Up: + if (completer->isVisible()) { + previousCompletition(); + return true; + } + completer->hide(); + hideHelp(); + if (ke->modifiers().testFlag(Qt::AltModifier)) { + copyLineUp(); + return true; + } + if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) { + moveLineUp(); + return true; + } + break; + case Qt::Key_Down: + if (completer->isVisible()) { + nextCompletition(); + return true; + } + completer->hide(); + hideHelp(); + if (ke->modifiers().testFlag(Qt::AltModifier)) { + copyLineDown(); + return true; + } + if (ke->modifiers().testFlag(Qt::ControlModifier) && ke->modifiers().testFlag(Qt::ShiftModifier)) { + moveLineDown(); + return true; + } + break; + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + if (completer->isVisible()) { + qApp->sendEvent(completer, new QKeyEvent(e->type(), ke->key(), ke->modifiers())); + return true; + } + break; + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Backspace: + case Qt::Key_Delete: + if (completer->isVisible()) + QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); + break; + case Qt::Key_Return: + if (completer->isVisible()) { + commitCompletition(); + completer->hide(); + return true; + } + if (ui->textCode->textCursor().selectedText().isEmpty()) + QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection); + break; + case Qt::Key_Tab: + if (!ui->textCode->textCursor().selectedText().isEmpty()) { + if (ke->modifiers().testFlag(Qt::ShiftModifier)) + deindent(); + else + indent(); + return true; + } + break; + case Qt::Key_D: + if (ke->modifiers().testFlag(Qt::ControlModifier)) { + completer->hide(); + return true; + } + break; + default: break; + } + if (!ke->text().isEmpty()) + kc = ke->text()[0]; + if (kc == '.') { + completer->hide(); + QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); + } else { + if ((kc.isLetterOrNumber() || kc.toLatin1() == '_') && completer->isVisible()) + QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false)); + } + break; + case QEvent::FocusOut: + if (_ignore_focus_out) { + _ignore_focus_out = false; + break; + } + case QEvent::Hide: + case QEvent::HideToParent: + case QEvent::MouseButtonPress: + //qDebug() << e; + completer->hide(); + hideHelp(); + default: break; + } + } + } + return QWidget::eventFilter(o, e); +} + + +void QCodeEdit::showEvent(QShowEvent * ) { + if (!_first) return; + _first = false; + completer->installEventFilter(this); + ui->textCode->installEventFilter(this); + ui->textCode->viewport()->installEventFilter(this); + ui->textLines->viewport()->installEventFilter(this); +} + + +void QCodeEdit::timerEvent(QTimerEvent * ) { + parse(); + emit parseRequest(); + killTimer(timer); + timer = 0; +} + + +void QCodeEdit::leaveEvent(QEvent * e) { + hideHelp(); + QWidget::leaveEvent(e); +} + + +char antiBracket(char c) { + switch (c) { + case '(': return ')'; + case '[': return ']'; + case '{': return '}'; + case '<': return '>'; + case ')': return '('; + case ']': return '['; + case '}': return '{'; + case '>': return '<'; + } + return 0; +} + + +void QCodeEdit::highlightBrackets() { + es_brackets.clear(); + QTextCursor stc = ui->textCode->textCursor(), tc; + QTextEdit::ExtraSelection es; + stc.setPosition(stc.position()); + QTextCursor::MoveOperation mop[2] = {QTextCursor::Left, QTextCursor::Right}; + QString mbr[2] = {")]}>", "([{<"}; + for (int d = 0; d < 2; ++d) { + tc = stc; + tc.movePosition(mop[d], QTextCursor::KeepAnchor); + if (!tc.selectedText().isEmpty()) { + char ch = tc.selectedText()[0].toLatin1(); + if (mbr[d].contains(ch)) { + es = es_bracket; + es.cursor = tc; + es_brackets << es; + QTextCursor ftc = tc; + int bcnt = 1; char fch = antiBracket(ch); + while (bcnt > 0) { + ftc.setPosition(ftc.position()); + if (!ftc.movePosition(mop[d], QTextCursor::KeepAnchor)) break; + //qDebug() << tc.selectedText(); + if (ftc.selectedText().isEmpty()) break; + if (ftc.selectedText()[0].toLatin1() == ch) ++bcnt; + if (ftc.selectedText()[0].toLatin1() == fch) --bcnt; + } + if (bcnt == 0) { + es.cursor = ftc; + es_brackets << es; + es.format = es_range.format; + es.cursor.setPosition(tc.position(), QTextCursor::KeepAnchor); + if (!es.cursor.selection().isEmpty()) + es_brackets << es; + } + } + } + } +} + + +void QCodeEdit::applyExtraSelection() { + ui->textCode->setExtraSelections(QList() << es_line << es_selected + << es_custom << es_brackets << es_search_list << es_cursor); +} + + +void QCodeEdit::nextCompletition() { + int ci = completer->currentIndex().row(); + if (ci >= completer->topLevelItemCount() - 1) return; + if (completer->topLevelItem(ci + 1)->flags().testFlag(Qt::ItemIsSelectable)) + completer->setCurrentItem(completer->topLevelItem(ci + 1)); + else { + if (ci >= completer->topLevelItemCount() - 2) return; + completer->setCurrentItem(completer->topLevelItem(ci + 2)); + } +} + + +void QCodeEdit::previousCompletition() { + int ci = completer->currentIndex().row(); + if (ci <= 0) return; + if (completer->topLevelItem(ci - 1)->flags().testFlag(Qt::ItemIsSelectable)) + completer->setCurrentItem(completer->topLevelItem(ci - 1)); + else { + if (ci <= 1) return; + completer->setCurrentItem(completer->topLevelItem(ci - 2)); + } +} + + +void QCodeEdit::clearSearch() { + es_search_list.clear(); + applyExtraSelection(); +} + + +void QCodeEdit::moveToSearch() { + if (es_search_list.isEmpty()) return; + if (cur_search_ind < 0) cur_search_ind += es_search_list.size(); + if (cur_search_ind >= es_search_list.size()) cur_search_ind = 0; + if (cur_search_ind < 0 || cur_search_ind >= es_search_list.size()) return; + ui->textCode->setTextCursor(es_search_list[cur_search_ind].cursor); +} + + +int QCodeEdit::searchIndFromCursor() { + if (es_search_list.isEmpty()) return -1; + int ci = ui->textCode->textCursor().anchor(); + for (int i = 0; i < es_search_list.size(); ++i) + if (es_search_list[i].cursor.anchor() > ci) + return i - 1; + return -1; +} + + +void QCodeEdit::searchAll() { + QString st = ui->comboSearch->currentText(); + es_search_list.clear(); + if (!st.isEmpty() && !ui->widgetSearch->isHidden()) { + QTextDocument::FindFlags ff = 0; + if (ui->buttonSearchCase->isChecked()) ff |= QTextDocument::FindCaseSensitively; + if (ui->buttonSearchWord->isChecked()) ff |= QTextDocument::FindWholeWords; + QTextCursor tc(ui->textCode->document()->begin()); + QTextEdit::ExtraSelection es = es_search; + while (true) { + tc = ui->textCode->document()->find(st, tc, ff); + if (tc.isNull()) break; + es.cursor = tc; + es_search_list << es; + } + } + applyExtraSelection(); + QString ss; + if (es_search_list.isEmpty()) + ss = "color: rgb(180, 0, 0);"; + ui->comboSearch->lineEdit()->setStyleSheet(ss); +} + + +void QCodeEdit::search_triggered() { + QTextCursor tc = ui->textCode->textCursor(); + QString st = tc.selectedText(); + if (st.isEmpty()) { + tc.select(QTextCursor::WordUnderCursor); + st = tc.selectedText(); + } + search(st); + //QMetaObject::invokeMethod(ui->comboSearch->lineEdit(), "returnPressed"); + if (ui->comboSearch->findText(st) < 0) + ui->comboSearch->insertItem(0, st); +} + + +void QCodeEdit::syncScrolls() { + ui->textLines->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value()); + ui->textLines->setHorizontalScrollBarPolicy(ui->textCode->horizontalScrollBar()->isVisible() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff); + //qDebug() << "!!!"; +} + + +void QCodeEdit::scrollUp() { + ui->textCode->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value() - 1); +} + + +void QCodeEdit::scrollDown() { + ui->textCode->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value() + 1); +} + + +void QCodeEdit::deleteLine() { + QTextCursor tc = ui->textCode->textCursor(); + tc.movePosition(QTextCursor::EndOfLine); + tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + bool md = true; + if (!tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor)) { + tc.movePosition(QTextCursor::StartOfLine); + tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + md = false; + } + tc.removeSelectedText(); + tc.movePosition(QTextCursor::StartOfLine); + if (md) tc.movePosition(QTextCursor::Down); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::copyLineUp() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(ss); tc.setPosition(se, QTextCursor::KeepAnchor); + bool ins_nl = false; + if (!tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor)) + ins_nl = true; + QString l = tc.selectedText(); + tc.beginEditBlock(); + tc.setPosition(ss); + if (ins_nl) + l.append("\n"); + tc.insertText(l); + tc.setPosition(ss_); + tc.setPosition(se_, QTextCursor::KeepAnchor); + tc.endEditBlock(); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::copyLineDown() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(ss); tc.setPosition(se, QTextCursor::KeepAnchor); + bool ins_nl = false; + if (!tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor)) + ins_nl = true; + QString l = tc.selectedText(); + tc.beginEditBlock(); + tc.setPosition(ss); + ss_ += l.size(); se_ += l.size(); + if (ins_nl) { + l.append("\n"); + ss_++; se_++; + } + tc.insertText(l); + tc.setPosition(ss_); + tc.setPosition(se_, QTextCursor::KeepAnchor); + tc.endEditBlock(); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::moveLineUp() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(ss); + if (!tc.movePosition(QTextCursor::Up)) + return; + tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); + QString l = tc.selectedText(); + ss -= l.size(); se -= l.size(); + ss_ -= l.size(); se_ -= l.size(); + tc.beginEditBlock(); + tc.removeSelectedText(); + tc.setPosition(se); + bool de = false; + if (!tc.movePosition(QTextCursor::Right)) { + l.prepend("\n"); + de = true; + } + tc.insertText(l); + if (de) { + tc.movePosition(QTextCursor::End); + tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); + tc.removeSelectedText(); + } + tc.setPosition(ss_); + tc.setPosition(se_, QTextCursor::KeepAnchor); + tc.endEditBlock(); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::moveLineDown() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(se); + if (!tc.movePosition(QTextCursor::Right)) + return; + bool de = false; + if (!tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor)) { + tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); + de = true; + } + QString l = tc.selectedText(); + tc.beginEditBlock(); + tc.removeSelectedText(); + tc.setPosition(ss); + if (de) l += "\n"; + tc.insertText(l); + if (de) { + tc.movePosition(QTextCursor::End); + tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); + tc.removeSelectedText(); + } + ss += l.size(); se += l.size(); + ss_ += l.size(); se_ += l.size(); + tc.setPosition(ss_); + tc.setPosition(se_, QTextCursor::KeepAnchor); + tc.endEditBlock(); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::indent() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(ss); + while (tc.position() < se_) { + tc.insertText("\t"); + se_++; + tc.movePosition(QTextCursor::StartOfLine); + if (!tc.movePosition(QTextCursor::Down)) + break; + } + tc.setPosition(ss_ + 1); + tc.setPosition(se_, QTextCursor::KeepAnchor); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::deindent() { + QTextCursor tc = ui->textCode->textCursor(); + int ss = tc.selectionStart(), ss_ = ss, se = tc.selectionEnd(), se_ = se; + QString st_ = tc.selection().toPlainText(); + if (st_.endsWith("\n")) { + st_.chop(1); + se--; se_--; + } + tc.setPosition(ss); tc.movePosition(QTextCursor::StartOfLine); ss = tc.position(); + tc.setPosition(se); tc.movePosition(QTextCursor::EndOfLine); se = tc.position(); + tc.setPosition(ss); + bool first = true; + while (tc.position() < se_) { + tc.movePosition(QTextCursor::StartOfLine); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + int rs = 0; + if (tc.selectedText() == "\t") { + tc.removeSelectedText(); + rs = 1; + } else { + for (int i = 0; i < 4; ++i) { + tc.movePosition(QTextCursor::StartOfLine); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + if (tc.selectedText() == " ") { + tc.removeSelectedText(); + rs++; + } + } + } + if (first) { + first = false; + ss_ -= rs; + } + se_ -= rs; + tc.movePosition(QTextCursor::StartOfLine); + if (!tc.movePosition(QTextCursor::Down)) + break; + } + tc.setPosition(ss_); + tc.setPosition(se_, QTextCursor::KeepAnchor); + ui->textCode->setTextCursor(tc); +} + + +void QCodeEdit::autoIndent() { + QTextCursor tc = ui->textCode->textCursor(), stc = tc; + tc.movePosition(QTextCursor::StartOfLine); + if (!tc.movePosition(QTextCursor::Up)) return; + tc.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor); + QString line = tc.selectedText(), tabs; + int i = 0; + for (; i < line.size(); ++i) + if (!line[i].isSpace()) { + tabs = line.left(i); + break; + } + if (i >= line.size()) + tabs = line.left(line.size() - 1); + int nt = qMax(0, line.count(QChar('{')) - line.count(QChar('}'))); + tabs.append(QString("\t").repeated(nt)); + if (tabs.isEmpty()) return; + stc.insertText(tabs); + ui->textCode->setTextCursor(stc); +} + + +void QCodeEdit::scrollToTop() { + prev_lc = -1; + updateLines(); + ui->textCode->verticalScrollBar()->setValue(0); + ui->textLines->verticalScrollBar()->setValue(0); +} + + +void QCodeEdit::newLine() { + QTextCursor tc = ui->textCode->textCursor(); + tc.movePosition(QTextCursor::EndOfLine); + tc.insertText("\n"); + ui->textCode->setTextCursor(tc); + QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection); +} + + +void QCodeEdit::newLineBefore() { + QTextCursor tc = ui->textCode->textCursor(); + tc.movePosition(QTextCursor::StartOfLine); + tc.insertText("\n"); + tc.movePosition(QTextCursor::Up); + ui->textCode->setTextCursor(tc); + QMetaObject::invokeMethod(this, "autoIndent", Qt::QueuedConnection); +} + + +void QCodeEdit::setFocus() { + ui->textCode->setFocus(); +} + + +void QCodeEdit::setText(const QString & t) { + ui->textCode->setPlainText(t); +} + + +void QCodeEdit::updateLines() { + if (timer > 0) killTimer(timer); + timer = startTimer(500); + ui->textCode->setTabStopWidth(ui->textCode->fontMetrics().width(" ")); + int lc = ui->textCode->document()->lineCount(); + if (prev_lc == lc) return; + prev_lc = lc; + ui->textLines->setFixedWidth(ui->textLines->fontMetrics().width(QString(" %1").arg(lc))); + ui->textLines->clear(); + for (int i = 1; i <= lc; ++i) + ui->textLines->appendPlainText(QString("%1").arg(i)); + ui->textLines->verticalScrollBar()->setValue(ui->textCode->verticalScrollBar()->value()); +} + + +QString QCodeEdit::selectArg(QString s, int arg) { + if (!s.contains('(') || arg < 0) return s; + QString ss = s.left(s.indexOf('(')); + s.remove(0, ss.size()); + if (s.startsWith('(')) s.remove(0, 1); + if (s.endsWith(')')) s.chop(1); + QStringList al = s.split(","); + QString ret = ss + "("; + for (int i = 0; i < al.size(); ++i) { + if (i > 0) ret += ", "; + if (i == arg) ret += ""; + ret += al[i].trimmed(); + if (i == arg) ret += ""; + } + ret += ")"; + return ret; +} + + +void QCodeEdit::raiseHelp(QTextCursor tc, int arg) { + bool ok; + QPair scope = getScope(tc, &ok); + QString st = tc.selectedText(); + if (arg >= 0) st = scope.second; + if (!ok || st.isEmpty()) { + hideHelp(); + return; + } + ok = false; + ACList acl(autoCompletitionList(scope.first, scope.second)); + foreach (const ACPair & i, acl) { + foreach (const StringsPair & s, i.second) { + QString ts = s.second; + //qDebug() << ts << st; + if (ts != st) { + if (ts.startsWith(st)) { + ts.remove(0, st.size()); + ts = ts.trimmed(); + if (!ts.isEmpty()) { + if (ts[0] != '(') + continue; + } + } else + continue; + } + //qDebug() << s.second << st; + ACClass acc = ac_classes.value(i.first); + lbl_help[0]->setIcon(acc.icon); + lbl_help[0]->setText(QString("[%1] %2 %3").arg(acc.name, s.first, selectArg(s.second, arg))); + ok = true; + break; + } + if (ok) break; + } + if (!ok) { + hideHelp(); + return; + } + //qDebug() << "help found" << tc.selectionStart() << tc.selectionEnd(); + es_cursor.cursor = tc; + applyExtraSelection(); + //tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); + lbl_help[0]->setFont(font()); + qApp->processEvents(); + widget_help->resize(widget_help->sizeHint()); + qApp->processEvents(); + QRect whr = ui->textCode->cursorRect(tc); + whr.setWidth(ui->textCode->fontMetrics().width(st)); + QPoint whp; + whp.setX(whr.left() - whr.width() - (widget_help->width() - whr.width()) / 2); + whp.setY(whr.top() - widget_help->height() - (fontHeight() / 3)); + //qDebug() << whr << whp << widget_help->width() << ", " << st; + widget_help->move(ui->textCode->viewport()->mapToGlobal(whp)); + widget_help->show(); + widget_help->raise(); + cursor_scope = scope.first; + cursor_scope << scope.second; + //qDebug() << "tooltip" << st; +} + + +void QCodeEdit::hideHelp() { + widget_help->hide(); + es_cursor.cursor = QTextCursor(); + cursor_scope.clear(); + applyExtraSelection(); +} + + +QTextCursor QCodeEdit::functionStart(QTextCursor tc, int * arg) { + QString doc = ui->textCode->toPlainText(); + int bcnt = 0, a = 0, i = -1; + for (i = tc.position() - 1; i >= 0; --i) { + if (doc[i] == ')') bcnt++; + if (doc[i] == '(') { + if (bcnt == 0) + break; + else + bcnt--; + } + //if (doc[i] == '(') bcnt--; + if (doc[i] == ',' && bcnt == 0) a++; + } + if (i < 0) return QTextCursor(); + if (arg) *arg = a; + QTextCursor ret(ui->textCode->document()); + ret.setPosition(i); + //qDebug() << "found" << i << a; + return ret; +} + + +QCodeEdit::ACList QCodeEdit::wordsCompletitionList(const QString & written) const { + QCodeEdit::ACList ret; + if (!written.isEmpty()) { + QTextCursor tc = QTextCursor(ui->textCode->document()->begin()), stc; + QStringList acwl; + tc = QTextCursor(ui->textCode->document()->begin()); + while (true) { + tc = ui->textCode->document()->find(written, tc); + if (tc.isNull()) break; + stc = tc; + stc.movePosition(QTextCursor::Left); + stc.select(QTextCursor::WordUnderCursor); + if (!stc.selectedText().isEmpty() && stc.selectedText().trimmed() != written) + acwl << stc.selectedText(); + } + acwl.removeDuplicates(); + ACPair acl; + acl.first = -1; + foreach (const QString & s, acwl) + acl.second << StringsPair("", s); + ret << acl; + } + return ret; +} + + +QPair QCodeEdit::getScope(QTextCursor tc, bool * ok) { + QPair ret; + QTextCursor stc = tc; + if (tc.isNull()) { + completer->hide(); + if (ok) *ok = false; + return ret; + } + int line = tc.block().firstLineNumber(); + if (completer->isVisible()) { + if (auto_comp_pl != line) { + completer->hide(); + auto_comp_pl = line; + if (ok) *ok = false; + return ret; + } + } + QString doc = ui->textCode->toPlainText(); + auto_comp_pl = line; + completer->clear(); + int spos = tc.position(), cpos = spos; + tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor); + QStringList scope; + QString written = tc.selectedText().trimmed(); + //qDebug() << "\n*** invokeAutoCompletition ***"; + if (written != "_" && !written.leftJustified(1)[0].isLetterOrNumber()) { + written.clear(); + } else { + cpos = skipCWord(doc, spos); + if (cpos >= 0) + written = doc.mid(cpos, spos - cpos).trimmed(); + } + while (cpos >= 0) { + cpos--; + //qDebug() << "char =" << doc.mid(cpos, 1); + if (doc.mid(cpos, 1) != ".") break; + QChar c = doc.mid(cpos - 1, 1).leftJustified(1)[0]; + int ppos = cpos; + if (c == '\"' || c == ')' || c == ']') { + cpos = skipRange(doc, cpos, pairChar(c), c, '\\'); + //qDebug() << "range" << cpos; + if (cpos < 0) break; + } + int npos = skipCWord(doc, cpos); + if (npos < 0) break; + scope.push_front(doc.mid(npos, ppos - npos)); + cpos = npos; + } + ret.first = scope; + ret.second = written; + if (ok) *ok = true; + return ret; +} + + +void QCodeEdit::invokeAutoCompletition(bool force) { + int arg = -1; + QTextCursor htc = functionStart(ui->textCode->textCursor(), &arg); + if (!htc.isNull()) { + //qDebug() << "raise"; + raiseHelp(htc, arg); + } + bool ok; + QPair scope = getScope(ui->textCode->textCursor(), &ok); + if (!ok) return; + ACList acl(autoCompletitionList(scope.first, scope.second)); + //qDebug() << written << scope << acl.size(); + if (scope.first.isEmpty() && scope.second.isEmpty() && !force) { + completer->hide(); + hideHelp(); + return; + } + acl << wordsCompletitionList(scope.second); + QFont bf(font()); + bf.setBold(true); + foreach (const ACPair & ac, acl) { + if (ac.second.isEmpty()) continue; + ACClass acc = ac_classes.value(ac.first); + QTreeWidgetItem * gi = new QTreeWidgetItem(); + gi->setText(0, acc.name); + gi->setTextAlignment(0, Qt::AlignCenter); + gi->setTextAlignment(1, Qt::AlignCenter); + gi->setFont(0, bf); + gi->setBackgroundColor(0, Qt::lightGray); + gi->setFlags(Qt::ItemIsEnabled); + completer->addTopLevelItem(gi); + gi->setFirstColumnSpanned(true); + foreach (const StringsPair & s, ac.second) { + QTreeWidgetItem * ni = new QTreeWidgetItem(); + ni->setIcon(0, acc.icon); + ni->setText(0, s.first); + ni->setText(1, s.second); + completer->addTopLevelItem(ni); + } + } + if (completer->topLevelItemCount() > 1) + completer->setCurrentItem(completer->topLevelItem(1)); + if (completer->isHidden()) + completer->move(ui->textCode->mapToGlobal(ui->textCode->cursorRect().bottomRight())); + if (completer->topLevelItemCount() > 0) { + completer->setVisible(true); + //qApp->processEvents(); + int sz = completer->verticalScrollBar()->width(); + for (int i = 0; i < completer->header()->count(); ++i) + sz += qMax(sz, ((QAbstractItemView*)completer)->sizeHintForColumn(i)); + completer->resize(sz, fontHeight() * 16); + } else + completer->hide(); +} + + +void QCodeEdit::commitCompletition() { + if (completer->currentItem() == 0) return; + if (!completer->currentItem()->flags().testFlag(Qt::ItemIsSelectable)) return; + QString ins = completer->currentItem()->text(1), ret = completer->currentItem()->text(0); + QTextCursor tc = ui->textCode->textCursor(), stc = tc; + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + bool ins_br = true, shifted = false; + if (!tc.selectedText().isEmpty()) { +// if (tc.selectedText()[0].isSpace()) { + if (!tc.selectedText()[0].isLetterOrNumber() && !tc.selectedText()[0].isSpace() && !(tc.selectedText()[0] == '_')) { + stc.movePosition(QTextCursor::Left); + shifted = true; + } else { + tc.movePosition(QTextCursor::Left); + tc.movePosition(QTextCursor::EndOfWord); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + } + if (!tc.selectedText().isEmpty()) + if (tc.selectedText()[0].toLatin1() == '(') + ins_br = false; + } + if (ins.contains("(")) + ins = ins.left(ins.indexOf("(")) + "()"; + if (!ins_br && ins.endsWith("()")) + ins.chop(2); + tc = stc; + tc.select(QTextCursor::WordUnderCursor); + if (!tc.selectedText().leftJustified(1)[0].isLetterOrNumber() && !(tc.selectedText().leftJustified(1)[0] == '_')) { + tc = stc; + if (shifted) + tc.movePosition(QTextCursor::Right); + } + ui->textCode->setTextCursor(tc); + ui->textCode->textCursor().insertText(ins); + tc = ui->textCode->textCursor(); + if (ins_br) { + if (ret == "void") { + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + if (tc.selectedText() != ";") { + ui->textCode->textCursor().insertText(";"); + tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); + } + } + if (ins.endsWith(")") && !completer->currentItem()->text(1).endsWith("()")) { + tc.movePosition(QTextCursor::Left); + ui->textCode->setTextCursor(tc); + } + } else { + if (completer->currentItem()->text(1).endsWith(")")) { + tc.movePosition(QTextCursor::Right); + ui->textCode->setTextCursor(tc); + } + if (completer->currentItem()->text(1).endsWith("()")) { + tc.movePosition(QTextCursor::Right); + ui->textCode->setTextCursor(tc); + } + } + completer->hide(); +} + + +void QCodeEdit::textEdit_cursorPositionChanged() { + es_line.cursor = ui->textCode->textCursor(); + es_line.cursor.select(QTextCursor::LineUnderCursor); + es_line.cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor); + highlightBrackets(); + applyExtraSelection(); +} + + +void QCodeEdit::textEdit_textChanged() { + if (_replacing) return; + searchAll(); + updateLines(); +} + + +void QCodeEdit::textEdit_selectionChanged() { + es_selected.clear(); + QString sf = ui->textCode->textCursor().selectedText(); + if (sf.trimmed().isEmpty() || sf.contains("\n")) { + applyExtraSelection(); + return; + } + QTextCursor tc(ui->textCode->document()->begin()); + QTextEdit::ExtraSelection es; + es.format.setBackground(QColor(251, 250, 150)); + while (true) { + tc = ui->textCode->document()->find(sf, tc, QTextDocument::FindCaseSensitively | QTextDocument::FindWholeWords); + if (tc.isNull()) break; + es.cursor = tc; + es_selected << es; + } + applyExtraSelection(); +} + + +void QCodeEdit::setShowSpaces(bool yes) { + spaces_ = yes; + QTextOption to = ui->textCode->document()->defaultTextOption(); + QTextOption::Flags tof = to.flags(); + if (yes) tof |= QTextOption::ShowTabsAndSpaces; + else tof &= ~QTextOption::ShowTabsAndSpaces; + to.setFlags(tof); + ui->textCode->document()->setDefaultTextOption(to); +} + + +void QCodeEdit::setShowLineNumbers(bool yes) { + ui->textLines->setVisible(yes); +} + + +void QCodeEdit::search(const QString & t) { + ui->widgetSearch->show(); + ui->comboSearch->setEditText(QString()); + ui->comboSearch->setEditText(t); + ui->comboSearch->setFocus(); + //searchAll(); + searchNext(false); +} + + +void QCodeEdit::searchNext(bool next) { + if (es_search_list.isEmpty()) + return; + cur_search_ind = searchIndFromCursor() + (next ? 1 : 0); + moveToSearch(); +} + + +void QCodeEdit::searchPrevious() { + if (es_search_list.isEmpty()) + return; + cur_search_ind = searchIndFromCursor() - 1; + moveToSearch(); +} + + +void QCodeEdit::hideSearch() { + ui->widgetSearch->hide(); + searchAll(); +} + + +void QCodeEdit::on_comboSearch_currentTextChanged(const QString & t) { + searchAll(); + searchNext(false); +} + + +void QCodeEdit::on_buttonReplace_clicked() { + if (es_search_list.isEmpty() || cur_search_ind < 0 || cur_search_ind >= es_search_list.size()) return; + if (ui->textCode->textCursor() != es_search_list[cur_search_ind].cursor) return; + if (ui->textCode->textCursor().selectedText().size() != es_search_list[cur_search_ind].cursor.selectedText().size()) return; + ui->textCode->textCursor().insertText(ui->comboReplace->currentText()); +} + + +void QCodeEdit::on_buttonReplaceSearch_clicked() { + on_buttonReplace_clicked(); + searchNext(); +} + + +void QCodeEdit::on_buttonReplaceAll_clicked() { + _replacing = true; + QString rt = ui->comboReplace->currentText(); + for (int i = es_search_list.size() - 1; i >= 0; --i) + es_search_list[i].cursor.insertText(rt); + _replacing = false; + textEdit_textChanged(); +} diff --git a/test/qad/widgets/qcodeedit.h b/test/qad/widgets/qcodeedit.h new file mode 100644 index 0000000..1dc844c --- /dev/null +++ b/test/qad/widgets/qcodeedit.h @@ -0,0 +1,163 @@ +#ifndef QCODEEDIT_H +#define QCODEEDIT_H + +#include +#include +#include +#include +#include "iconedlabel.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +namespace Ui { + class QCodeEdit; +} + + +class QCodeEdit: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(bool showSpaces READ showSpaces WRITE setShowSpaces) + Q_PROPERTY(bool showLineNumbers READ showLineNumbers WRITE setShowLineNumbers) + Q_PROPERTY(QFont editorFont READ editorFont WRITE setEditorFont) + +public: + QCodeEdit(QWidget * parent = 0); + ~QCodeEdit(); + + enum ACClassType { + Keyword, + Function, + Namespace + }; + + QTextCursor textCursor() const; + QTextDocument * document() const; + void setDocument(QTextDocument * doc); + void setTextCursor(const QTextCursor & c); + void centerCursor(); + void insertText(const QString & text); + void appendText(const QString & text); + void setCustomExtraSelection(const QList & es); + QRect cursorRect() const; + QRect cursorRect(const QTextCursor & cursor) const; + QString text() const; + QStringList cursorScope() const; + bool showSpaces() const {return spaces_;} + bool showLineNumbers() const; + + void setEditorFont(QFont f); + QFont editorFont() const; + + QPlainTextEdit * textEdit() const; + + void registerAutoCompletitionClass(int id, ACClassType ac_class, const QString & name, const QIcon & icon = QIcon()) {ac_classes[id] = ACClass(id, ac_class, name, icon);} + +protected: + typedef QPair StringsPair; + typedef QPair > ACPair; + typedef QList ACList; + + virtual ACList autoCompletitionList(const QStringList & scope, const QString & written) const {return ACList();} + virtual void parse() {} + virtual void documentUnset() {} + virtual void documentChanged(QTextDocument * d) {} + QString selectArg(QString s, int arg); + void raiseHelp(QTextCursor tc, int arg = -1); + QTextCursor functionStart(QTextCursor tc, int * arg); + ACList wordsCompletitionList(const QString & written) const; + QPair getScope(QTextCursor tc, bool * ok = 0); + static int skipRange(const QString & s, int pos, QChar oc, QChar cc, QChar sc = QChar()); + static int skipCWord(const QString & s, int pos); + static bool matchWritten(QString s, QString w); + static QChar pairChar(QChar c); + + Ui::QCodeEdit * ui; + +private: + struct ACClass { + ACClass(int i = -2, ACClassType c = QCodeEdit::Keyword, const QString & n = QString(), const QIcon & ic = QIcon()): id(i), class_(c), name(n), icon(ic) {} + int id; + ACClassType class_; + QString name; + QIcon icon; + }; + + QTreeWidget * completer; + IconedLabel * lbl_help[2]; + QFrame * widget_help; + QTextEdit::ExtraSelection es_line, es_cursor, es_bracket, es_range, es_search; + QList es_selected, es_custom, es_brackets, es_search_list; + QMap ac_classes; + QStringList cursor_scope; + int prev_lc, auto_comp_pl, timer, cur_search_ind, pos_press, pos_el_press; + bool spaces_, _ignore_focus_out, _first, _destructor, _replacing; + + bool eventFilter(QObject * o, QEvent * e); + void showEvent(QShowEvent * ); + void timerEvent(QTimerEvent * ); + void leaveEvent(QEvent * ); + void highlightBrackets(); + void applyExtraSelection(); + void nextCompletition(); + void previousCompletition(); + void clearSearch(); + void moveToSearch(); + int searchIndFromCursor(); + +private slots: + void syncScrolls(); + void scrollUp(); + void scrollDown(); + void hideHelp(); + void deleteLine(); + void copyLineUp(); + void copyLineDown(); + void moveLineUp(); + void moveLineDown(); + void indent(); + void deindent(); + void autoIndent(); + void invokeAutoCompletition(bool force = false); + void commitCompletition(); + void searchAll(); + void search_triggered(); + void textEdit_cursorPositionChanged(); + void textEdit_textChanged(); + void textEdit_selectionChanged(); + void on_comboSearch_currentTextChanged(const QString & t); + void on_buttonReplace_clicked(); + void on_buttonReplaceSearch_clicked(); + void on_buttonReplaceAll_clicked(); + +public slots: + void updateLines(); + void scrollToTop(); + void newLine(); + void newLineBefore(); + void setFocus(); + void setText(const QString & t); + void setShowSpaces(bool yes); + void setShowLineNumbers(bool yes); + void search(const QString & t); + void searchNext(bool next = true); + void searchPrevious(); + void hideSearch(); + +signals: + void textChanged(); + void parseRequest(); + +}; + +//Q_DECLARE_OPERATORS_FOR_FLAGS(QPIConsole::Formats) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCODEEDIT_H diff --git a/test/qad/widgets/qcodeedit.ui b/test/qad/widgets/qcodeedit.ui new file mode 100644 index 0000000..8874863 --- /dev/null +++ b/test/qad/widgets/qcodeedit.ui @@ -0,0 +1,388 @@ + + + QCodeEdit + + + + 0 + 0 + 726 + 577 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + Qt::NoFocus + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QPlainTextEdit::NoWrap + + + Qt::NoTextInteraction + + + + + + + QFrame::NoFrame + + + false + + + QPlainTextEdit::NoWrap + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Search: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + Case sensitive + + + Aa + + + true + + + + + + + + 0 + 0 + + + + Whole words + + + W + + + true + + + + + + + + 0 + 0 + + + + Previous + + + + :/icons/go-previous.png:/icons/go-previous.png + + + + + + + + 0 + 0 + + + + Next + + + + :/icons/go-next.png:/icons/go-next.png + + + + + + + + + Replace: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + Replace + + + R + + + + + + + + 0 + 0 + + + + Replace and search + + + Rs + + + + + + + + 0 + 0 + + + + Replace all + + + Ra + + + + + + + + + + 0 + 0 + + + + true + + + QComboBox::InsertAtTop + + + + + + + + 0 + 0 + + + + true + + + QComboBox::InsertAtTop + + + + + + + + + + + + + + EComboBox + QComboBox +
ecombobox.h
+
+
+ + + + buttonSearchNext + clicked() + QCodeEdit + searchNext() + + + 723 + 670 + + + 731 + 578 + + + + + buttonSearchPrev + clicked() + QCodeEdit + searchPrevious() + + + 691 + 670 + + + 743 + 536 + + + + + buttonSearchCase + toggled(bool) + QCodeEdit + searchAll() + + + 612 + 654 + + + 740 + 499 + + + + + buttonSearchWord + toggled(bool) + QCodeEdit + searchAll() + + + 648 + 658 + + + 753 + 511 + + + + + + search(QString) + searchNext() + searchPrevious() + searchAll() + hideSearch() + +
diff --git a/test/qad/widgets/qipedit.cpp b/test/qad/widgets/qipedit.cpp new file mode 100644 index 0000000..dab01f1 --- /dev/null +++ b/test/qad/widgets/qipedit.cpp @@ -0,0 +1,82 @@ +#include "qipedit.h" + +QIPEdit::QIPEdit(QWidget * parent, const QString & ip): QWidget(parent) { + layout = new QBoxLayout(QBoxLayout::LeftToRight, this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + QIntValidator * validator = new QIntValidator(0, 255, this); + for (int i = 0; i < 4; i++) { + edits.push_back(new QLineEdit(this)); + edits.back()->setAlignment(Qt::AlignHCenter); + edits.back()->setMaxLength(3); + edits.back()->setValidator(validator); + edits.back()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + layout->addWidget(edits.back()); + if (i < 3) { + dots.push_back(new QLabel(this)); + dots.back()->setText("."); + dots.back()->adjustSize(); + layout->addWidget(dots.back()); + } + } + //for (int i = 0; i < 3; i++) edits[i]->setTabOrder(edits[i+1], edits[i]); + connect(edits[0], SIGNAL(returnPressed()), this, SLOT(returnPressed0())); + connect(edits[1], SIGNAL(returnPressed()), this, SLOT(returnPressed1())); + connect(edits[2], SIGNAL(returnPressed()), this, SLOT(returnPressed2())); + connect(edits[3], SIGNAL(returnPressed()), this, SLOT(returnPressed3())); + connect(edits[0], SIGNAL(textChanged(const QString & )), this, SLOT(textChanged0(const QString & ))); + connect(edits[1], SIGNAL(textChanged(const QString & )), this, SLOT(textChanged1(const QString & ))); + connect(edits[2], SIGNAL(textChanged(const QString & )), this, SLOT(textChanged2(const QString & ))); + connect(edits[3], SIGNAL(textChanged(const QString & )), this, SLOT(textChanged3(const QString & ))); + setLayout(layout); + setIP(ip); + cind = 0; +} + + +QIPEdit::~QIPEdit() { + foreach (QLineEdit * i, edits) + delete i; + foreach (QLabel * i, dots) + delete i; + edits.clear(); + dots.clear(); + delete layout; +} + + +void QIPEdit::setIP(const QString & text) { + QString s, str = text; + s = str.left(str.indexOf('.')); + edits[0]->setText(s == "" ? "0" : s); + str = str.right(str.length() - s.length() - 1); + s = str.left(str.indexOf('.')); + edits[1]->setText(s == "" ? "0" : s); + str = str.right(str.length() - s.length() - 1); + s = str.left(str.indexOf('.')); + edits[2]->setText(s == "" ? "0" : s); + str = str.right(str.length() - s.length() - 1); + edits[3]->setText(str == "" ? "0" : str); +} + + +QString QIPEdit::IP() { + QString s; + if (edits[0]->text() == "") s = "0."; + else s = edits[0]->text() + "."; + if (edits[1]->text() == "") s += "0."; + else s += edits[1]->text() + "."; + if (edits[2]->text() == "") s += "0."; + else s += edits[2]->text() + "."; + if (edits[3]->text() == "") s += "0"; + else s += edits[3]->text(); + return s; +} + + +void QIPEdit::returnPress(int index) { + if (index < 3) { + edits[index + 1]->setFocus(); + edits[index + 1]->setSelection(0, 3); + } +} diff --git a/test/qad/widgets/qipedit.h b/test/qad/widgets/qipedit.h new file mode 100644 index 0000000..4d44aef --- /dev/null +++ b/test/qad/widgets/qipedit.h @@ -0,0 +1,48 @@ +#ifndef QIPEDIT_H +#define QIPEDIT_H + +#include +#include +#include +#include +#include +#include + +class QIPEdit: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QString IP READ IP WRITE setIP) + +public: + QIPEdit(QWidget * parent = 0, const QString & ip = ""); + ~QIPEdit(); + + QString IP(); + +private: + void returnPress(int index); + inline void textChange(int index, const QString & text) {if (text.length() == 3 && isVisible()) returnPress(index); emit valueChanged(IP());} + + int cind; + QBoxLayout * layout; + QVector edits; + QVector dots; + +public slots: + void setIP(const QString & text); + +private slots: + void returnPressed0() {returnPress(0);} + void returnPressed1() {returnPress(1);} + void returnPressed2() {returnPress(2);} + void returnPressed3() {returnPress(3);} + void textChanged0(const QString & text) {textChange(0, text);} + void textChanged1(const QString & text) {textChange(1, text);} + void textChanged2(const QString & text) {textChange(2, text);} + void textChanged3(const QString & text) {textChange(3, text);} + +signals: + void valueChanged(QString); +}; + +#endif // QIPEDIT_H diff --git a/test/qad/widgets/qpiconfignewdialog.cpp b/test/qad/widgets/qpiconfignewdialog.cpp new file mode 100644 index 0000000..18c450a --- /dev/null +++ b/test/qad/widgets/qpiconfignewdialog.cpp @@ -0,0 +1,77 @@ +#include "qpiconfignewdialog.h" +#include "ui_qpiconfignewdialog.h" + + +QPIConfigNewDialog::QPIConfigNewDialog(QWidget * parent): QDialog(parent) { + ui = new Ui::QPIConfigNewDialog(); + ui->setupUi(this); + radios = findChildren(); + ui->widgetValue->hideAll(); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); +} + + +void QPIConfigNewDialog::changeEvent(QEvent * e) { + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void QPIConfigNewDialog::typeChanged() { + foreach (QRadioButton * i, radios) { + if (i->isChecked()) { + ui->widgetValue->setType(i->property("type").toString()); + return; + } + } +} + + +QString QPIConfigNewDialog::type() { + foreach (QRadioButton * i, radios) + if (i->isChecked()) + return i->property("type").toString(); + return " "; +} + + +void QPIConfigNewDialog::reset(bool node) { + ui->lineName->clear(); + ui->lineComment->clear(); + ui->radioType_0->setChecked(true); + ui->lineName->setFocus(); + ui->widgetValue->setType("s"); + ui->widgetValue->value.clear(); + ui->widgetValue->setVisible(!node); + ui->groupType->setVisible(!node); + ui->labelValue->setVisible(!node); + ui->labelComment->setVisible(!node); + ui->lineComment->setVisible(!node); + adjustSize(); +} + + +QString QPIConfigNewDialog::name() { + return ui->lineName->text(); +} + + +QString QPIConfigNewDialog::value() { + return ui->widgetValue->value; +} + + +QString QPIConfigNewDialog::comment() { + return ui->lineComment->text(); +} + + +void QPIConfigNewDialog::on_lineName_textChanged(const QString & text) { + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); +} diff --git a/test/qad/widgets/qpiconfignewdialog.h b/test/qad/widgets/qpiconfignewdialog.h new file mode 100644 index 0000000..ea688fd --- /dev/null +++ b/test/qad/widgets/qpiconfignewdialog.h @@ -0,0 +1,40 @@ +#ifndef QPICONFIGNEWDIALOG_H +#define QPICONFIGNEWDIALOG_H + +#include +#include + + +namespace Ui { + class QPIConfigNewDialog; +} + + +class QPIConfigNewDialog: public QDialog +{ + Q_OBJECT + +public: + QPIConfigNewDialog(QWidget * parent = 0); + + QString type(); + QString name(); + QString value(); + QString comment(); + void reset(bool node = false); + +protected: + void changeEvent(QEvent * e); + + Ui::QPIConfigNewDialog * ui; + +private slots: + void on_lineName_textChanged(const QString & text); + void typeChanged(); + +private: + QList radios; + +}; + +#endif // QPICONFIGNEWDIALOG_H diff --git a/test/qad/widgets/qpiconfignewdialog.ui b/test/qad/widgets/qpiconfignewdialog.ui new file mode 100644 index 0000000..b60137f --- /dev/null +++ b/test/qad/widgets/qpiconfignewdialog.ui @@ -0,0 +1,432 @@ + + + QPIConfigNewDialog + + + + 0 + 0 + 300 + 316 + + + + + 300 + 0 + + + + Dialog + + + + + + Type + + + + + + string + + + true + + + s + + + + + + + integer + + + n + + + + + + + float + + + f + + + + + + + string list + + + l + + + + + + + boolean + + + b + + + + + + + color + + + c + + + + + + + rectangle + + + r + + + + + + + area + + + a + + + + + + + point + + + p + + + + + + + vector + + + v + + + + + + + ip + + + i + + + + + + + + + + Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Comment: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Value: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + ConfigValueWidget + QWidget +
qpiconfigvaluewidget.h
+ 1 +
+
+ + lineName + buttonBox + + + + + buttonBox + accepted() + QPIConfigNewDialog + accept() + + + 126 + 326 + + + 110 + 231 + + + + + buttonBox + rejected() + QPIConfigNewDialog + reject() + + + 169 + 326 + + + 149 + 232 + + + + + radioType_0 + clicked() + QPIConfigNewDialog + typeChanged() + + + 33 + 70 + + + 1 + 61 + + + + + radioType_1 + clicked() + QPIConfigNewDialog + typeChanged() + + + 25 + 88 + + + -1 + 99 + + + + + radioType_2 + clicked() + QPIConfigNewDialog + typeChanged() + + + 17 + 119 + + + 2 + 130 + + + + + radioType_3 + clicked() + QPIConfigNewDialog + typeChanged() + + + 26 + 143 + + + 0 + 165 + + + + + radioType_4 + clicked() + QPIConfigNewDialog + typeChanged() + + + 50 + 170 + + + 2 + 195 + + + + + radioType_5 + clicked() + QPIConfigNewDialog + typeChanged() + + + 243 + 37 + + + 312 + 10 + + + + + radioType_6 + clicked() + QPIConfigNewDialog + typeChanged() + + + 275 + 67 + + + 315 + 40 + + + + + radioType_7 + clicked() + QPIConfigNewDialog + typeChanged() + + + 262 + 95 + + + 311 + 72 + + + + + radioType_8 + clicked() + QPIConfigNewDialog + typeChanged() + + + 250 + 113 + + + 313 + 104 + + + + + radioType_9 + clicked() + QPIConfigNewDialog + typeChanged() + + + 286 + 145 + + + 311 + 138 + + + + + radioType_10 + clicked() + QPIConfigNewDialog + typeChanged() + + + 273 + 168 + + + 312 + 167 + + + + + + typeChanged() + +
diff --git a/test/qad/widgets/qpiconfigvaluewidget.cpp b/test/qad/widgets/qpiconfigvaluewidget.cpp new file mode 100644 index 0000000..c47eba3 --- /dev/null +++ b/test/qad/widgets/qpiconfigvaluewidget.cpp @@ -0,0 +1,98 @@ +#include "qpiconfigvaluewidget.h" +#include "qpievaluator.h" + + +ConfigValueWidget::ConfigValueWidget(QWidget * parent): QWidget(parent), lay(QBoxLayout::Down, this) { + lay.setContentsMargins(0, 0, 0, 0); + w_integer.setRange(INT_MIN, INT_MAX); + w_float.setRange(-DBL_MAX, DBL_MAX); + w_float.setDecimals(5); + active = true; + lay.addWidget(&w_string); + lay.addWidget(&w_list); + lay.addWidget(&w_bool); + lay.addWidget(&w_integer); + lay.addWidget(&w_float); + lay.addWidget(&w_color); + lay.addWidget(&w_rect); + lay.addWidget(&w_point); + lay.addWidget(&w_ip); + lay.addWidget(&w_enum); + lay.addWidget(&w_path); + connect(&w_string, SIGNAL(textChanged(QString)), this, SLOT(valueChanged())); + connect(&w_list, SIGNAL(valueChanged()), this, SLOT(valueChanged())); + connect(&w_bool, SIGNAL(toggled(bool)), this, SLOT(valueChanged())); + connect(&w_integer, SIGNAL(valueChanged(int)), this, SLOT(valueChanged())); + connect(&w_float, SIGNAL(valueChanged(double)), this, SLOT(valueChanged())); + connect(&w_color, SIGNAL(colorChanged(QColor)), this, SLOT(valueChanged())); + connect(&w_rect, SIGNAL(valueChanged(QRectF)), this, SLOT(valueChanged())); + connect(&w_point, SIGNAL(valueChanged(QPointF)), this, SLOT(valueChanged())); + connect(&w_ip, SIGNAL(valueChanged(QString)), this, SLOT(valueChanged())); + connect(&w_enum, SIGNAL(currentIndexChanged(int)), this, SLOT(valueChanged())); + connect(&w_path, SIGNAL(valueChanged()), this, SLOT(valueChanged())); +} + + +void ConfigValueWidget::setType(const QString & t) { + hideAll(); + type = t.left(1); + active = false; + if (type == "e") {QStringList en_sl = QPIEvaluator::inBrackets(comment).split(';'); + if (en_sl.size()>1) { + w_enum.show(); w_enum.addItems(en_sl); setValue(value); active = true; return; + } else {type = "s";}} + if (type == "s") {w_string.show(); setValue(value); active = true; return;} + if (type == "l") {w_list.show(); setValue(value); active = true; return;} + if (type == "b") {w_bool.show(); setValue(value); active = true; return;} + if (type == "n") {w_integer.show(); setValue(value); active = true; return;} + if (type == "f") {w_float.show(); setValue(value); active = true; return;} + if (type == "c") {w_color.show(); setValue(value); active = true; return;} + if (type == "r") {w_rect.show(); w_rect.setDecimals(0); setValue(value); active = true; return;} + if (type == "a") {w_rect.show(); w_rect.setDecimals(3); setValue(value); active = true; return;} + if (type == "p") {w_point.show(); w_point.setDecimals(0); setValue(value); active = true; return;} + if (type == "v") {w_point.show(); w_point.setDecimals(3); setValue(value); active = true; return;} + if (type == "i") {w_ip.show(); setValue(value); active = true; return;} + if (type == "F") {w_path.show(); setValue(value); active = true; return;} + if (type == "D") {w_path.show(); setValue(value); active = true; return;} +} + + +void ConfigValueWidget::setValue(const QString & v) { + value = v; + active = false; + if (type == "l") {w_list.setValue(v.split("%|%")); active = true; return;} + if (type == "b") {w_bool.setChecked(v.toInt() > 0 || v.toLower().trimmed() == "true"); active = true; return;} + if (type == "n") {w_integer.setValue(QString2int(v)); active = true; return;} + if (type == "f") {w_float.setValue(v.toDouble()); active = true; return;} + if (type == "c") {w_color.setColor(QString2QColor(v)); active = true; return;} + if (type == "r") {w_rect.setValue(QString2QRectF(v)); active = true; return;} + if (type == "a") {w_rect.setValue(QString2QRectF(v)); active = true; return;} + if (type == "p") {w_point.setValue(QString2QPointF(v)); active = true; return;} + if (type == "v") {w_point.setValue(QString2QPointF(v)); active = true; return;} + if (type == "i") {w_ip.setIP(v); active = true; return;} + if (type == "e") {w_enum.setCurrentIndex(w_enum.findText(v)); active = true; return;} + if (type == "F") {w_path.is_dir = false; w_path.setValue(v); active = true; return;} + if (type == "D") {w_path.is_dir = true; w_path.setValue(v); active = true; return;} + w_string.setText(v); + active = true; +} + + +void ConfigValueWidget::valueChanged() { + if (!active) return; + if (type == "l") {value = w_list.value().join("%|%"); emit changed(this, value); return;} + if (type == "b") {value = w_bool.isChecked() ? "true" : "false"; emit changed(this, value); return;} + if (type == "n") {value = QString::number(w_integer.value()); emit changed(this, value); return;} + if (type == "f") {value = QString::number(w_float.value()); emit changed(this, value); return;} + if (type == "c") {value = QColor2QString(w_color.color()); emit changed(this, value); return;} + if (type == "r") {value = QRectF2QString(w_rect.value()); emit changed(this, value); return;} + if (type == "a") {value = QRectF2QString(w_rect.value()); emit changed(this, value); return;} + if (type == "p") {value = QPointF2QString(w_point.value()); emit changed(this, value); return;} + if (type == "v") {value = QPointF2QString(w_point.value()); emit changed(this, value); return;} + if (type == "i") {value = w_ip.IP(); emit changed(this, value); return;} + if (type == "e") {value = w_enum.currentText(); emit changed(this, value); return;} + if (type == "F") {value = w_path.value(); emit changed(this, value); return;} + if (type == "D") {value = w_path.value(); emit changed(this, value); return;} + value = w_string.text(); + emit changed(this, value); +} diff --git a/test/qad/widgets/qpiconfigvaluewidget.h b/test/qad/widgets/qpiconfigvaluewidget.h new file mode 100644 index 0000000..4e9614f --- /dev/null +++ b/test/qad/widgets/qpiconfigvaluewidget.h @@ -0,0 +1,58 @@ +#ifndef QPICONFIGVALUEWIDGET_H +#define QPICONFIGVALUEWIDGET_H + +#include "qpiconfig.h" +#include "qvariantedit.h" +#include "qrectedit.h" +#include "qpointedit.h" +#include "colorbutton.h" +#include "ecombobox.h" +#include "qipedit.h" +#include "limits.h" +#include "float.h" +#include +#include +#include +#include + + +class ConfigValueWidget: public QWidget +{ + Q_OBJECT + friend class QPIConfigWidget; + friend class QPIConfigNewDialog; +public: + ConfigValueWidget(QWidget * parent = 0); + ~ConfigValueWidget() {hide();} + + void setType(const QString & t); + void setValue(const QString & v); + void setEntry(QPIConfig::Entry * e) {value = e->value(); full_name = e->_full_name; comment = e->comment(); setType(e->type());} + +private: + void hideAll() {w_string.hide(); w_list.hide(); w_bool.hide(); w_integer.hide(); w_float.hide(); w_color.hide(); w_rect.hide(); w_point.hide(); w_ip.hide(); w_enum.hide(); w_path.hide();} + + QString type, value, full_name, comment; + bool active; + QBoxLayout lay; + CLineEdit w_string; + StringListEdit w_list; + ColorButton w_color; + QCheckBox w_bool; + QSpinBox w_integer; + QDoubleSpinBox w_float; + QRectEdit w_rect; + QPointEdit w_point; + QIPEdit w_ip; + QComboBox w_enum; + PathEdit w_path; + +private slots: + void valueChanged(); + +signals: + void changed(ConfigValueWidget * , QString ); + +}; + +#endif // QPICONFIGVALUEWIDGET_H diff --git a/test/qad/widgets/qpiconfigwidget.cpp b/test/qad/widgets/qpiconfigwidget.cpp new file mode 100644 index 0000000..22cabd1 --- /dev/null +++ b/test/qad/widgets/qpiconfigwidget.cpp @@ -0,0 +1,380 @@ +#include "qpiconfigwidget.h" + + +QPIConfigWidget::QPIConfigWidget(QWidget * parent, QPIConfig * c, bool on): QTreeWidget(parent), actionAddItem(this), actionAddNode(this), + actionToItem(this), actionToNode(this), actionRemove(this), + actionExpandAll(this), actionCollapseAll(this) { + active = on; + if (active) { + setColumnCount(4); + setColumnWidth(0, 150); + setColumnWidth(1, 200); + } else setColumnCount(0); + setSelectionMode(ExtendedSelection); + setVerticalScrollMode(ScrollPerPixel); + actionAddItem.setIcon(QIcon(":/icons/item-add.png")); + actionAddNode.setIcon(QIcon(":/icons/node-add.png")); + actionToItem.setIcon(QIcon(":/icons/item.png")); + actionToNode.setIcon(QIcon(":/icons/node.png")); + actionRemove.setIcon(QIcon(":/icons/edit-delete.png")); + popupMenu.addAction(&actionAddItem); + popupMenu.addAction(&actionAddNode); + popupMenu.addSeparator(); + /*popupMenu.addAction(&actionToItem); + popupMenu.addAction(&actionToNode); + popupMenu.addSeparator();*/ + popupMenu.addAction(&actionRemove); + popupMenu.addSeparator(); + popupMenu.addAction(&actionExpandAll); + popupMenu.addAction(&actionCollapseAll); + viewport()->installEventFilter(this); + connect(this, SIGNAL(itemClicked(QTreeWidgetItem * , int)), this, SLOT(itemClicked(QTreeWidgetItem * , int))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem * , int)), this, SLOT(itemChanged(QTreeWidgetItem * , int))); + connect(&actionAddItem, SIGNAL(triggered()), this, SLOT(on_actionAddItem_triggered())); + connect(&actionAddNode, SIGNAL(triggered()), this, SLOT(on_actionAddNode_triggered())); + connect(&actionRemove, SIGNAL(triggered()), this, SLOT(on_actionRemove_triggered())); + connect(&actionExpandAll, SIGNAL(triggered()), this, SLOT(expandAll())); + connect(&actionCollapseAll, SIGNAL(triggered()), this, SLOT(collapseAll())); + read_only_name = read_only_value = read_only_type = read_only_comment = false; + c_hidden << false << false << false << false; + pi = c_pi = 0; + translate(); + setQPIConfig(c); + //resize(600, 400); + //show(); +} + + +void QPIConfigWidget::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + translate(); + return; + } + //for (int i = 0; i < 4; ++i) + // setColumnHidden(i, c_hidden[i]); + QTreeWidget::changeEvent(e); +} + + +bool QPIConfigWidget::eventFilter(QObject * o, QEvent * e) { + if (e->type() == QEvent::MouseButtonPress) { + if (viewport() == qobject_cast(o)) { + pi = itemAt(((QMouseEvent * )e)->pos()); + if (((QMouseEvent * )e)->buttons() == Qt::RightButton) { + qApp->processEvents(); + itemClicked(pi, 1); + popupMenu.popup(((QMouseEvent * )e)->globalPos()); + } + } + } + return QTreeWidget::eventFilter(o, e); +} + + +void QPIConfigWidget::itemClicked(QTreeWidgetItem * item, int column) { + if (item != 0) { + if ((column == 0 && !read_only_name) || (column == 3 && !read_only_comment)) item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable); + else item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + } + bool node = true, ro = read_only_name || read_only_type || read_only_value; + if (item != 0) + if (itemTWidget(item) != 0) + if (itemTWidget(item)->isEnabled()) + node = false; + actionAddItem.setVisible(node && !ro); + actionAddNode.setVisible(node && !ro); + actionRemove.setVisible(!ro && !selectedItems().isEmpty()); +} + + +void QPIConfigWidget::itemChanged(QTreeWidgetItem * item, int column) { + if (item != c_pi) { + c_pi = item; + if (item != 0) { + c_name = item->text(0); + c_comment = item->text(3); + } + return; + } + if (item == 0) return; + if (c_name == item->text(0) && c_comment == item->text(3)) return; + //qDebug() << "change" << item->text(0); + QPIConfig::Entry * e = itemEntry(item); + if (e == 0) return; + if (column == 0) { + buildFullNames(item); + e->setName(item->text(column)); + conf->buildFullNames(e->parent()); + //qDebug() << itemCWidget(item)->full_name; + } + if (column == 3) e->setComment(item->text(column)); + c_name = item->text(0); + c_comment = item->text(3); + emit changed(); +} + + +void QPIConfigWidget::typeChange(int t, UComboBox * c) { + ConfigValueWidget * cw = (ConfigValueWidget * )c->property("qpic_widget").toLongLong(); + cw->setType(types.key(s_types[t])); + conf->getValue(cw->full_name).setType(types.key(s_types[t])); + emit changed(); +} + + +void QPIConfigWidget::valueChange(ConfigValueWidget * w, QString v) { + conf->getValue(w->full_name).setValue(v); + emit changed(); +} + + +void QPIConfigWidget::on_actionAddItem_triggered() { + if (conf == 0 || !active) return; + QString fp; + if (pi == 0) pi = invisibleRootItem(); + else fp = itemCWidget(pi)->full_name + conf->delim; + new_dialog.reset(); + if (new_dialog.exec() == QDialog::Rejected) return; + QPIConfig::Entry * e; + if (pi->childCount() == 0) { + //qDebug() << "pi empty, remove " << itemCWidget(pi)->full_name; + conf->removeEntry(itemCWidget(pi)->full_name, false); + } + //qDebug() << "add " << fp + new_dialog.name(); + e = &(conf->addEntry(fp + new_dialog.name(), new_dialog.value().isEmpty() ? "0" : new_dialog.value(), new_dialog.type(), false)); + expandItem(pi); + pi = addEntry(pi, e); + pi->setText(0, new_dialog.name()); + pi->setText(3, new_dialog.comment()); + int ind = s_types.indexOf(types[new_dialog.type()]); + if (ind < 0) w_types.back()->setCurrentIndex(0); + else w_types.back()->setCurrentIndex(ind); + emit changed(); +} + + +void QPIConfigWidget::on_actionAddNode_triggered() { + if (conf == 0 || !active) return; + QString fp; + if (pi == 0) pi = invisibleRootItem(); + else fp = itemCWidget(pi)->full_name + conf->delim; + new_dialog.reset(true); + if (new_dialog.exec() == QDialog::Rejected) return; + QPIConfig::Entry e; + //e = &(conf->addEntry(fp + new_dialog.name(), "", "", false)); + e._full_name = fp + new_dialog.name(); + expandItem(pi); + pi = addEntry(pi, &e, true); + pi->setText(0, new_dialog.name()); + pi->setText(3, new_dialog.comment()); + setItemWidget(pi, 2, 0); + emit changed(); +} + + +void QPIConfigWidget::on_actionRemove_triggered() { + //hide(); + if (conf == 0 || !active) return; + QList si = selectedItems(); + conf->buildFullNames(&(conf->root)); + QPIConfig::Entry * e; + foreach (QTreeWidgetItem * i, si) { + e = itemEntry(i); + if (e == 0) continue; + //qDebug() << "remove " + e->_full_name; + conf->removeEntry(e->_full_name, false); + deleteEntry(i); + } + emit changed(); + //show(); +} + + +void QPIConfigWidget::clear() { + if (!active) return; + bool hidden = isHidden(); + hide(); + QTreeWidget::clear(); + foreach (ConfigValueWidget * i, w_values) + delete i; + foreach (QComboBox * i, w_types) + delete i; + w_values.clear(); + w_types.clear(); + if (!hidden) show(); +} + + +void QPIConfigWidget::buildTree() { + if (!active) return; + if (conf == 0) return; + bool hidden = isHidden(); + hide(); + clear(); + conf->buildFullNames(&(conf->root)); + buildEntry(invisibleRootItem(), &conf->rootEntry()); + if (!hidden) show(); +} + + +void QPIConfigWidget::setReadOnlyValue(bool yes) { + read_only_value = yes; + foreach (ConfigValueWidget * i, w_values) + i->setEnabled(!yes); +} + + +void QPIConfigWidget::setReadOnlyType(bool yes) { + read_only_type = yes; + foreach (QComboBox * i, w_types) { + i->setEnabled(!yes); + i->setFrame(!yes); + } +} + + +void QPIConfigWidget::buildEntry(QTreeWidgetItem * i, QPIConfig::Entry * e) { + foreach (QPIConfig::Entry * j, e->children()) + buildEntry(addEntry(i, j, !j->isLeaf()), j); +} + + +void QPIConfigWidget::buildFullNames(QTreeWidgetItem * i) { + ConfigValueWidget * cw, * pw; + cw = itemCWidget(i); + if (i->parent() != 0) { + pw = itemCWidget(i->parent()); + cw->full_name = pw->full_name + conf->delim + i->text(0); + } else cw->full_name = i->text(0); + for (int j = 0; j < i->childCount(); ++j) + buildFullNames(i->child(j)); +} + + +QPIConfig::Entry * QPIConfigWidget::itemEntry(QTreeWidgetItem * i) { + ConfigValueWidget * cfw = itemCWidget(i); + if (cfw == 0) return 0; + return &(conf->getValue(cfw->full_name)); +} + + +QTreeWidgetItem * QPIConfigWidget::addEntry(QTreeWidgetItem * i, QPIConfig::Entry * e, bool node) { + if (conf == 0) return 0; + ti = new QTreeWidgetItem(); + ti->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ti->setSizeHint(0, QSize(26, 26)); + ti->setText(0, e->name()); + ti->setText(3, e->comment()); + w_values.push_back(new ConfigValueWidget); + w_values.back()->setEntry(e); + w_values.back()->setEnabled(!read_only_value); + if (!node) { + w_types.push_back(new UComboBox()); + w_types.back()->addItems(s_types); + w_types.back()->setCurrentIndex(s_types.indexOf(types[e->type().leftJustified(1).left(1)])); + w_types.back()->setProperty("qpic_widget", QVariant((qlonglong)w_values.back())); + w_types.back()->setEnabled(!read_only_type); + w_types.back()->setFrame(!read_only_type); + connect(w_types.back(), SIGNAL(currentIndexChanged(int, UComboBox * )), this, SLOT(typeChange(int,UComboBox * ))); + } + connect(w_values.back(), SIGNAL(changed(ConfigValueWidget * , QString)), this, SLOT(valueChange(ConfigValueWidget * , QString))); + i->addChild(ti); + setItemWidget(ti, 1, w_values.back()); + if (!node) { + setItemWidget(ti, 2, w_types.back()); + if (itemTWidget(i) != 0) { //itemTWidget(i)->setEnabled(false); + w_types.remove(w_types.indexOf(itemTWidget(i))); + setItemWidget(i, 2, 0); + } + } + return ti; +} + + +void QPIConfigWidget::deleteEntry(QTreeWidgetItem * i) { + ConfigValueWidget * vw; + UComboBox * cb; + int cc = i->childCount(); + for (int j = 0; j < cc; ++j) + deleteEntry(i->child(0)); + vw = qobject_cast(itemWidget(i, 1)); + cb = qobject_cast(itemWidget(i, 2)); + if (vw != 0) { + w_values.remove(w_values.indexOf(vw)); + delete vw; + } + if (cb != 0) { + w_types.remove(w_types.indexOf(cb)); + delete cb; + } + delete i; +} + + +bool QPIConfigWidget::filter(const QString & f, QTreeWidgetItem * i) { + if (i->childCount() == 0) return filterItem(f, i); + bool found = false; + for (int j = 0; j < i->childCount(); ++j) + if (filter(f, i->child(j))) found = true; + i->setHidden(!found); + return found; +} + + +bool QPIConfigWidget::filterItem(const QString & f, QTreeWidgetItem * i) { + if (f.isEmpty()) { + i->setHidden(false); + return true; + } + bool ret = (!isColumnHidden(0) && i->text(0).indexOf(f, 0, Qt::CaseInsensitive) >= 0) || + (!isColumnHidden(1) && itemCWidget(i)->value.indexOf(f, 0, Qt::CaseInsensitive) >= 0) || + (!isColumnHidden(3) && i->text(3).indexOf(f, 0, Qt::CaseInsensitive) >= 0); + if (itemTWidget(i) != 0) + ret = ret || (!isColumnHidden(2) && itemTWidget(i)->currentText().indexOf(f, 0, Qt::CaseInsensitive) >= 0); + i->setHidden(!ret); + return ret; +} + + +void QPIConfigWidget::translate() { + QStringList l; + l << tr("Name") << tr("Value") << tr("Type") << tr("Comment"); + if (active) setHeaderLabels(l); + types.clear(); + s_types.clear(); + addTrEntry("s", tr("string")); + addTrEntry("l", tr("string list")); + addTrEntry("n", tr("integer")); + addTrEntry("f", tr("float")); + addTrEntry("b", tr("boolean")); + addTrEntry("c", tr("color")); + addTrEntry("r", tr("rectangle")); + addTrEntry("a", tr("area")); + addTrEntry("p", tr("point")); + addTrEntry("v", tr("vector")); + addTrEntry("i", tr("ip")); + actionAddItem.setText(tr("Add item ...")); + actionAddNode.setText(tr("Add node ...")); + actionToItem.setText(tr("Convert to item")); + actionToNode.setText(tr("Convert to node")); + actionRemove.setText(tr("Remove")); + actionExpandAll.setText(tr("Expand all")); + actionCollapseAll.setText(tr("Collapse all")); + if (!active) return; + for (int i = 0; i < 4; ++i) + setColumnHidden(i, c_hidden[i]); +} + + +QString QPIConfigWidget::writeToString() { + if (conf == 0) return QString(); + conf->buildFullNames(&(conf->root)); + return conf->writeAllToString(); +} + + +void QPIConfigWidget::readFromString(QString str) { + if (conf == 0) return; + conf->readAllFromString(str); + buildTree(); +} diff --git a/test/qad/widgets/qpiconfigwidget.h b/test/qad/widgets/qpiconfigwidget.h new file mode 100644 index 0000000..130e13a --- /dev/null +++ b/test/qad/widgets/qpiconfigwidget.h @@ -0,0 +1,111 @@ +#ifndef QPICONFIGWIDGET_H +#define QPICONFIGWIDGET_H + +#include "qpiconfig.h" +#include "qpiconfignewdialog.h" +#include "qpiconfigvaluewidget.h" +#include +#include +#include +#include +#include +#include + + +class UComboBox: public QComboBox +{ + Q_OBJECT +public: + UComboBox(QWidget * parent = 0): QComboBox(parent) {connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(indexChange(int)));} +private slots: + void indexChange(int i) {emit currentIndexChanged(i, this);} +signals: + void currentIndexChanged(int, UComboBox * ); +}; + + +class QPIConfigWidget: public QTreeWidget +{ + Q_OBJECT + Q_PROPERTY(bool readOnlyName READ readOnlyName WRITE setReadOnlyName) + Q_PROPERTY(bool readOnlyValue READ readOnlyValue WRITE setReadOnlyValue) + Q_PROPERTY(bool readOnlyType READ readOnlyType WRITE setReadOnlyType) + Q_PROPERTY(bool readOnlyComment READ readOnlyComment WRITE setReadOnlyComment) + Q_PROPERTY(bool columnNameVisible READ columnNameVisible WRITE setColumnNameVisible) + Q_PROPERTY(bool columnValueVisible READ columnValueVisible WRITE setColumnValueVisible) + Q_PROPERTY(bool columnTypeVisible READ columnTypeVisible WRITE setColumnTypeVisible) + Q_PROPERTY(bool columnCommentVisible READ columnCommentVisible WRITE setColumnCommentVisible) +public: + QPIConfigWidget(QWidget * parent = 0, QPIConfig * c = 0, bool on = true); + ~QPIConfigWidget() {clear();} + + void setQPIConfig(QPIConfig * c) {conf = c; buildTree();} + bool readOnlyName() {return read_only_name;} + bool readOnlyValue() {return read_only_value;} + bool readOnlyType() {return read_only_type;} + bool readOnlyComment() {return read_only_comment;} + bool columnNameVisible() {return !c_hidden[0];} + bool columnValueVisible() {return !c_hidden[1];} + bool columnTypeVisible() {return !c_hidden[2];} + bool columnCommentVisible() {return !c_hidden[3];} + QString writeToString(); + void readFromString(QString str); + +private: + void changeEvent(QEvent * e); + bool eventFilter(QObject * o, QEvent * e); + void buildEntry(QTreeWidgetItem * i, QPIConfig::Entry * e); + void buildFullNames(QTreeWidgetItem * i); + QPIConfig::Entry * itemEntry(QTreeWidgetItem * i); + ConfigValueWidget * itemCWidget(QTreeWidgetItem * i) {return qobject_cast(itemWidget(i, 1));} + UComboBox * itemTWidget(QTreeWidgetItem * i) {return qobject_cast(itemWidget(i, 2));} + QTreeWidgetItem * addEntry(QTreeWidgetItem * i, QPIConfig::Entry * e, bool node = false); + void deleteEntry(QTreeWidgetItem * i); + bool filter(const QString & f, QTreeWidgetItem * i); + bool filterItem(const QString & f, QTreeWidgetItem * i); + void translate(); + void addTrEntry(const QString & s, const QString & f) {types.insert(s, f); s_types << f;} + + QPIConfig * conf; + QPIConfigNewDialog new_dialog; + QAction actionAddItem, actionAddNode, actionToItem, actionToNode, actionRemove, actionExpandAll, actionCollapseAll; + QMenu popupMenu; + QString c_name, c_comment; + QTreeWidgetItem * pi, * ti, * c_pi; + QHash types; + QStringList s_types; + QVector w_values; + QVector w_types; + QVector c_hidden; + bool active, read_only_name, read_only_value, read_only_type, read_only_comment; + +public slots: + void parse() {if (conf == 0) clear(); else conf->readAll();} + void write() {if (conf == 0) return; conf->buildFullNames(&(conf->root)); conf->writeAll();} + void clear(); + void buildTree(); + void filter(const QString & f) {if (!active) return; filter(f, invisibleRootItem());} + void setReadOnlyName(bool yes) {read_only_name = yes;} + void setReadOnlyValue(bool yes); + void setReadOnlyType(bool yes); + void setReadOnlyComment(bool yes) {read_only_comment = yes;} + void setColumnNameVisible(bool yes) {setColumnHidden(0, !yes); c_hidden[0] = !yes;} + void setColumnValueVisible(bool yes) {setColumnHidden(1, !yes); c_hidden[1] = !yes;} + void setColumnTypeVisible(bool yes) {setColumnHidden(2, !yes); c_hidden[2] = !yes;} + void setColumnCommentVisible(bool yes) {setColumnHidden(3, !yes); c_hidden[3] = !yes;} + +private slots: + void itemClicked(QTreeWidgetItem * item, int column); + void itemChanged(QTreeWidgetItem * item, int column); + void typeChange(int t, UComboBox * c); + void valueChange(ConfigValueWidget * w, QString v); + void on_actionAddItem_triggered(); + void on_actionAddNode_triggered(); + void on_actionRemove_triggered(); + +signals: + void changed(); + +}; + +#endif // QPICONFIGWIDGET_H diff --git a/test/qad/widgets/qpiconsole.cpp b/test/qad/widgets/qpiconsole.cpp new file mode 100644 index 0000000..1b0d555 --- /dev/null +++ b/test/qad/widgets/qpiconsole.cpp @@ -0,0 +1,268 @@ +#include "qpiconsole.h" + + +QPIConsole::QPIConsole(QWidget * parent): QTabWidget(parent) { + connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); + cur_tab = timer = -1; + def_align = Qt::AlignCenter; +} + + +void QPIConsole::timerEvent(QTimerEvent * ) { + for (int i = 0; i < columns().size(); ++i) { + Column & ccol(tabs[cur_tab].columns[i]); + QVector & cvars(ccol.variables); + if (ccol.alignment.testFlag(Qt::AlignLeft)) { + ccol.layout->setLabelAlignment(Qt::AlignLeft); + } else { + ccol.layout->setLabelAlignment(Qt::AlignRight); + } + for (int j = 0; j < cvars.size(); ++j) { + Variable & tv(cvars[j]); + tv.label->setText(tv.name); + if (tv.type >= 0) { + if (ccol.alignment.testFlag(Qt::AlignLeft)) { + tv.label->setAlignment(Qt::AlignLeft); + if (tv.widget != 0) tv.widget->setAlignment(Qt::AlignLeft); + } else { + if (ccol.alignment.testFlag(Qt::AlignRight)) { + tv.label->setAlignment(Qt::AlignRight); + if (tv.widget != 0) tv.widget->setAlignment(Qt::AlignRight); + } else { + tv.label->setAlignment(Qt::AlignRight); + if (tv.widget != 0) tv.widget->setAlignment(Qt::AlignLeft); + } + } + } + if (tv.widget == 0) continue; + if (tv.type <= 0 && tv.ptr == 0) continue; + if (tv.ptr != 0) { + switch (tv.type) { + case 0: tv.widget->setText((tv.ptr != 0 ? *(const QString*)tv.ptr : QString())); break; + case 1: tv.widget->setText((tv.ptr != 0 ? *(const bool*)tv.ptr : false) ? "true" : "false"); break; + case 2: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const int*)tv.ptr : 0, tv.format)); break; + case 3: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const long*)tv.ptr : 0l, tv.format)); break; + case 4: tv.widget->setText(QString(tv.ptr != 0 ? *(const char*)tv.ptr : char(0))); break; + case 5: tv.widget->setText(numFloatString(tv.ptr != 0 ? *(const float*)tv.ptr : 0.f, tv.format)); break; + case 6: tv.widget->setText(numFloatString(tv.ptr != 0 ? *(const double*)tv.ptr : 0., tv.format)); break; + case 7: tv.widget->setText(numFloatString(tv.ptr != 0 ? *(const short*)tv.ptr : short(0), tv.format)); break; + case 8: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const uint*)tv.ptr : 0u, tv.format)); break; + case 9: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const ulong*)tv.ptr : 0ul, tv.format)); break; + case 10: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const ushort*)tv.ptr : ushort(0), tv.format)); break; + case 11: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const uchar*)tv.ptr : uchar(0), tv.format)); break; + case 12: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const llong*)tv.ptr : 0l, tv.format)); break; + case 13: tv.widget->setText(numIntString(tv.ptr != 0 ? *(const ullong*)tv.ptr: 0ull, tv.format)); break; + case 14: tv.widget->setText(numIntString(bitsValue(tv.ptr, tv.bitFrom, tv.bitCount), tv.format, tv.bitCount/8)); break; + default: break; + } + } + } + } +} + + +#define ADD_VAR_BODY tv.name = name; if (!tv.name.isEmpty()) tv.name += ":"; tv.bitFrom = tv.bitCount = 0; tv.format = format; checkColumn(col); +#define ADD_VAR_SBODY tv.name = name; tv.bitFrom = tv.bitCount = 0; tv.format = format; checkColumn(col); + +#define ADD_VAR_QT QLabel * lbl = new QLabel(name); QLabel * w = new QLabel(); \ + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); \ + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); \ + column(col).variables.back().label = lbl; column(col).variables.back().widget = w; \ + lbl->setAlignment(column(col).alignment); \ + applyFormat(lbl, format); applyFormat(w, format); \ + column(col).layout->addRow(lbl, w); + +void QPIConsole::addString(const QString & name, int col, Formats format) { + ADD_VAR_SBODY tv.type = -1; tv.ptr = 0; column(col).push_back(tv); + QLabel * lbl = new QLabel(name); + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + column(col).variables.back().label = lbl; column(col).variables.back().widget = 0; + lbl->setAlignment(column(col).alignment); + applyFormat(lbl, format); + column(col).layout->addRow(lbl); +} +void QPIConsole::addVariable(const QString & name, const QString* ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 0; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const bool * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 1; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const int * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 2; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const long * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 3; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const char * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 4; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const float * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 5; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const double * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 6; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const short * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 7; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const uint * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 8; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const ulong * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 9; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const ushort * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 10; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const uchar * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 11; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const llong * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 12; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addVariable(const QString & name, const ullong * ptr, int col, Formats format) { + ADD_VAR_BODY tv.type = 13; tv.ptr = ptr; column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addBitVariable(const QString & name, const void * ptr, int fromBit, int bitCount, int col, Formats format) { + tv.name = name; tv.bitFrom = fromBit; tv.bitCount = bitCount; tv.type = 14; tv.ptr = ptr; tv.format = format; + checkColumn(col); column(col).push_back(tv); ADD_VAR_QT} +void QPIConsole::addEmptyLine(int col, uint count) { + tv.name = ""; tv.type = 0; tv.ptr = 0; tv.format = Normal; + for (uint i = 0; i < count; ++i) { + checkColumn(col); + column(col).push_back(tv); + QLabel * lbl = new QLabel(); + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + column(col).variables.back().label = lbl; column(col).variables.back().widget = 0; + lbl->setAlignment(column(col).alignment); + applyFormat(lbl, tv.format); + column(col).layout->addRow(lbl); + } +} + + +int QPIConsole::addTab(const QString & name, char bind_key) { + QWidget * w = new QWidget(); + QVBoxLayout * lay = new QVBoxLayout(); + QHBoxLayout * clay = new QHBoxLayout(); + QLabel * lbl = new QLabel(); + lay->setContentsMargins(2, 2, 2, 2); + clay->setContentsMargins(0, 0, 0, 0); + lay->addLayout(clay); + lay->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding)); + lay->addWidget(lbl); + w->setLayout(lay); + QTabWidget::addTab(w, name); + tabs.push_back(Tab(name, bind_key)); + tabs.back().layout = clay; + tabs.back().widget = lbl; + cur_tab = tabs.size() - 1; + setCurrentIndex(cur_tab); + return tabs.size(); +} + + +void QPIConsole::checkColumn(int col) { + while (columns().size() < col) { + QFormLayout * lay = new QFormLayout(); + lay->setContentsMargins(2, 2, 2, 2); + lay->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); + columns().push_back(Column(def_align)); + columns().back().layout = lay; + tabs[cur_tab].layout->addLayout(lay); + } +} + + +int QPIConsole::bitsValue(const void * src, int offset, int count) const { + int ret = 0, stbyte = offset / 8, cbit = offset - stbyte * 8; + char cbyte = reinterpret_cast(src)[stbyte]; + for (int i = 0; i < count; i++) { + ret |= ((cbyte >> cbit & 1) << i); + cbit++; + if (cbit == 8) { + cbit = 0; + stbyte++; + cbyte = reinterpret_cast(src)[stbyte]; + } + } + return ret; +} + + +const QString & QPIConsole::toBin(const void * d, int s) { + binstr.clear(); + uchar cc, b; + for (int i = 0; i < s; ++i) { + cc = ((const uchar *)d)[i]; + b = 1; + for (int j = 0; j < 8; ++j) { + binstr.prepend(cc & b ? "1" : "0"); + b <<= 1; + } + if (i < s - 1) binstr.prepend(" "); + } + return binstr; +} + + +void QPIConsole::applyFormat(QLabel * l, QPIConsole::Formats f) { + QColor fcol = Qt::black, bcol = QColor(0xFFFFFF); + QFont fnt = font(); + + if (f.testFlag(QPIConsole::Bold)) fnt.setBold(true); + if (f.testFlag(QPIConsole::Italic)) fnt.setItalic(true); + if (f.testFlag(QPIConsole::Underline)) fnt.setUnderline(true); + + if (f.testFlag(QPIConsole::Black)) fcol = Qt::black; + if (f.testFlag(QPIConsole::Red)) fcol = Qt::red; + if (f.testFlag(QPIConsole::Green)) fcol = Qt::green; + if (f.testFlag(QPIConsole::Yellow)) fcol = Qt::yellow; + if (f.testFlag(QPIConsole::Blue)) fcol = Qt::blue; + if (f.testFlag(QPIConsole::Magenta)) fcol = Qt::magenta; + if (f.testFlag(QPIConsole::Cyan)) fcol = Qt::cyan; + if (f.testFlag(QPIConsole::White)) fcol = Qt::white; + if (f.testFlag(QPIConsole::Lighter)) fcol = fcol.lighter(150); + if (f.testFlag(QPIConsole::Darker)) fcol = fcol.darker(150); + + if (f.testFlag(QPIConsole::BackBlack)) bcol = Qt::black; + if (f.testFlag(QPIConsole::BackRed)) bcol = Qt::red; + if (f.testFlag(QPIConsole::BackGreen)) bcol = Qt::green; + if (f.testFlag(QPIConsole::BackYellow)) bcol = Qt::yellow; + if (f.testFlag(QPIConsole::BackBlue)) bcol = Qt::blue; + if (f.testFlag(QPIConsole::BackMagenta)) bcol = Qt::magenta; + if (f.testFlag(QPIConsole::BackCyan)) bcol = Qt::cyan; + if (f.testFlag(QPIConsole::BackWhite)) bcol = Qt::white; + if (f.testFlag(QPIConsole::BackLighter)) bcol = bcol.lighter(150); + //if (f.testFlag(QPIConsole::BackDarker)) bcol = bcol.darker(150); + + if (f.testFlag(QPIConsole::Inverse)) { + QColor tc = fcol; + fcol = bcol; + bcol = tc; + } + + l->setFont(fnt); + QPalette pal = palette(); + pal.setColor(QPalette::WindowText, fcol); + pal.setColor(QPalette::Window, bcol); + l->setPalette(pal); + l->setAutoFillBackground(bcol != QColor(0xFFFFFF)); +} + + +bool QPIConsole::removeTab(uint index) { + if (int(index) >= tabs.size()) return false; + delete QTabWidget::widget(index); + tabs.remove(index); + return true; +} + + +bool QPIConsole::setTab(uint index) { + if (int(index) >= tabs.size()) return false; + setCurrentIndex(index); + return true; +} + + +bool QPIConsole::renameTab(uint index, const QString & new_name) { + if (int(index) >= tabs.size()) return false; + if (tabs[index].name == new_name) return true; + setTabText(index, new_name); + tabs[index].name = new_name; + return true; +} + + +void QPIConsole::setTabEnabled(int index, bool on) { + if (int(index) >= tabs.size()) return; + if (isTabEnabled(index) == on) return; + QTabWidget::setTabEnabled(index, on); +} diff --git a/test/qad/widgets/qpiconsole.h b/test/qad/widgets/qpiconsole.h new file mode 100644 index 0000000..5e72d33 --- /dev/null +++ b/test/qad/widgets/qpiconsole.h @@ -0,0 +1,187 @@ +#ifndef QPICONSOLE_H +#define QPICONSOLE_H + +#include +#include +#include +#include +#include +#include + +typedef long long llong; +typedef unsigned char uchar; +typedef unsigned short int ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long ullong; +typedef long double ldouble; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QPIConsole: public QTabWidget { + Q_OBJECT +public: + QPIConsole(QWidget * parent = 0); + + enum Format {Normal = 0x01, + Bold = 0x02, + Italic = 0x08, + Underline = 0x10, + Inverse = 0x40, + Black = 0x100, + Red = 0x200, + Green = 0x400, + Yellow = 0x800, + Blue = 0x1000, + Magenta = 0x2000, + Cyan = 0x4000, + White = 0x8000, + BackBlack = 0x10000, + BackRed = 0x20000, + BackGreen = 0x40000, + BackYellow = 0x80000, + BackBlue = 0x100000, + BackMagenta = 0x200000, + BackCyan = 0x400000, + BackWhite = 0x800000, + Dec = 0x1000000, + Hex = 0x2000000, + Oct = 0x4000000, + Bin = 0x8000000, + Scientific = 0x10000000, + Lighter = 0x20000000, + Darker = 0x40000000, + BackLighter = 0x80000000 + }; + + Q_DECLARE_FLAGS(Formats, Format) + Q_FLAGS(Formats) + Q_ENUMS(Format) + + Q_PROPERTY(Qt::Alignment defaultAlignment READ defaultAlignment WRITE setDefaultAlignment) + + void addString(const QString & name, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const QString * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const char * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const bool * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const short * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const int * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const long * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const llong * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const uchar * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const ushort * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const uint * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const ulong * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const ullong * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const float * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addVariable(const QString & name, const double * ptr, int column = 1, Formats format = QPIConsole::Normal); + void addBitVariable(const QString & name, const void * ptr, int fromBit, int bitCount, int column = 1, Formats format = QPIConsole::Normal); + void addEmptyLine(int column = 1, uint count = 1); + + uint tabsCount() const {return tabs.size();} + QString currentTab() const {return tabs[cur_tab].name;} + int addTab(const QString & name, char bind_key = 0); + bool removeTab(uint index); + bool removeTab(const QString & name) {return removeTab(tabIndex(name));} + bool renameTab(uint index, const QString & new_name); + bool renameTab(const QString & name, const QString & new_name) {return renameTab(tabIndex(name), new_name);} + void setTabEnabled(int index, bool on); + bool setTab(uint index); + bool setTab(const QString & name) {return setTab(tabIndex(name));} + //void clearTabs(bool clearScreen = true) {if (clearScreen && isRunning()) {toUpperLeft(); clearScreenLower();} tabs.clear();} + + void addCustomStatus(const QString & str, Formats format = QPIConsole::Normal) {tabs[cur_tab].widget->setText(str); applyFormat(tabs[cur_tab].widget, format);} + void clearCustomStatus() {tabs[cur_tab].widget->clear();} + Qt::Alignment defaultAlignment() const {return def_align;} + void setDefaultAlignment(Qt::Alignment align) {def_align = align;} + void setColumnAlignment(int col, Qt::Alignment align) {if (col < 0 || col >= columns().size()) return; column(col).alignment = align;} + void setColumnAlignmentToAll(Qt::Alignment align) {for (int i = 0; i < tabs.size(); ++i) for (int j = 0; j < tabs[i].columns.size(); ++j) tabs[i].columns[j].alignment = align;/* fillLabels();*/} + + void clearVariables() {clearVariables(true);} + void clearVariables(bool clearScreen) {/*if (clearScreen && isRunning()) {toUpperLeft(); clearScreenLower();}*/ columns().clear();} + +private: + void timerEvent(QTimerEvent * ); + QSize sizeHint() const {return QSize(100, 100);} + + void checkColumn(int col); + void applyFormat(QLabel * l, Formats f); + int bitsValue(const void * src, int offset, int count) const; + int tabIndex(const QString & n) const {for (int i = 0; i < tabs.size(); ++i) if (tabs[i].name == n) return i; return -1;} + const QString & toBin(const void * d, int s); + template + QString numIntString(T v, Formats f, int bits = 0) { + if (f.testFlag(QPIConsole::Hex)) return "0x" + QString::number(v, 16).toUpper(); + if (f.testFlag(QPIConsole::Dec)) return QString::number(v); + if (f.testFlag(QPIConsole::Oct)) return "0" + QString::number(v, 8); + if (f.testFlag(QPIConsole::Bin)) return toBin(&v, bits); + return QString::number(v); + } + template + QString numFloatString(T v, Formats f) { + if (f.testFlag(QPIConsole::Scientific)) return QString::number(v, 'E', 5); + return QString::number(v); + } + + struct Variable { + Variable() {label = widget = 0;} + QString name; + Formats format; + int type; + int bitFrom; + int bitCount; + const void * ptr; + QLabel * label; + QLabel * widget; + void operator =(const Variable & src) {name = src.name; format = src.format; type = src.type; + bitFrom = src.bitFrom; bitCount = src.bitCount; ptr = src.ptr;} + }; + + struct Column { + Column(Qt::Alignment align = Qt::AlignRight) {variables.reserve(16); alignment = align;} + QVector variables; + Qt::Alignment alignment; + QFormLayout * layout; + int size() const {return variables.size();} + Variable & operator [](int index) {return variables[index];} + const Variable & operator [](int index) const {return variables[index];} + void push_back(const Variable & v) {variables.push_back(v);} + void operator =(const Column & src) {variables = src.variables; alignment = src.alignment;} + }; + + struct Tab { + Tab(QString n = "", char k = 0) {columns.reserve(16); name = n; key = k;} + QVector columns; + QString name; + char key; + QHBoxLayout * layout; + QLabel * widget; + }; + + QVector & columns() {return tabs[cur_tab].columns;} + Column & column(int index) {return tabs[cur_tab].columns[index - 1];} + + Qt::Alignment def_align; + QVector tabs; + QString binstr; + Variable tv; + int cur_tab, timer; + +private slots: + void tabChanged(int tab) {cur_tab = tab;} + +public slots: + void start(float freq = 40) {if (timer >= 0) killTimer(timer); timer = startTimer(freq > 0. ? 1000 / freq : 25);} + void stop() {if (timer >= 0) killTimer(timer); timer = -1;} + +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QPIConsole::Formats) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPICONSOLE_H diff --git a/test/qad/widgets/qpointedit.cpp b/test/qad/widgets/qpointedit.cpp new file mode 100644 index 0000000..ebd0aae --- /dev/null +++ b/test/qad/widgets/qpointedit.cpp @@ -0,0 +1,34 @@ +#include "qpointedit.h" +#include "float.h" + + +QPointEdit::QPointEdit(QWidget * parent): QWidget(parent), lay(QBoxLayout::LeftToRight, this) { + s_x = new QDoubleSpinBox(this); + s_y = new QDoubleSpinBox(this); + s_x->setMinimum(-DBL_MAX); + s_x->setMaximum(DBL_MAX); + s_y->setMinimum(-DBL_MAX); + s_y->setMaximum(DBL_MAX); + s_x->setToolTip(tr("X")); + s_y->setToolTip(tr("Y")); + lbl = new QLabel(this); + lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + lbl->setText("x"); + lay.setContentsMargins(0, 0, 0, 0); + lay.setSpacing(1); + lay.addWidget(s_x); + lay.addWidget(lbl); + lay.addWidget(s_y); + connect(s_x, SIGNAL(valueChanged(double)), this, SLOT(changed())); + connect(s_y, SIGNAL(valueChanged(double)), this, SLOT(changed())); +} + + +void QPointEdit::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + s_x->setToolTip(tr("X")); + s_y->setToolTip(tr("Y")); + return; + } + QWidget::changeEvent(e); +} diff --git a/test/qad/widgets/qpointedit.h b/test/qad/widgets/qpointedit.h new file mode 100644 index 0000000..15119c7 --- /dev/null +++ b/test/qad/widgets/qpointedit.h @@ -0,0 +1,42 @@ +#ifndef QPOINTEDIT_H +#define QPOINTEDIT_H + +#include +#include +#include +#include + + +class QPointEdit: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QPointF value READ value WRITE setValue) + Q_PROPERTY(int decimals READ decimals WRITE setDecimals) + +public: + explicit QPointEdit(QWidget * parent = 0); + ~QPointEdit() {delete s_x; delete s_y; delete lbl;} + + QPointF value() const {return QPointF(s_x->value(), s_y->value());} + int decimals() const {return s_x->decimals();} + +public slots: + void setValue(QPointF v) {s_x->setValue(v.x()); s_y->setValue(v.y());} + void setDecimals(int d) {s_x->setDecimals(d); s_y->setDecimals(d);} + +private: + virtual void changeEvent(QEvent * e); + + QBoxLayout lay; + QDoubleSpinBox * s_x, * s_y; + QLabel * lbl; + +private slots: + void changed() {emit valueChanged(QPointF(s_x->value(), s_y->value()));} + +signals: + void valueChanged(QPointF); + +}; + +#endif // QPOINTEDIT_H diff --git a/test/qad/widgets/qrectedit.cpp b/test/qad/widgets/qrectedit.cpp new file mode 100644 index 0000000..e1387a4 --- /dev/null +++ b/test/qad/widgets/qrectedit.cpp @@ -0,0 +1,56 @@ +#include "qrectedit.h" +#include "float.h" + + +QRectEdit::QRectEdit(QWidget * parent): QWidget(parent), lay(QBoxLayout::LeftToRight, this) { + s_x = new QDoubleSpinBox(this); + s_y = new QDoubleSpinBox(this); + s_w = new QDoubleSpinBox(this); + s_h = new QDoubleSpinBox(this); + s_x->setMinimum(-DBL_MAX); + s_x->setMaximum(DBL_MAX); + s_y->setMinimum(-DBL_MAX); + s_y->setMaximum(DBL_MAX); + s_w->setMinimum(-DBL_MAX); + s_w->setMaximum(DBL_MAX); + s_h->setMinimum(-DBL_MAX); + s_h->setMaximum(DBL_MAX); + s_x->setToolTip(tr("X")); + s_y->setToolTip(tr("Y")); + s_w->setToolTip(tr("Height")); + s_h->setToolTip(tr("Width")); + lbl_0 = new QLabel(this); + lbl_0->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + lbl_0->setText("x"); + lbl_1 = new QLabel(this); + lbl_1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + lbl_1->setText(";"); + lbl_2 = new QLabel(this); + lbl_2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + lbl_2->setText("x"); + lay.setContentsMargins(0, 0, 0, 0); + lay.setSpacing(1); + lay.addWidget(s_x); + lay.addWidget(lbl_0); + lay.addWidget(s_y); + lay.addWidget(lbl_1); + lay.addWidget(s_w); + lay.addWidget(lbl_2); + lay.addWidget(s_h); + connect(s_x, SIGNAL(valueChanged(double)), this, SLOT(changed())); + connect(s_y, SIGNAL(valueChanged(double)), this, SLOT(changed())); + connect(s_w, SIGNAL(valueChanged(double)), this, SLOT(changed())); + connect(s_h, SIGNAL(valueChanged(double)), this, SLOT(changed())); +} + + +void QRectEdit::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + s_x->setToolTip(tr("X")); + s_y->setToolTip(tr("Y")); + s_w->setToolTip(tr("Height")); + s_h->setToolTip(tr("Width")); + return; + } + QWidget::changeEvent(e); +} diff --git a/test/qad/widgets/qrectedit.h b/test/qad/widgets/qrectedit.h new file mode 100644 index 0000000..7e4064d --- /dev/null +++ b/test/qad/widgets/qrectedit.h @@ -0,0 +1,51 @@ +#ifndef QRECTEDIT_H +#define QRECTEDIT_H + +#include +#include +#include +#include + + +class QRectEdit: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QRectF value READ value WRITE setValue) + Q_PROPERTY(int decimals READ decimals WRITE setDecimals) + Q_PROPERTY(double maximum READ maximum WRITE setMaximum) + Q_PROPERTY(double minimum READ minimum WRITE setMinimum) + Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep) + +public: + explicit QRectEdit(QWidget * parent = 0); + ~QRectEdit() {delete s_x; delete s_y; delete s_w; delete s_h; delete lbl_0; delete lbl_1; delete lbl_2;} + + QRectF value() const {return QRectF(s_x->value(), s_y->value(), s_w->value(), s_h->value());} + int decimals() const {return s_x->decimals();} + double maximum() const {return s_x->maximum();} + double minimum() const {return s_x->minimum();} + double singleStep() const {return s_x->singleStep();} + +public slots: + void setValue(QRectF v) {s_x->setValue(v.x()); s_y->setValue(v.y()); s_w->setValue(v.width()); s_h->setValue(v.height());} + void setDecimals(int d) {s_x->setDecimals(d); s_y->setDecimals(d); s_w->setDecimals(d); s_h->setDecimals(d);} + void setMaximum(double m) {s_x->setMaximum(m); s_y->setMaximum(m); s_w->setMaximum(m); s_h->setMaximum(m);} + void setMinimum(double m) {s_x->setMinimum(m); s_y->setMinimum(m); s_w->setMinimum(m); s_h->setMinimum(m);} + void setSingleStep(double m) {s_x->setSingleStep(m); s_y->setSingleStep(m); s_w->setSingleStep(m); s_h->setSingleStep(m);} + +private: + virtual void changeEvent(QEvent * e); + + QBoxLayout lay; + QDoubleSpinBox * s_x, * s_y, * s_w, * s_h; + QLabel * lbl_0, * lbl_1, * lbl_2; + +private slots: + void changed() {emit valueChanged(QRectF(s_x->value(), s_y->value(), s_w->value(), s_h->value()));} + +signals: + void valueChanged(QRectF); + +}; + +#endif // QRECTEDIT_H diff --git a/test/qad/widgets/qvariantedit.cpp b/test/qad/widgets/qvariantedit.cpp new file mode 100644 index 0000000..64131e4 --- /dev/null +++ b/test/qad/widgets/qvariantedit.cpp @@ -0,0 +1,490 @@ +#include "qvariantedit.h" +#include +#include + + +StringListEdit::~StringListEdit() { + delete combo; + delete butt_add; + delete butt_del; + delete butt_clear; +} + + +QStringList StringListEdit::value() const { + QStringList l; + for (int i = 0; i < combo->count(); ++i) l << combo->itemText(i); + return l; +} + + +void StringListEdit::setValue(const QStringList & v) { + combo->clear(); + if (!v.isEmpty()) { + combo->addItems(v); + combo->setCurrentIndex(0); + } +} + + +void StringListEdit::addItem() { + combo->addItem(combo->currentText()); + emit valueChanged(); +} + + +void StringListEdit::delItem() { + if (combo->currentIndex() < 0) return; + combo->removeItem(combo->currentIndex()); + emit valueChanged(); +} + + +void StringListEdit::clear() { + if (combo->count() == 0) return; + combo->clear(); + emit valueChanged(); +} + + +StringListEdit::StringListEdit(QWidget * parent): QWidget(parent), lay(QBoxLayout::LeftToRight, this) { + combo = new EComboBox(this); + combo->setEditable(true); + combo->setLineEdit(new CLineEdit); + combo->setInsertPolicy(QComboBox::NoInsert); + butt_add = new QPushButton(this); + butt_del = new QPushButton(this); + butt_clear = new QPushButton(this); + /*butt_add->setIconSize(QSize(16, 16)); + butt_del->setIconSize(QSize(16, 16)); + butt_clear->setIconSize(QSize(16, 16));*/ + butt_add->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + butt_del->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + butt_clear->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + butt_add->setIcon(QIcon(":/icons/list-add.png")); + butt_del->setIcon(QIcon(":/icons/edit-delete.png")); + butt_clear->setIcon(QIcon(":/icons/edit-clear.png")); + butt_add->setToolTip(tr("Add")); + butt_del->setToolTip(tr("Remove")); + butt_clear->setToolTip(tr("Clear")); + lay.setContentsMargins(0, 0, 0, 0); + lay.setSpacing(2); + lay.addWidget(combo); + lay.addWidget(butt_add); + lay.addWidget(butt_del); + lay.addWidget(butt_clear); + connect(combo->lineEdit(), SIGNAL(returnPressed()), this, SLOT(editItem())); + connect(butt_add, SIGNAL(clicked(bool)), this, SLOT(addItem())); + connect(butt_del, SIGNAL(clicked(bool)), this, SLOT(delItem())); + connect(butt_clear, SIGNAL(clicked(bool)), this, SLOT(clear())); +} + + +void StringListEdit::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + butt_add->setToolTip(tr("Add")); + butt_del->setToolTip(tr("Remove")); + butt_clear->setToolTip(tr("Clear")); + return; + } + QWidget::changeEvent(e); +} + + +void StringListEdit::editItem() { + int ci = combo->currentIndex(); + if (ci < 0) return; + combo->setItemText(ci, combo->currentText()); +} + + + + +PathEdit::PathEdit(QWidget * parent): QWidget(parent), lay(QBoxLayout::LeftToRight, this) { + is_dir = is_abs = false; + filter = tr("All files(*)"); + line = new CLineEdit(this); + //line->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + butt_select = new QPushButton(this); + //butt_select->setIconSize(QSize(16, 16)); + butt_select->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + butt_select->setIcon(QIcon(":/icons/document-open.png")); + butt_select->setToolTip(tr("Choose") + " ..."); + lay.setContentsMargins(0, 0, 0, 0); + //lay.setSpacing(2); + lay.addWidget(line); + lay.addWidget(butt_select); + connect(line, SIGNAL(textChanged(QString)), this, SIGNAL(valueChanged())); + connect(butt_select, SIGNAL(clicked(bool)), this, SLOT(select())); +} + + +void PathEdit::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + butt_select->setToolTip(tr("Choose") + " ..."); + return; + } + QWidget::changeEvent(e); +} + + +void PathEdit::resizeEvent(QResizeEvent *) { + butt_select->setMaximumHeight(line->height()); + +} + + +void PathEdit::select() { + QString ret; + if (is_dir) ret = QFileDialog::getExistingDirectory(this, tr("Select directory"), value()); + else ret = QFileDialog::getOpenFileName(this, tr("Select file"), value(), filter); + if (ret.isEmpty()) return; + if (!is_abs) + ret = QDir::current().relativeFilePath(ret); + line->setText(ret); + emit valueChanged(); +} + + + + +QVariantEdit::QVariantEdit(QWidget * parent): QWidget(parent) { + setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); + layout()->setContentsMargins(0, 0, 0, 0); + _empty = 0; + _line = 0; + _check = 0; + _color = 0; + _list = 0; + _date = 0; + _spin = 0; + _espin = 0; + _rect = 0; + _point = 0; + _path = 0; + _enum = 0; + _custom =_cur_edit = 0; + _recreate(QVariant()); +} + + +QVariantEdit::~QVariantEdit() { + _delete(); +} + + +void QVariantEdit::resizeEvent(QResizeEvent * e) { + //_resize(); + QWidget::resizeEvent(e); +} + + +void QVariantEdit::_recreate(const QVariant & new_value) { + if (!new_value.isValid()) { + if (_cur_edit != _empty) _delete(); + if (_empty == 0) { + _empty = new QLabel(trUtf8("Invalid value"), this); + _empty->setAlignment(Qt::AlignCenter); + _cur_edit = _empty; + _resize(); + } + _value = new_value; + return; + } + if (_value.userType() == new_value.userType()) { + _value = new_value; + return; + } + _delete(); + switch (new_value.type()) { + case QVariant::Bool: + _check = new QCheckBox(this); + _check->setAutoFillBackground(true); + _cur_edit = _check; + connect(_check, SIGNAL(toggled(bool)), this, SLOT(_changed())); + break; + case QVariant::Int: + _spin = new QDoubleSpinBox(this); + _spin->setDecimals(0); + _spin->setRange(-0x7FFFFFFF, 0x7FFFFFFF); + _cur_edit = _spin; + connect(_spin, SIGNAL(valueChanged(double)), this, SLOT(_changed())); + break; + case QVariant::UInt: + _spin = new QDoubleSpinBox(this); + _spin->setDecimals(0); + _spin->setRange(0, 0xFFFFFFFF); + _cur_edit = _spin; + connect(_spin, SIGNAL(valueChanged(double)), this, SLOT(_changed())); + break; + case QVariant::LongLong: + _spin = new QDoubleSpinBox(this); + _spin->setDecimals(0); + _spin->setRange(-0x7FFFFFFFFFFFFFFFL, 0x7FFFFFFFFFFFFFFFL); + _cur_edit = _spin; + connect(_spin, SIGNAL(valueChanged(double)), this, SLOT(_changed())); + break; + case QVariant::ULongLong: + _spin = new QDoubleSpinBox(this); + _spin->setDecimals(0); + _spin->setRange(0L, 0x7FFFFFFFFFFFFFFFL); + _cur_edit = _spin; + connect(_spin, SIGNAL(valueChanged(double)), this, SLOT(_changed())); + break; + case QVariant::Double: + _espin = new EvalSpinBox(this); + //_spin->setDecimals(5); + //_spin->setRange(-1E+199, 1E+199); + _cur_edit = _espin; + connect(_espin, SIGNAL(valueChanged(double)), this, SLOT(_changed())); + break; + case QVariant::Color: + _color = new ColorButton(this); + _color->setUseAlphaChannel(true); + _cur_edit = _color; + connect(_color, SIGNAL(colorChanged(QColor)), this, SLOT(_changed())); + break; + case QVariant::String: + _line = new CLineEdit(this); + _cur_edit = _line; + connect(_line, SIGNAL(textChanged(QString)), this, SLOT(_changed())); + break; + case QVariant::StringList: + _list = new StringListEdit(this); + _cur_edit = _list; + connect(_list, SIGNAL(valueChanged()), this, SLOT(_changed())); + break; + case QVariant::Rect: + _rect = new QRectEdit(this); + _rect->setDecimals(0); + _cur_edit = _rect; + connect(_rect, SIGNAL(valueChanged(QRectF)), this, SLOT(_changed())); + break; + case QVariant::RectF: + _rect = new QRectEdit(this); + _rect->setDecimals(3); + _cur_edit = _rect; + connect(_rect, SIGNAL(valueChanged(QRectF)), this, SLOT(_changed())); + break; + case QVariant::Point: + _point = new QPointEdit(this); + _point->setDecimals(0); + _cur_edit = _point; + connect(_point, SIGNAL(valueChanged(QPointF)), this, SLOT(_changed())); + break; + case QVariant::PointF: + _point = new QPointEdit(this); + _point->setDecimals(3); + _cur_edit = _point; + connect(_point, SIGNAL(valueChanged(QPointF)), this, SLOT(_changed())); + break; + case QVariant::Date: + _date = new QDateEdit(this); + _cur_edit = _date; + connect(_date, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(_changed())); + break; + case QVariant::Time: + _date = new QTimeEdit(this); + _cur_edit = _date; + connect(_date, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(_changed())); + break; + case QVariant::DateTime: + _date = new QDateTimeEdit(this); + _cur_edit = _date; + connect(_date, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(_changed())); + break; + default: break; + } + if (!_cur_edit) { + if (new_value.canConvert()) { + _enum = new EComboBox(this); + _setEnum(new_value.value()); + _cur_edit = _enum; + connect(_enum, SIGNAL(currentIndexChanged(int)), this, SLOT(_changed())); + } + if (new_value.canConvert()) { + _path = new PathEdit(this); + _setFile(new_value.value()); + _cur_edit = _path; + connect(_path, SIGNAL(valueChanged()), this, SLOT(_changed())); + } + if (new_value.canConvert()) { + _path = new PathEdit(this); + _setDir(new_value.value()); + _cur_edit = _path; + connect(_path, SIGNAL(valueChanged()), this, SLOT(_changed())); + } + if (!_cur_edit) { // try custom + QVariantEditorFactoryBase * f = QVariantEditorFactories::editorFactory(new_value.userType()); + if (f) { + QWidget * fw = f->createEditor(); + if (fw) { + fw->setParent(this); + _custom = fw; + _cur_edit = _custom; + connect(_custom, SIGNAL(valueChanged()), this, SLOT(_changed())); + } + } + } + } + //qDebug() << _cur_edit; + if (_cur_edit) { + _resize(); + //_cur_edit->show(); + } + _value = new_value; +} + + +QVariant QVariantEdit::value() const { + switch (_value.type()) { + case QVariant::Bool: return _check->isChecked(); + case QVariant::Int: return int(_spin->value()); + case QVariant::UInt: return (unsigned int)(_spin->value()); + case QVariant::LongLong: return qlonglong(_spin->value()); + case QVariant::ULongLong: return qulonglong(_spin->value()); + case QVariant::Double: return _espin->value(); + case QVariant::Color: return _color->color(); + case QVariant::String: return _line->text(); + case QVariant::StringList: return _list->value(); + case QVariant::Rect: return _rect->value().toRect(); + case QVariant::RectF: return _rect->value(); + case QVariant::Point: return _point->value().toPoint(); + case QVariant::PointF: return _point->value(); + case QVariant::Date: return _date->date(); + case QVariant::Time: return _date->time(); + case QVariant::DateTime: return _date->dateTime(); + default: + if (_value.canConvert() && _enum) { + QAD::Enum ret; + for (int i = 0; i < _enum->count(); ++i) + ret.enum_list << QAD::Enumerator(_enum->itemData(i).toInt(), _enum->itemText(i)); + ret.enum_name = _enum->property("enum_name").toString(); + ret.selected = _enum->currentText(); + return QVariant::fromValue(ret); + } + if (_value.canConvert() && _path) { + if (!_path->is_dir) { + QAD::File ret; + ret.file = _path->value(); + ret.filter = _path->filter; + ret.is_abs = _path->is_abs; + return QVariant::fromValue(ret); + } + } + if (_value.canConvert() && _path) { + if (_path->is_dir) { + QAD::Dir ret; + ret.dir = _path->value(); + ret.is_abs = _path->is_abs; + return QVariant::fromValue(ret); + } + } + if (_custom) { + return _custom->property("value"); + } + } + return QVariant(); +} + + +void QVariantEdit::setValue(const QVariant & v) { + _recreate(v); + if (_cur_edit) _cur_edit->blockSignals(true); + if (_line) {_line->setText(v.toString());} + if (_check) {_check->setChecked(v.toBool()); _check->setText(v.toBool() ? "true" : "false");} + if (_color) {_color->setColor(v.value());} + if (_list) {_list->setValue(v.toStringList());} + if (_date) {_date->setDateTime(v.toDateTime());} + if (_spin) {_spin->setValue(v.toDouble());} + if (_espin) {_espin->setValue(v.toDouble());} + if (_rect) {_rect->setValue(v.toRectF());} + if (_point) {_point->setValue(v.toPointF());} + if (_path) { + if (_path->is_dir) _setDir(v.value()); + else _setFile(v.value()); + } + if (_enum) {_setEnum(v.value());} + if (_custom) {_setCustom(v);} + if (_cur_edit) _cur_edit->blockSignals(false); +} + + +void QVariantEdit::_delete() { + if (_cur_edit) + delete _cur_edit; + _cur_edit = 0; + _custom = 0; + _empty = 0; + _line = 0; + _check = 0; + _color = 0; + _list = 0; + _date = 0; + _spin = 0; + _espin = 0; + _rect = 0; + _point = 0; + _path = 0; + _enum = 0; +} + + +void QVariantEdit::_resize() { + if (!_cur_edit) return; + layout()->addWidget(_cur_edit); +} + + +void QVariantEdit::_newPath() { + _delete(); + _path = new PathEdit(this); + _cur_edit = _path; + _value = _value.toString(); + connect(_path, SIGNAL(valueChanged()), this, SLOT(_changed())); + _resize(); + //_cur_edit->show(); +} + + +void QVariantEdit::_setEnum(const QAD::Enum & v) { + _enum->clear(); + _enum->setProperty("enum_name", v.enum_name); + foreach (const QAD::Enumerator & e, v.enum_list) + _enum->addItem(e.name, QVariant(e.value)); + int i(0); + for (i = 0; i < _enum->count(); ++i) + if (_enum->itemText(i) == v.selected) { + _enum->setCurrentIndex(i); + break; + } + if (i == _enum->count()) + _enum->setCurrentIndex(-1); +} + + +void QVariantEdit::_setFile(const QAD::File & v) { + _path->is_dir = false; + _path->setValue(v.file); + _path->filter = v.filter; + _path->is_abs = v.is_abs; +} + + +void QVariantEdit::_setDir(const QAD::Dir & v) { + _path->is_dir = true; + _path->setValue(v.dir); + _path->is_abs = v.is_abs; +} + + +void QVariantEdit::_setCustom(const QVariant & v) { + _custom->setProperty("value", v); +} + + +void QVariantEdit::_changed() { + if (_check) _check->setText(_check->isChecked() ? "true" : "false"); + emit valueChanged(value()); +} + diff --git a/test/qad/widgets/qvariantedit.h b/test/qad/widgets/qvariantedit.h new file mode 100644 index 0000000..7954281 --- /dev/null +++ b/test/qad/widgets/qvariantedit.h @@ -0,0 +1,135 @@ +#ifndef QVARIANTEDIT_H +#define QVARIANTEDIT_H + +#include "qvariantedit_custom.h" +#include "qad_types.h" +#include "clineedit.h" +#include "ecombobox.h" +#include "colorbutton.h" +#include "qrectedit.h" +#include "qpointedit.h" +#include "evalspinbox.h" +#include +#include +#include + + +class StringListEdit: public QWidget +{ + Q_OBJECT +public: + StringListEdit(QWidget * parent = 0); + ~StringListEdit(); + + QStringList value() const; + +private: + virtual void changeEvent(QEvent * e); + + QBoxLayout lay; + EComboBox * combo; + QPushButton * butt_add, * butt_del, * butt_clear; + +public slots: + void setValue(const QStringList & v); + +private slots: + void editItem(); + void addItem(); + void delItem(); + void clear(); + +signals: + void valueChanged(); + +}; + + + + +class PathEdit: public QWidget +{ + Q_OBJECT +public: + PathEdit(QWidget * parent = 0); + ~PathEdit() {delete line; delete butt_select;} + + QString value() const {return line->text();} + + bool is_dir, is_abs; + QString filter; + +private: + virtual void changeEvent(QEvent * e); + virtual void resizeEvent(QResizeEvent * ); + + QBoxLayout lay; + CLineEdit * line; + QPushButton * butt_select; + +public slots: + void setValue(const QString & v) {line->setText(v);} + +private slots: + void select(); + +signals: + void valueChanged(); + +}; + + + + +class QVariantEdit: public QWidget +{ + Q_OBJECT + Q_PROPERTY(QVariant value READ value WRITE setValue) + +public: + explicit QVariantEdit(QWidget * parent = 0); + ~QVariantEdit(); + + QVariant value() const; + //QSize sizeHint() const {if (_cur_edit) return _cur_edit->sizeHint(); return QWidget::sizeHint();} + //QSize minimumSizeHint() const {if (_cur_edit) return _cur_edit->minimumSizeHint(); return QWidget::minimumSizeHint();} + +protected: + virtual void resizeEvent(QResizeEvent * ); + void _recreate(const QVariant & new_value); + void _delete(); + void _resize(); + void _newPath(); + void _setEnum(const QAD::Enum & v); + void _setFile(const QAD::File & v); + void _setDir(const QAD::Dir & v); + void _setCustom(const QVariant & v); + + + QLabel * _empty; + CLineEdit * _line; + QCheckBox * _check; + ColorButton * _color; + StringListEdit * _list; + QDateTimeEdit * _date; + QDoubleSpinBox * _spin; + EvalSpinBox * _espin; + QRectEdit * _rect; + QPointEdit * _point; + PathEdit * _path; + EComboBox * _enum; + QWidget * _custom, * _cur_edit; + QVariant _value; + +private slots: + void _changed(); + +public slots: + void setValue(const QVariant & v); + +signals: + void valueChanged(QVariant); + +}; + +#endif // QVARIANTEDIT_H diff --git a/test/qad/widgets/qvariantedit_custom.cpp b/test/qad/widgets/qvariantedit_custom.cpp new file mode 100644 index 0000000..29fecb5 --- /dev/null +++ b/test/qad/widgets/qvariantedit_custom.cpp @@ -0,0 +1,43 @@ +#include "qvariantedit_custom.h" + + +QVariantEditorFactories::QVariantEditorFactories() { + +} + + +QVariantEditorFactories * QVariantEditorFactories::instance() { + static QVariantEditorFactories * ret = new QVariantEditorFactories(); + return ret; +} + + +void QVariantEditorFactories::registerEditorFactory(int meta_id, QVariantEditorFactoryBase * f) { + QVariantEditorFactories * s = QVariantEditorFactories::instance(); + if (!f) { + unregisterEditorFactory(meta_id); + return; + } + if (s->factories.contains(meta_id)) + ;//qDebug() << "[QVariantEditorFactories] Warning: factory for metaTypeID" << meta_id << "already registered, override"; + s->factories[meta_id] = f; +} + + +void QVariantEditorFactories::unregisterEditorFactory(int meta_id) { + QVariantEditorFactories * s = QVariantEditorFactories::instance(); + s->factories.remove(meta_id); +} + + +bool QVariantEditorFactories::isRegisteredEditorFactory(int meta_id) { + QVariantEditorFactories * s = QVariantEditorFactories::instance(); + return s->factories.contains(meta_id); +} + + +QVariantEditorFactoryBase * QVariantEditorFactories::editorFactory(int meta_id) { + QVariantEditorFactories * s = QVariantEditorFactories::instance(); + return s->factories.value(meta_id, 0); +} + diff --git a/test/qad/widgets/qvariantedit_custom.h b/test/qad/widgets/qvariantedit_custom.h new file mode 100644 index 0000000..c2b1c3b --- /dev/null +++ b/test/qad/widgets/qvariantedit_custom.h @@ -0,0 +1,36 @@ +#ifndef QVARIANTEDIT_CUSTOM_H +#define QVARIANTEDIT_CUSTOM_H + +#include +#include +#include + +class QVariantEdit; + + +class QVariantEditorFactoryBase { + friend class QVariantEdit; +public: + QVariantEditorFactoryBase() {} + virtual QWidget * createEditor() = 0; +private: + +}; + + +class QVariantEditorFactories { +public: + static void registerEditorFactory(int meta_id, QVariantEditorFactoryBase * f); + static void unregisterEditorFactory(int meta_id); + static bool isRegisteredEditorFactory(int meta_id); + static QVariantEditorFactoryBase * editorFactory(int meta_id); + +private: + QVariantEditorFactories(); + static QVariantEditorFactories * instance(); + + QMap factories; +}; + + +#endif // QVARIANTEDIT_CUSTOM_H diff --git a/test/qad/widgets/session_manager.cpp b/test/qad/widgets/session_manager.cpp new file mode 100644 index 0000000..a602cf5 --- /dev/null +++ b/test/qad/widgets/session_manager.cpp @@ -0,0 +1,236 @@ +#include +#include +#include +#include "session_manager.h" + + +void SessionManager::setFile(const QString & file) { + if (file.isEmpty()) { + file_.clear(); + return; + } + QFileInfo fi(file); + if (fi.isAbsolute()) { + file_ = file; + return; + } + file_ = QApplication::applicationDirPath() + "/" + file; +} + + +void SessionManager::removeMainWidget(QWidget * e) { + for (int i = 0; i < widgets.size(); ++i) { + if (widgets[i].first == e->objectName()) { + widgets.remove(i); + --i; + } + } +} + + +void SessionManager::save() { + if (file_.isEmpty()) return; + QPIConfig sr(file_); + QObjectList tsc; + for (int i = 0; i < mwindows.size(); ++i) { + tsc << mwindows[i].second; + sr.setValue(mwindows[i].first + " state", mwindows[i].second->saveState(), false); + sr.setValue(mwindows[i].first + " window state", (int)mwindows[i].second->windowState(), false); + sr.setValue(mwindows[i].first + " geometry " + QString::number((int)mwindows[i].second->windowState()), mwindows[i].second->saveGeometry(), false); + QList sp = mwindows[i].second->findChildren(); + foreach (QSplitter * s, sp) + sr.setValue(mwindows[i].first + " splitter " + s->objectName(), s->saveState(), false); + } + for (int i = 0; i < widgets.size(); ++i) { + tsc << widgets[i].second; + sr.setValue(widgets[i].first + " geometry " + QString::number((int)widgets[i].second->windowState()), widgets[i].second->saveGeometry(), false); + sr.setValue(widgets[i].first + " window state", (int)widgets[i].second->windowState(), false); + } + for (int i = 0; i < checks.size(); ++i) + sr.setValue(checks[i].first, checks[i].second->isChecked(), false); + for (int i = 0; i < lines.size(); ++i) + sr.setValue(lines[i].first, lines[i].second->text(), "s", false); + for (int i = 0; i < combos.size(); ++i) + sr.setValue(combos[i].first, combos[i].second->currentIndex(), false); + for (int i = 0; i < dspins.size(); ++i) + sr.setValue(dspins[i].first, dspins[i].second->value(), false); + for (int i = 0; i < spins.size(); ++i) + sr.setValue(spins[i].first, spins[i].second->value(), false); + for (int i = 0; i < spinsliders.size(); ++i) + sr.setValue(spinsliders[i].first, spinsliders[i].second->value(), false); + for (int i = 0; i < evals.size(); ++i) + sr.setValue(evals[i].first, evals[i].second->expression(), "s", false); + for (int i = 0; i < tabs.size(); ++i) + sr.setValue(tabs[i].first, tabs[i].second->currentIndex(), false); + for (int i = 0; i < buttons.size(); ++i) + sr.setValue(buttons[i].first, buttons[i].second->isChecked(), false); + for (int i = 0; i < stacks.size(); ++i) + sr.setValue(stacks[i].first, stacks[i].second->currentIndex(), false); + for (int i = 0; i < actions.size(); ++i) + sr.setValue(actions[i].first, actions[i].second->isChecked(), false); + for (int i = 0; i < stringlists.size(); ++i) + sr.setValue(stringlists[i].first, *stringlists[i].second, false); + for (int i = 0; i < strings.size(); ++i) + sr.setValue(strings[i].first, *strings[i].second, "s", false); + for (int i = 0; i < colors.size(); ++i) + sr.setValue(colors[i].first, *colors[i].second, false); + for (int i = 0; i < bools.size(); ++i) + sr.setValue(bools[i].first, *bools[i].second, false); + for (int i = 0; i < ints.size(); ++i) + sr.setValue(ints[i].first, *ints[i].second, false); + for (int i = 0; i < floats.size(); ++i) + sr.setValue(floats[i].first, *floats[i].second, false); + QSet all_list; + foreach (QObject * c, tsc) { + all_list |= QSet::fromList(c->findChildren()); + } + QMap funcs = metaFunctions(all_list, "sessionSave"); + //qDebug() << "check for save" << all_list.size(); + foreach (QObject * o, all_list) { + const QMetaObject * mo = o->metaObject(); + QByteArray fn = funcs.value(mo); + if (!mo || fn.isEmpty()) continue; + QByteArray value; + //qDebug() << "save" << o->objectName(); + mo->invokeMethod(o, fn.constData(), Q_ARG(QByteArray*, &value)); + sr.setValue(o->objectName(), value, false); + } + //qDebug() << mcl.size(); + emit saving(sr); + sr.writeAll(); +} + + +void SessionManager::load(bool onlyMainwindow) { + if (file_.isEmpty()) return; + QPIConfig sr(file_); + QObjectList tsc; + for (int i = 0; i < mwindows.size(); ++i) { + tsc << mwindows[i].second; + mwindows[i].second->restoreState(sr.getValue(mwindows[i].first + " state", QByteArray())); + mwindows[i].second->restoreGeometry(sr.getValue(mwindows[i].first + " geometry " + QString::number((int)mwindows[i].second->windowState()), QByteArray())); + mwindows[i].second->setWindowState((Qt::WindowState)(int)sr.getValue(mwindows[i].first + " window state", 0)); + QList sp = mwindows[i].second->findChildren(); + foreach (QSplitter * s, sp) + s->restoreState(sr.getValue(mwindows[i].first + " splitter " + s->objectName(), QByteArray())); + } + for (int i = 0; i < widgets.size(); ++i) { + tsc << widgets[i].second; + widgets[i].second->restoreGeometry(sr.getValue(widgets[i].first + " geometry " + QString::number((int)widgets[i].second->windowState()), QByteArray())); + widgets[i].second->setWindowState((Qt::WindowState)(int)sr.getValue(widgets[i].first + " window state", 0)); + } + if (onlyMainwindow) return; + for (int i = 0; i < checks.size(); ++i) + checks[i].second->setChecked(sr.getValue(checks[i].first, checks[i].second->isChecked())); + for (int i = 0; i < lines.size(); ++i) + lines[i].second->setText(sr.getValue(lines[i].first, lines[i].second->text())); + for (int i = 0; i < combos.size(); ++i) { + QComboBox * c = combos[i].second; + int v = sr.getValue(combos[i].first, c->currentIndex()); + if (v >= 0 && v < c->count()) + c->setCurrentIndex(v); + } + for (int i = 0; i < dspins.size(); ++i) + dspins[i].second->setValue(sr.getValue(dspins[i].first, dspins[i].second->value())); + for (int i = 0; i < spins.size(); ++i) + spins[i].second->setValue(sr.getValue(spins[i].first, spins[i].second->value())); + for (int i = 0; i < spinsliders.size(); ++i) + spinsliders[i].second->setValue(sr.getValue(spinsliders[i].first, spinsliders[i].second->value())); + for (int i = 0; i < evals.size(); ++i) + evals[i].second->setExpression(sr.getValue(evals[i].first, evals[i].second->expression())); + for (int i = 0; i < tabs.size(); ++i) { + QTabWidget * t = tabs[i].second; + int v = sr.getValue(tabs[i].first, t->currentIndex()); + if (v >= 0 && v < t->count()) + t->setCurrentIndex(v); + } + for (int i = 0; i < buttons.size(); ++i) + buttons[i].second->setChecked(sr.getValue(buttons[i].first, buttons[i].second->isChecked())); + for (int i = 0; i < stacks.size(); ++i) + stacks[i].second->setCurrentIndex(qMin((int)sr.getValue(stacks[i].first, stacks[i].second->currentIndex()), stacks[i].second->count())); + for (int i = 0; i < actions.size(); ++i) + actions[i].second->setChecked(sr.getValue(actions[i].first, actions[i].second->isChecked())); + for (int i = 0; i < stringlists.size(); ++i) + *stringlists[i].second = sr.getValue(stringlists[i].first, *stringlists[i].second); + for (int i = 0; i < strings.size(); ++i) + *strings[i].second = sr.getValue(strings[i].first, *strings[i].second).stringValue(); + for (int i = 0; i < colors.size(); ++i) + *colors[i].second = sr.getValue(colors[i].first, *colors[i].second); + for (int i = 0; i < bools.size(); ++i) + *bools[i].second = sr.getValue(bools[i].first, *bools[i].second); + for (int i = 0; i < ints.size(); ++i) + *ints[i].second = sr.getValue(ints[i].first, *ints[i].second); + for (int i = 0; i < floats.size(); ++i) + *floats[i].second = sr.getValue(floats[i].first, *floats[i].second); + QSet all_list; + foreach (QObject * c, tsc) { + all_list |= QSet::fromList(c->findChildren()); + } + QMap funcs = metaFunctions(all_list, "sessionLoad"); + //qDebug() << "check for load" << all_list.size(); + foreach (QObject * o, all_list) { + const QMetaObject * mo = o->metaObject(); + QByteArray fn = funcs.value(mo); + if (!mo || fn.isEmpty()) continue; + QByteArray value = sr.getValue(o->objectName(), QByteArray()); + //qDebug() << "load" << o->objectName(); + mo->invokeMethod(o, fn.constData(), Q_ARG(QByteArray*, &value)); + } + emit loading(sr); +} + + +void SessionManager::clear(bool with_filename) { + mwindows.clear(); + widgets.clear(); + checks.clear(); + lines.clear(); + combos.clear(); + dspins.clear(); + spins.clear(); + spinsliders.clear(); + evals.clear(); + tabs.clear(); + buttons.clear(); + stacks.clear(); + actions.clear(); + stringlists.clear(); + strings.clear(); + colors.clear(); + bools.clear(); + ints.clear(); + floats.clear(); + if (with_filename) setFile(""); +} + + +QMap SessionManager::metaFunctions(const QSet & objects, QByteArray fname) { + QMap funcs; + foreach (QObject * o, objects) { + const QMetaObject * mo = o->metaObject(); + if (!mo) continue; + QByteArray fn; + if (!funcs.contains(mo)) { + for (int i = 0; i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + QByteArray mmn = +#if QT_VERSION >= 0x050000 + mm.name(); +#else + mm.signature(); + mmn = mmn.left(mmn.indexOf('(')); +#endif + + if (mmn == fname) { + if (mm.parameterTypes().size() > 0) { + if (mm.parameterTypes()[0] == "QByteArray*") { + fn = mmn; + } + } + } + } + funcs[mo] = fn; + } else fn = funcs[mo]; + } + return funcs; +} diff --git a/test/qad/widgets/session_manager.h b/test/qad/widgets/session_manager.h new file mode 100644 index 0000000..3a8a5aa --- /dev/null +++ b/test/qad/widgets/session_manager.h @@ -0,0 +1,104 @@ +#ifndef SESSION_MANAGER_H +#define SESSION_MANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spinslider.h" +#include "evalspinbox.h" +#include "qpiconfig.h" + +/// for all children widgets of "QMainWindow"s and MainWidgets +/// check for slots +/// * void sessionSave(QByteArray * data); +/// * void sessionLoad(QByteArray * data); + + +class SessionManager: public QObject +{ + Q_OBJECT +public: + SessionManager(const QString & file = QString()) {setFile(file);} + ~SessionManager() {;} + + void setFile(const QString & file); + + void addEntry(QMainWindow * e) {mwindows.push_back(QPair(e->objectName(), e));} + void addEntry(QCheckBox * e) {checks.push_back(QPair(e->objectName(), e));} + void addEntry(QLineEdit * e) {lines.push_back(QPair(e->objectName(), e));} + void addEntry(QComboBox * e) {combos.push_back(QPair(e->objectName(), e));} + void addEntry(QDoubleSpinBox * e) {dspins.push_back(QPair(e->objectName(), e));} + void addEntry(QSpinBox * e) {spins.push_back(QPair(e->objectName(), e));} + void addEntry(SpinSlider * e) {spinsliders.push_back(QPair(e->objectName(), e));} + void addEntry(EvalSpinBox * e) {evals.push_back(QPair(e->objectName(), e));} + void addEntry(QTabWidget * e) {tabs.push_back(QPair(e->objectName(), e));} + void addEntry(QAction * e) {actions.push_back(QPair(e->objectName(), e));} + void addEntry(QAbstractButton * e) {buttons.push_back(QPair(e->objectName(), e));} + void addEntry(QStackedWidget * e) {stacks.push_back(QPair(e->objectName(), e));} + void addMainWidget(QWidget * e) {widgets.push_back(QPair(e->objectName(), e));} + void removeMainWidget(QWidget * e); + + void addEntry(const QString & name, QMainWindow * e) {mwindows.push_back(QPair(name, e));} + void addEntry(const QString & name, QCheckBox * e) {checks.push_back(QPair(name, e));} + void addEntry(const QString & name, QLineEdit * e) {lines.push_back(QPair(name, e));} + void addEntry(const QString & name, QComboBox * e) {combos.push_back(QPair(name, e));} + void addEntry(const QString & name, QDoubleSpinBox * e) {dspins.push_back(QPair(name, e));} + void addEntry(const QString & name, QSpinBox * e) {spins.push_back(QPair(name, e));} + void addEntry(const QString & name, SpinSlider * e) {spinsliders.push_back(QPair(name, e));} + void addEntry(const QString & name, EvalSpinBox * e) {evals.push_back(QPair(name, e));} + void addEntry(const QString & name, QTabWidget * e) {tabs.push_back(QPair(name, e));} + void addEntry(const QString & name, QAbstractButton * e) {buttons.push_back(QPair(name, e));} + void addEntry(const QString & name, QStackedWidget * e) {stacks.push_back(QPair(name, e));} + void addEntry(const QString & name, QAction * e) {actions.push_back(QPair(name, e));} + void addEntry(const QString & name, QStringList * e) {stringlists.push_back(QPair(name, e));} + void addEntry(const QString & name, QString * e) {strings.push_back(QPair(name, e));} + void addEntry(const QString & name, QColor * e) {colors.push_back(QPair(name, e));} + void addEntry(const QString & name, bool * e) {bools.push_back(QPair(name, e));} + void addEntry(const QString & name, int * e) {ints.push_back(QPair(name, e));} + void addEntry(const QString & name, float * e) {floats.push_back(QPair(name, e));} + void addMainWidget(const QString & name, QWidget * e) {widgets.push_back(QPair(name, e));} + +private: + QMap metaFunctions(const QSet & objects, QByteArray fname); + + QVector > mwindows; + QVector > widgets; + QVector > checks; + QVector > lines; + QVector > combos; + QVector > dspins; + QVector > spins; + QVector > spinsliders; + QVector > evals; + QVector > tabs; + QVector > buttons; + QVector > stacks; + QVector > actions; + QVector > stringlists; + QVector > strings; + QVector > colors; + QVector > bools; + QVector > ints; + QVector > floats; + QString file_; + +public slots: + void save(); + void load(bool onlyMainwindow = false); + void clear(bool with_filename = true); + +signals: + void loading(QPIConfig & ); + void saving(QPIConfig & ); + +}; + +#endif // SESSION_MANAGER_H diff --git a/test/qad/widgets/shortcuts.cpp b/test/qad/widgets/shortcuts.cpp new file mode 100644 index 0000000..234a637 --- /dev/null +++ b/test/qad/widgets/shortcuts.cpp @@ -0,0 +1,233 @@ +#include "shortcuts.h" + + +void ShortcutEdit::keyPressEvent(QKeyEvent * e) { + Qt::KeyboardModifiers km = e->modifiers(); + km &= ~Qt::KeypadModifier; + km &= ~Qt::GroupSwitchModifier; + if (e->key() != Qt::Key_Control && e->key() != Qt::Key_Shift && + e->key() != Qt::Key_Alt && e->key() != Qt::Key_Meta) + setText(QKeySequence(km | e->key()).toString()); +} + + + +Shortcuts::Shortcuts(QWidget * parent, bool on): QTreeWidget(parent) { + aw = 0; + QImage ti(QSize(16, 16), QImage::Format_ARGB32_Premultiplied); + QPainter p(&ti); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.eraseRect(ti.rect()); + p.end(); + empty_icon = QPixmap::fromImage(ti); + bfont = font(); + bfont.setWeight(QFont::Bold); + active = on; + setColumnCount(2); +#if QT_VERSION < 0x050000 + header()->setResizeMode(0, QHeaderView::ResizeToContents); + header()->setResizeMode(1, QHeaderView::ResizeToContents); +#else + header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); +#endif + header()->setStretchLastSection(true); + setColumnWidth(1, 200); + setSortingEnabled(true); + QStringList l; + l << tr("Command") << tr("Shortcut"); + setHeaderLabels(l); + assignWindow(parent); +} + + +Shortcuts::~Shortcuts() { + foreach (ShortcutEdit * i, edits) + delete i; + edits.clear(); +} + + +void Shortcuts::changeEvent(QEvent * e) { + if (e->type() == QEvent::LanguageChange) { + QStringList l; + l << tr("Command") << tr("Shortcut"); + setHeaderLabels(l); + } +} + + +void Shortcuts::assignWindow(QWidget * w) { + if (w == 0) return; + while ((qobject_cast(w) == 0) && (w->parentWidget() != 0)) + w = w->parentWidget(); + aw = qobject_cast(w); + updateShortcuts(); +} + + +QStringList Shortcuts::actionTree(QAction * a) { + QStringList tree; + QList aw = a->associatedWidgets(); + if (aw.size() == 0) return tree; + QWidget * cw = 0; + QMenu * tm; + QToolBar * tt; + foreach (QWidget * i, aw) { + tm = qobject_cast(i); + if (tm == 0) continue; + cw = i; + while (cw != 0) { + tm = qobject_cast(cw); + if (tm != 0) { + if (!tm->title().isEmpty()) + tree.push_front(tm->title()); + cw = cw->parentWidget(); + } else break; + } + if (!tree.isEmpty()) return tree; + } + foreach (QWidget * i, aw) { + tt = qobject_cast(i); + if (tt == 0) continue; + cw = i; + if (!tt->windowTitle().isEmpty()) + tree.push_front(tt->windowTitle()); + break; + } + return tree; +} + + +QList > Shortcuts::shortcuts() { + QList > l; + foreach (ShortcutEdit * i, edits) { + if (i->action()->objectName().isEmpty()) continue; + l << QPair(i->action()->objectName(), i->text()); + } + return l; +} + + +void Shortcuts::clear() { + foreach (ShortcutEdit * i, edits) + delete i; + edits.clear(); + hide(); + QList tl = findItems("", Qt::MatchContains); + foreach (QTreeWidgetItem * i, tl) + delete i; + show(); +} + + +bool Shortcuts::checkAction(QAction * a) { + if (a->menu() != 0) return false; + if (a->isSeparator()) return false; + if (a->text().isEmpty()) return false; + if (a->associatedWidgets().isEmpty()) return false; + if (QString(a->metaObject()->className()) != "QAction") return false; + if (qobject_cast(a) != 0) return false; + return true; +} + + +void Shortcuts::updateShortcuts() { + //return; + if (aw == 0 || !active) return; + hide(); + int cpos = verticalScrollBar()->value(); + clear(); +#if QT_VERSION < 0x050000 + header()->setResizeMode(0, QHeaderView::Fixed); +#else + header()->setSectionResizeMode(0, QHeaderView::Fixed); +#endif + QList al = aw->findChildren(); + QTreeWidgetItem * pi, * ci; + QStringList tree; + bool s = isSortingEnabled(), isFound; + setSortingEnabled(false); + foreach (QAction * i, al) { + if (!checkAction(i)) continue; + edits.push_back(new ShortcutEdit()); + tree = actionTree(i); + pi = invisibleRootItem(); + foreach (QString t, tree) { + isFound = false; + for (int j = 0; j < pi->childCount(); ++j) { + if (pi->child(j)->text(0) == t) { + pi = pi->child(j); + isFound = true; + break; + } + } + if (isFound) continue; + ci = new QTreeWidgetItem(pi); + ci->setText(0, t); + ci->setToolTip(0, t); + ci->setFont(0, bfont); + pi->addChild(ci); + pi = ci; + } + ci = new QTreeWidgetItem(pi); + ci->setText(0, i->text()); + ci->setToolTip(0, i->text()); + if (i->icon().isNull()) + ci->setIcon(0, empty_icon); + else + ci->setIcon(0, i->icon()); + edits.back()->ti = ci; + edits.back()->assignAction(i); + pi->addChild(ci); + //qDebug() << "set widget" << edits.back(); + setItemWidget(ci, 1, edits.back()); + } + setSortingEnabled(s); +#if QT_VERSION < 0x050000 + header()->setResizeMode(0, QHeaderView::ResizeToContents); +#else + header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); +#endif + expandAll(); + verticalScrollBar()->setValue(cpos); + show(); +} + + +void Shortcuts::commit() { + foreach (ShortcutEdit * i, edits) + i->commit(); +} + + +void Shortcuts::reset() { + foreach (ShortcutEdit * i, edits) + i->reset(); + updateShortcuts(); +} + + +void Shortcuts::filter(const QString & what) { + hide(); + for (int i = 0; i < topLevelItemCount(); ++i) + filterTree(topLevelItem(i), what); + show(); +} + + +bool Shortcuts::filterTree(QTreeWidgetItem * ti, QString f) { + if (f.isEmpty()) { + for (int i = 0; i < ti->childCount(); ++i) + filterTree(ti->child(i), f); + ti->setHidden(false); + return true; + } + bool isFound = false; + for (int i = 0; i < ti->childCount(); ++i) + if (filterTree(ti->child(i), f)) isFound = true; + if (ti->text(0).indexOf(f, 0, Qt::CaseInsensitive) >= 0 || + ti->text(1).indexOf(f, 0, Qt::CaseInsensitive) >= 0) isFound = true; + ti->setHidden(!isFound); + return isFound; +} diff --git a/test/qad/widgets/shortcuts.h b/test/qad/widgets/shortcuts.h new file mode 100644 index 0000000..1bddb1a --- /dev/null +++ b/test/qad/widgets/shortcuts.h @@ -0,0 +1,90 @@ +#ifndef SHORTCUTS_H +#define SHORTCUTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "clineedit.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class ShortcutEdit: public CLineEdit +{ + Q_OBJECT + friend class Shortcuts; +public: + explicit ShortcutEdit(QWidget * parent = 0): CLineEdit(parent) {ti = 0; ca = 0; connect(this, SIGNAL(textChanged(QString)), this, SLOT(textChanged_(QString)));} + + void assignAction(QAction * a) {clear(); ca = a; reset();} + QAction * action() const {return ca;} + bool isEmpty() const {return text().isEmpty();} + void commit() {if (ca == 0) return; ca->setShortcut(QKeySequence(text()));} + void reset() {if (ca == 0) return; setText(ca->shortcut().toString());} + +private slots: + void textChanged_(QString t) {if (ti != 0) ti->setText(1, t);} + +private: + void keyPressEvent(QKeyEvent * e); + + QAction * ca; + QTreeWidgetItem * ti; + +}; + + +class Shortcuts: public QTreeWidget +{ + Q_OBJECT + +public: + explicit Shortcuts(QWidget * parent = 0, bool on = true); + ~Shortcuts(); + + void assignWindow(QWidget * w); + void setActive(bool on) {active = on;} + QList > shortcuts(); + QStringList actionTree(QAction * a); + static bool checkAction(QAction * a); + +public slots: + void clear(); + void updateShortcuts(); + void commit(); + void reset(); + void filter(const QString & what); + +private: + virtual void updateEditorGeometries() {foreach (ShortcutEdit * i, edits) i->setGeometry(visualRect(indexFromItem(i->ti, 1)));} + virtual void changeEvent(QEvent * ); + bool filterTree(QTreeWidgetItem * ti, QString f); + + QMainWindow * aw; + QVector edits; + QIcon empty_icon; + QFont bfont; + bool active; + +private slots: + +signals: + void shortcutChanged(QAction * , QShortcut & ); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPINSLIDER_H diff --git a/test/qad/widgets/spinslider.cpp b/test/qad/widgets/spinslider.cpp new file mode 100644 index 0000000..29ac431 --- /dev/null +++ b/test/qad/widgets/spinslider.cpp @@ -0,0 +1,85 @@ +#include "spinslider.h" +#include + + +SpinSlider::SpinSlider(QWidget * parent): QWidget(parent) { + min_ = val_ = 0.; + max_ = 100.; + dec_ = 1; + page = 10.; + ticks_ = 1; + direc = LeftToRight; + square = false; + slider = new QSlider(); + slider->setOrientation(Qt::Horizontal); + slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + spin = new QDoubleSpinBox(); + adjust(); + layout = new QBoxLayout(QBoxLayout::LeftToRight); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(slider); + layout->addWidget(spin); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); + connect(spin, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double))); + setLayout(layout); +} + + +SpinSlider::~SpinSlider() { + delete spin; + delete slider; + delete layout; +} + + +void SpinSlider::setOrientation(Qt::Orientation orient) { + slider->setOrientation(orient); + if (orient == Qt::Horizontal) + slider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + else + slider->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); +} + + +void SpinSlider::adjust() { + adjusting = true; + delim = qPow(10, dec_); + spin->setDecimals(dec_); + spin->setRange(min_, max_); + if (square) slider->setRange(sqrt(min_ * delim), sqrt(max_ * delim)); + else slider->setRange(min_ * delim, max_ * delim); + if (val_ < min_) val_ = min_; + if (val_ > max_) val_ = max_; + spin->setValue(val_); + if (square) slider->setValue(static_cast(sqrt(val_ * delim))); + else slider->setValue(static_cast(val_ * delim)); + slider->setPageStep(qRound(page * delim)); + slider->setTickInterval(qRound(ticks_ * delim)); + emit valueChanged(val_); + emit valueChanged(qRound(val_)); + adjusting = false; +} + + +void SpinSlider::sliderChanged(int value) { + if (adjusting) return; + adjusting = true; + if (square) spin->setValue(static_cast(sqr(value) / delim)); + else spin->setValue(static_cast(value) / delim); + val_ = spin->value(); + emit valueChanged(val_); + emit valueChanged(qRound(val_)); + adjusting = false; +} + + +void SpinSlider::spinChanged(double value) { + if (adjusting) return; + adjusting = true; + val_ = value; + if (square) slider->setValue(static_cast(sqrt(value * delim))); + else slider->setValue(qRound(value * delim)); + emit valueChanged(val_); + emit valueChanged(qRound(val_)); + adjusting = false; +} diff --git a/test/qad/widgets/spinslider.h b/test/qad/widgets/spinslider.h new file mode 100644 index 0000000..6c9d0c8 --- /dev/null +++ b/test/qad/widgets/spinslider.h @@ -0,0 +1,102 @@ +#ifndef SPINSLIDER_H +#define SPINSLIDER_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class SpinSlider: public QWidget +{ + Q_OBJECT + Q_ENUMS(Direction) + Q_PROPERTY(double minimum READ minimum WRITE setMinimum) + Q_PROPERTY(double maximum READ maximum WRITE setMaximum) + Q_PROPERTY(double value READ value WRITE setValue) + Q_PROPERTY(int decimals READ decimals WRITE setDecimals) + Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep) + Q_PROPERTY(double pageStep READ pageStep WRITE setPageStep) + Q_PROPERTY(QString prefix READ prefix WRITE setPrefix) + Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) + Q_PROPERTY(QSlider::TickPosition tickPosition READ tickPosition WRITE setTickPosition) + Q_PROPERTY(int tickInterval READ tickInterval WRITE setTickInterval) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(Direction direction READ direction WRITE setDirection) + Q_PROPERTY(bool invertedAppearance READ invertedAppearance WRITE setInvertedAppearance) + Q_PROPERTY(bool squareScale READ squareScale WRITE setSquareScale) + Q_PROPERTY(double spinMinimum READ spinMinimum WRITE setSpinMinimum) + Q_PROPERTY(double spinMaximum READ spinMaximum WRITE setSpinMaximum) + +public: + explicit SpinSlider(QWidget * parent = 0); + ~SpinSlider(); + + enum Direction {LeftToRight, RightToLeft, TopToBottom, BottomToTop}; + + double minimum() const {return min_;} + double maximum() const {return max_;} + double spinMinimum() const {return spin->minimum();} + double spinMaximum() const {return spin->maximum();} + double value() const {return val_;} + int decimals() const {return dec_;} + double singleStep() const {return spin->singleStep();} + double pageStep() const {return page;} + QString prefix() const {return spin->prefix();} + QString suffix() const {return spin->suffix();} + QSlider::TickPosition tickPosition() const {return slider->tickPosition();} + int tickInterval() const {return ticks_;} + Qt::Orientation orientation() const {return slider->orientation();} + Direction direction() const {return direc;} + bool invertedAppearance() const {return slider->invertedAppearance();} + bool squareScale() const {return square;} + + void setSingleStep(double step) {spin->setSingleStep(step); slider->setPageStep(qRound(step * delim));} + void setPageStep(double step) {page = step; slider->setPageStep(qRound(page * delim));} + void setPrefix(QString prefix) {spin->setPrefix(prefix);} + void setSuffix(QString suffix) {spin->setSuffix(suffix);} + void setTickPosition(QSlider::TickPosition tp) {slider->setTickPosition(tp);} + void setTickInterval(int ti) {ticks_ = ti; slider->setTickInterval(qRound(ticks_ * delim));} + void setOrientation(Qt::Orientation orient); + void setDirection(Direction d) {direc = d; layout->setDirection((QBoxLayout::Direction)d);} + void setInvertedAppearance(bool yes) {slider->setInvertedAppearance(yes);} + void setSquareScale(bool yes) {square = yes; adjust();} + +public slots: + void setMinimum(double value) {min_ = value; adjust();} + void setMaximum(double value) {max_ = value; adjust();} + void setSpinMinimum(double value) {spin->setMinimum(value);} + void setSpinMaximum(double value) {spin->setMaximum(value);} + void setValue(double value) {val_ = value; spin->setValue(value);} + void setDecimals(int value) {dec_ = value; adjust();} + void reset() {val_ = 0.; spin->setValue(0.);} + +private: + void adjust(); + double sqr(const double & v) {return v * v;} + + double min_, max_, val_, delim, page; + int dec_, ticks_; + bool adjusting, square; + QSlider * slider; + QDoubleSpinBox * spin; + QBoxLayout * layout; + Direction direc; + +private slots: + void sliderChanged(int value); + void spinChanged(double value); + +signals: + void valueChanged(double); + void valueChanged(int); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SPINSLIDER_H diff --git a/test/qcd_utils/CMakeLists.txt b/test/qcd_utils/CMakeLists.txt new file mode 100644 index 0000000..c9d3fba --- /dev/null +++ b/test/qcd_utils/CMakeLists.txt @@ -0,0 +1,50 @@ +project(qcd_utils) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (NOT LIBPROJECT) + find_package(PIP REQUIRED) + option(LIB "System install" 0) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +find_package(QAD REQUIRED) +list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME}) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +include_directories(${PIP_INCLUDES} ${QAD_INCLUDES}) +file(GLOB SRC "*.h" "*.cpp" "*.ui" "*.qrc" "lang/*.ts") +find_qt(${QtVersions} Core Gui) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} pip qad_utils qad_widgets qad_graphic cd_utils piqt) +message(STATUS "Building ${PROJECT_NAME}") + +set(QCD_UTILS_UTILS 1) +if(LIBPROJECT) + sdk_install("" "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") + set(QCD_UTILS_UTILS ${UTILS}) +else() + if(LIB) + if(WIN32) + install(FILES ${out_HDR} DESTINATION ${MINGW_INCLUDE}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtBin) + else() + install(FILES ${out_HDR} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() + +if(QCD_UTILS_UTILS) + add_subdirectory(pult) +endif() diff --git a/test/qcd_utils/pult/CMakeLists.txt b/test/qcd_utils/pult/CMakeLists.txt new file mode 100644 index 0000000..230c5a0 --- /dev/null +++ b/test/qcd_utils/pult/CMakeLists.txt @@ -0,0 +1,26 @@ +project(cd_pult) +find_qt(${QtVersions} Core Gui OpenGL) +qt_sources(SRC) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_executable(${PROJECT_NAME} WIN32 out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets qad_graphic qad_application qcd_utils piqt_utils) +message(STATUS "Building ${PROJECT_NAME}") +if(LIB) + if(WIN32) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + else() + if(APPLE) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/local/bin) + else() + if (DEFINED ANDROID_PLATFORM) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${ANDROID_SYSTEM_LIBRARY_PATH}/usr/bin) + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin) + endif() + endif() + endif() + #message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") +else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + #message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") +endif() diff --git a/test/qcd_utils/pult/cddirectk.cpp b/test/qcd_utils/pult/cddirectk.cpp new file mode 100644 index 0000000..7ab3be6 --- /dev/null +++ b/test/qcd_utils/pult/cddirectk.cpp @@ -0,0 +1,312 @@ +#include "cddirectk.h" +#include "ui_cddirectk_type_dialog.h" +#include "cdutils_core.h" +#include "cdutils_k.h" +#include "qcd_core.h" +#include "qcd_model.h" +#include "graphic.h" +#include "piqt.h" +#include "qvariantedit.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace CDUtils; + + +KDockWidget::KDockWidget(QString title, QMainWindow * p): QDockWidget(title, p) { + da = p; + menu = new QMenu(this); + QAction * a = new QAction(QIcon(":/icons/document-edit.png"), "Rename ...", this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(rename())); + dactions << a; + a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove", this); + connect(a, SIGNAL(triggered(bool)), this, SIGNAL(removeRequest())); + dactions << a; + menu_k = new QMenu(this); + menu_k->setTitle(trUtf8("Remove K")); + lay = new QFormLayout(); + lay->setContentsMargins(0, qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin), 0, 0); + lay->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); + QWidget * w = new QWidget(); + w->setAcceptDrops(true); + w->installEventFilter(this); + w->setLayout(lay); + setWidget(w); + type_dialog = new CDDirectKTypeDialog(); +} + + +void KDockWidget::addK(const CDType & t, CDDirectKTypeDialog::TypeInfo ti) { + if (t.cd_type() != CDType::cdK) return; + PIDeque xp = t.path(); + if (k_list.contains(xp)) return; + k_list << xp; + info_list << ti; + //piCout << "add" << xp; + QWidget * ve = ti.create(); + //qDebug() << "add" << ve; + lay->addRow(PI2QString(t.pathString().join(".")) + ":", ve); + QCDCore::instance()->bindWidget(ve, t); + //ve->setValue(); +} + + +QByteArray KDockWidget::save() const { + ChunkStream cs; + cs.add(1, windowTitle()) + .add(2, getList(k_list)) + .add(3, info_list); + return cs.data(); +} + + +void KDockWidget::load(QByteArray ba) { + clear(); + if (ba.isEmpty()) return; + ChunkStream cs(ba); + PIVector > list; + QVector ilist; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: setWindowTitle(cs.getData()); break; + case 2: list = setList(cs.getData()); break; + case 3: ilist = cs.getData >(); break; + default: break; + } + } + ilist.resize(list.size()); + for (int i = 0; i < list.size_s(); ++i) { + addK(K[list[i]], ilist[i]); + } +} + + +void KDockWidget::clear() { + while (lay->rowCount() > 0) + removeRow(0); + k_list.clear(); + info_list.clear(); +} + + +void KDockWidget::changedGlobal() { + //piCout << "changedGlobal ..." << k_list.size_s() << info_list.size() << lay->count() << lay->rowCount(); + for (int i = 0; i < k_list.size_s(); ++i) { + //piCout << "update" << i << "0"; + if (!K.exists(k_list[i])) { + k_list.remove(i); + info_list.remove(i); + removeRow(i); + --i; + continue; + } + //piCout << "update" << i << "1"; + QLabel * lbl = qobject_cast(lay->itemAt(i, QFormLayout::LabelRole)->widget()); + //piCout << "update" << i << "2"; + if (lbl) lbl->setText(PI2QString(K[k_list[i]].pathString().join(".")) + ":"); + //piCout << "update" << i << "3"; + } + //piCout << "changedGlobal ok"; +} + + +bool KDockWidget::eventFilter(QObject * o, QEvent * e) { + //if (o == graphic->viewport()) { + switch (e->type()) { + case QEvent::DragMove: { + QDragMoveEvent * de = (QDragMoveEvent*)e; + const QMimeData * mime = de->mimeData(); + if (!mime) break; + if (!mime->text().startsWith("k")) break; + de->setDropAction(Qt::CopyAction); + de->accept(); + return true; + } break; + case QEvent::DragEnter: { + QDragEnterEvent * de = (QDragEnterEvent*)e; + const QMimeData * mime = de->mimeData(); + if (!mime) break; + if (!mime->text().startsWith("k")) break; + de->setDropAction(Qt::CopyAction); + de->accept(); + return true; + } break; + case QEvent::Drop: { + QDropEvent * de = (QDropEvent*)e; + const QMimeData * mime = de->mimeData(); + if (!mime) break; + //qDebug() << "drop" << mime->text(); + if (!mime->text().startsWith("k")) break; + CDDirectKTypeDialog::TypeInfo ti; + CDType & k(K[CDCore::stringToPath(Q2PIString(mime->text().mid(1)))]); + if (k.type().left(1) == "n" || k.type().left(1) == "f") { + if (type_dialog->exec() == QDialog::Accepted) + ti = type_dialog->getType(); + else + return true; + } + addK(k, ti); + de->accept(); + return true; + } break; + default: break; + } + //} + return QWidget::eventFilter(o, e); +} + + +void KDockWidget::contextMenuEvent(QContextMenuEvent * e) { + qDeleteAll(menu_k->actions()); + menu_k->clear(); + for (int i = 0; i < k_list.size_s(); ++i) { + QAction * a = new QAction(PI2QString(K[k_list[i]].pathString().join(".")), this); + a->setData(i); + connect(a, SIGNAL(triggered(bool)), this, SLOT(removeK())); + menu_k->addAction(a); + } + QMenu * mwm = da->createPopupMenu(); + menu->clear(); + menu->addActions(dactions); + menu->addMenu(menu_k); + menu->addSeparator(); + menu->addActions(mwm->actions()); + menu->popup(e->globalPos()); + mwm->deleteLater(); +} + + +void KDockWidget::removeRow(int r) { + if (r < 0 || r >= lay->rowCount()) return; +#if QT_VERSION >= 0x050800 + QFormLayout::TakeRowResult rr = lay->takeRow(r); + if (rr.fieldItem) {delete rr.fieldItem->widget(); delete rr.fieldItem;} + if (rr.labelItem) {delete rr.labelItem->widget(); delete rr.labelItem;} +#else + piForTimes (2) { + QLayoutItem * i = lay->itemAt(r+r); + lay->removeItem(i); + if (i) {delete i->widget(); delete i;} + } +#endif +} + + +void KDockWidget::rename() { + QString nn = QInputDialog::getText(this, trUtf8("Rename area"), trUtf8("New area name:"), + QLineEdit::Normal, windowTitle()); + if (nn.isEmpty()) return; + setWindowTitle(nn); +} + + +void KDockWidget::removeK() { + QAction * a = qobject_cast(sender()); + if (!a) return; + int ind = a->data().toInt(); + if (ind < 0 || ind >= k_list.size_s()) return; + k_list.remove(ind); + if (ind >= 0 && ind < info_list.size()) + info_list.remove(ind); + removeRow(ind); +} + + + + +CDDirectK::CDDirectK(QWidget * parent) : QWidget(parent), Ui::CDDirectK() { + setupUi(this); + da = new QMainWindow(); + da->setWindowFlags(frame->windowFlags()); + da->setDockNestingEnabled(true); + layoutMain->addWidget(da); +} + + +CDDirectK::~CDDirectK() { +} + + +void CDDirectK::reset() { + qDeleteAll(docks); + docks.clear(); +} + + +QByteArray CDDirectK::save() const { + ChunkStream cs; + QVector dstates; + foreach (KDockWidget * d, docks) { + dstates << d->save(); + } + cs.add(1, docks.size()) + .add(2, dstates) + .add(3, da->saveState()); + return cs.data(); +} + + +void CDDirectK::load(QByteArray ba) { + reset(); + if (ba.isEmpty()) return; + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: { + int s = cs.getData(); + piForTimes (s) + addArea(); + } break; + case 2: { + QVector dstates = cs.getData >(); + for (int i = 0; i < piMini(dstates.size(), docks.size()); ++i) + docks[i]->load(dstates[i]); + } break; + case 3: da->restoreState(cs.getData()); break; + default: break; + } + } +} + + +void CDDirectK::addArea() { + KDockWidget * dw = new KDockWidget(QString("area %1").arg(docks.size()), da); + connect(dw, SIGNAL(removeRequest()), this, SLOT(removeArea())); + da->addDockWidget(Qt::RightDockWidgetArea, dw); + docks << dw; + for (int i = 0; i < docks.size(); ++i) + docks[i]->setObjectName(QString("dock_%1").arg(i)); +} + + +void CDDirectK::changedGlobal() { + foreach (KDockWidget * d, docks) + d->changedGlobal(); +} + + +void CDDirectK::removeArea() { + KDockWidget * d = qobject_cast(sender()); + if (!d) return; + docks.removeAll(d); + d->deleteLater(); + for (int i = 0; i < docks.size(); ++i) + docks[i]->setObjectName(QString("dock_%1").arg(i)); +} + + +void CDDirectK::on_buttonAdd_clicked() { + addArea(); +} + + +void CDDirectK::on_buttonRemoveAll_clicked() { + qDeleteAll(docks); + docks.clear(); +} diff --git a/test/qcd_utils/pult/cddirectk.h b/test/qcd_utils/pult/cddirectk.h new file mode 100644 index 0000000..b5d71f4 --- /dev/null +++ b/test/qcd_utils/pult/cddirectk.h @@ -0,0 +1,77 @@ +#ifndef CDDIRECTK_H +#define CDDIRECTK_H + +#include "cdgraphics.h" +#include "ui_cddirectk.h" +#include "cddirectk_type_dialog.h" + +class QFormLayout; + + +class KDockWidget: public QDockWidget { + Q_OBJECT +public: + KDockWidget(QString title = QString(), QMainWindow * p = 0); + + void addK(const CDUtils::CDType & t, CDDirectKTypeDialog::TypeInfo ti); + QByteArray save() const; + void load(QByteArray ba); + void clear(); + void changedGlobal(); + + QFormLayout * lay; + +private: + bool eventFilter(QObject * o, QEvent * e); + void contextMenuEvent(QContextMenuEvent * e); + void removeRow(int r); + + QMenu * menu, * menu_k; + QList dactions; + QMainWindow * da; + CDDirectKTypeDialog * type_dialog; + PIVector > k_list; + QVector info_list; + +private slots: + void rename(); + void removeK(); + +signals: + void removeRequest(); + +}; + + + + +class CDDirectK: public QWidget, public Ui::CDDirectK +{ + Q_OBJECT +public: + explicit CDDirectK(QWidget *parent = 0); + ~CDDirectK(); + + void reset(); + QByteArray save() const; + void load(QByteArray ba); + +private: + void addArea(); + + QList docks; + QMainWindow * da; + +public slots: + void changedGlobal(); + +private slots: + void removeArea(); + void on_buttonAdd_clicked(); + void on_buttonRemoveAll_clicked(); + +signals: + +}; + +#endif // CDDIRECTK_H diff --git a/test/qcd_utils/pult/cddirectk.ui b/test/qcd_utils/pult/cddirectk.ui new file mode 100644 index 0000000..a14e715 --- /dev/null +++ b/test/qcd_utils/pult/cddirectk.ui @@ -0,0 +1,85 @@ + + + CDDirectK + + + + 0 + 0 + 624 + 411 + + + + CD Pult + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + + 0 + + + 0 + + + + + Add new + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Remove all + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + + + + + diff --git a/test/qcd_utils/pult/cddirectk_type_dialog.cpp b/test/qcd_utils/pult/cddirectk_type_dialog.cpp new file mode 100644 index 0000000..ba95141 --- /dev/null +++ b/test/qcd_utils/pult/cddirectk_type_dialog.cpp @@ -0,0 +1,76 @@ +#include "cddirectk_type_dialog.h" +#include "cdutils_core.h" +#include "qcd_core.h" +#include "qcd_model.h" +#include "piqt.h" +#include "spinslider.h" +#include "qvariantedit.h" + + +CDDirectKTypeDialog::CDDirectKTypeDialog(QWidget * parent) : QDialog(parent), Ui::CDDirectKTypeDialog() { + setupUi(this); +} + + +CDDirectKTypeDialog::~CDDirectKTypeDialog() { +} + + +CDDirectKTypeDialog::TypeInfo CDDirectKTypeDialog::getType() const { + if (!groupBox->isChecked()) return TypeInfo(); + TypeInfo ret; + ret.type = comboType->currentIndex(); + ret.params_d[0] = evalMin->value(); + ret.params_d[1] = evalMax->value(); + ret.params_d[2] = spinDecimals->value(); + ret.params_d[3] = evalStep->value(); + ret.params_s[0] = linePrefix->text(); + ret.params_s[1] = lineSuffix->text(); + return ret; +} + + + + +CDDirectKTypeDialog::TypeInfo::TypeInfo(int type_) { + type = type_; + params_d.resize(4); + params_s.resize(2); +} + + +QWidget * CDDirectKTypeDialog::TypeInfo::create() { + params_d.resize(4); + params_s.resize(2); + switch (type) { + case 0: { + QDoubleSpinBox * ret = new QDoubleSpinBox(); + ret->setMinimum(params_d[0]); + ret->setMaximum(params_d[1]); + ret->setDecimals(params_d[2]); + ret->setSingleStep(params_d[3]); + ret->setPrefix(params_s[0]); + ret->setSuffix(params_s[1]); + return ret; + } break; + case 1: { + QSlider * ret = new QSlider(Qt::Horizontal); + ret->setMinimum(params_d[0]); + ret->setMaximum(params_d[1]); + ret->setSingleStep(params_d[3]); + return ret; + } break; + case 2: { + SpinSlider * ret = new SpinSlider(); + ret->setMinimum(params_d[0]); + ret->setMaximum(params_d[1]); + ret->setDecimals(params_d[2]); + ret->setSingleStep(params_d[3]); + ret->setPrefix(params_s[0]); + ret->setSuffix(params_s[1]); + return ret; + } break; + default: break; + } + return new QVariantEdit(); +} diff --git a/test/qcd_utils/pult/cddirectk_type_dialog.h b/test/qcd_utils/pult/cddirectk_type_dialog.h new file mode 100644 index 0000000..c3ee06a --- /dev/null +++ b/test/qcd_utils/pult/cddirectk_type_dialog.h @@ -0,0 +1,44 @@ +#ifndef CDDIRECTK_TYPE_DIALOG_H +#define CDDIRECTK_TYPE_DIALOG_H + +#include +#include "ui_cddirectk_type_dialog.h" + + +class CDDirectKTypeDialog: public QDialog, public Ui::CDDirectKTypeDialog +{ + Q_OBJECT +public: + explicit CDDirectKTypeDialog(QWidget * parent = 0); + ~CDDirectKTypeDialog(); + + struct TypeInfo { + TypeInfo(int type_ = -1); + QWidget * create(); + int type; + QVector params_d; + QVector params_s; + }; + + TypeInfo getType() const; + +private: + +public slots: + +private slots: + +signals: + +}; + +inline QDataStream & operator <<(QDataStream & s, const CDDirectKTypeDialog::TypeInfo & v) { + s << v.type << v.params_d << v.params_s; + return s; +} +inline QDataStream & operator >>(QDataStream & s, CDDirectKTypeDialog::TypeInfo & v) { + s >> v.type >> v.params_d >> v.params_s; + return s; +} + +#endif // CDDIRECTK_TYPE_DIALOG_H diff --git a/test/qcd_utils/pult/cddirectk_type_dialog.ui b/test/qcd_utils/pult/cddirectk_type_dialog.ui new file mode 100644 index 0000000..3c4ac3a --- /dev/null +++ b/test/qcd_utils/pult/cddirectk_type_dialog.ui @@ -0,0 +1,232 @@ + + + CDDirectKTypeDialog + + + + 0 + 0 + 275 + 310 + + + + CD Pult + + + + + + Custom + + + true + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Type: + + + + + + + + Spin + + + + + Slider + + + + + SpinSlider + + + + + + + + Min: + + + + + + + + + + Max: + + + + + + + 100.000000000000000 + + + + + + + Decimals: + + + + + + + + + + Single step: + + + + + + + 1.000000000000000 + + + + + + + Prefix: + + + + + + + + + + Suffix: + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + CLineEdit + QLineEdit +
clineedit.h
+
+ + EvalSpinBox + QWidget +
evalspinbox.h
+
+
+ + + + groupBox + toggled(bool) + widget + setEnabled(bool) + + + 86 + 49 + + + 94 + 91 + + + + + buttonBox + accepted() + CDDirectKTypeDialog + accept() + + + 297 + 285 + + + 315 + 280 + + + + + buttonBox + rejected() + CDDirectKTypeDialog + reject() + + + 281 + 290 + + + 283 + 307 + + + + +
diff --git a/test/qcd_utils/pult/cdgraphics.cpp b/test/qcd_utils/pult/cdgraphics.cpp new file mode 100644 index 0000000..ea08f4c --- /dev/null +++ b/test/qcd_utils/pult/cdgraphics.cpp @@ -0,0 +1,353 @@ +#include "cdgraphics.h" +#include "ui_qcd_graphic.h" +#include "cdutils_core.h" +#include "cdutils_x.h" +#include "qcd_core.h" +#include "qcd_model.h" +#include "graphic.h" +#include "piqt.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace CDUtils; + + +QStringList CDUtils::getList(const PIVector > & x_list) { + QStringList ret; + piForeachC (PIDeque & p, x_list) + ret << PI2QString(CDCore::pathToString(p)); + return ret; +} + + +PIVector > CDUtils::setList(const QStringList & l) { + PIVector > ret; + foreach (QString s, l) + ret << CDCore::stringToPath(Q2PIString(s)); + return ret; +} + + + + +GDockWidget::GDockWidget(QString title, QMainWindow * p): QDockWidget(title, p) { + da = p; + menu = new QMenu(this); + QAction * a = new QAction(QIcon(":/icons/document-edit.png"), "Rename ...", this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(rename())); + dactions << a; + a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove", this); + connect(a, SIGNAL(triggered(bool)), this, SIGNAL(removeRequest())); + dactions << a; + menu_x = new QMenu(this); + menu_x->setTitle(trUtf8("Remove X")); + graphic = new CDGraphicWidget(); + graphic->graphic()->viewport()->setAcceptDrops(true); + graphic->graphic()->viewport()->installEventFilter(this); + setWidget(graphic); +} + + +void GDockWidget::addX(const CDType & t) { + if (t.cd_type() != CDType::cdX) return; + PIDeque xp = t.path(); + if (x_list.contains(xp)) return; + x_list << xp; + int gind = graphic->graphic()->graphicsCount(); + graphic->graphic()->setGraphicsCount(gind + 1); + graphic->graphic()->setGraphicName(PI2QString(t.pathString().join(".")), gind); +} + + +void GDockWidget::drawX(const PIMap > & data) { + for (int i = 0; i < x_list.size_s(); ++i) { + PIString sp = CDCore::pathToString(x_list[i]); + const PIVector & ch(data[sp]); + for (int j = 0; j < ch.size_s(); ++j) + graphic->graphic()->addPoint(ch[j], i, false); + } + graphic->graphic()->updateGraphics(); +} + + +QByteArray GDockWidget::save() const { + ChunkStream cs; + cs.add(1, windowTitle()) + .add(2, getList(x_list)) + .add(3, graphic->graphic()->save()) + .add(4, graphic->ui->evalHistory->expression()) + .add(5, graphic->ui->evalVisible->expression()); + return cs.data(); +} + + +void GDockWidget::load(QByteArray ba) { + if (ba.isEmpty()) return; + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: setWindowTitle(cs.getData()); break; + case 2: x_list = setList(cs.getData()); break; + case 3: graphic->graphic()->load(cs.getData()); break; + case 4: graphic->ui->evalHistory->setExpression(cs.getData()); break; + case 5: graphic->ui->evalVisible->setExpression(cs.getData()); break; + default: break; + } + } +} + + +void GDockWidget::changedGlobal() { + for (int i = 0; i < x_list.size_s(); ++i) { + if (!X.exists(x_list[i])) { + x_list.remove(i); + graphic->graphic()->removeGraphic(i); + --i; + continue; + } + graphic->graphic()->setGraphicName(PI2QString(X[x_list[i]].pathString().join(".")), i); + } +} + + +bool GDockWidget::eventFilter(QObject * o, QEvent * e) { + //if (o == graphic->viewport()) { + switch (e->type()) { + case QEvent::DragMove: { + QDragMoveEvent * de = (QDragMoveEvent*)e; + const QMimeData * mime = de->mimeData(); + //qDebug() << "enter" << mime; + if (!mime) break; + if (!mime->text().startsWith("x")) break; + de->setDropAction(Qt::CopyAction); + de->accept(); + return true; + } break; + case QEvent::DragEnter: { + QDragEnterEvent * de = (QDragEnterEvent*)e; + const QMimeData * mime = de->mimeData(); + //qDebug() << "enter" << mime; + if (!mime) break; + if (!mime->text().startsWith("x")) break; + de->setDropAction(Qt::CopyAction); + de->accept(); + return true; + } break; + case QEvent::Drop: { + QDropEvent * de = (QDropEvent*)e; + const QMimeData * mime = de->mimeData(); + if (!mime) break; + //qDebug() << "drop" << mime->text(); + if (!mime->text().startsWith("x")) break; + addX(X[CDCore::stringToPath(Q2PIString(mime->text().mid(1)))]); + de->accept(); + return true; + } break; + default: break; + } + //} + return QWidget::eventFilter(o, e); +} + + +void GDockWidget::contextMenuEvent(QContextMenuEvent * e) { + if (graphic->graphic()->underMouse()) return; + qDeleteAll(menu_x->actions()); + menu_x->clear(); + for (int i = 0; i < graphic->graphic()->graphicsCount(); ++i) { + QPixmap icon(da->iconSize()); + icon.fill(graphic->graphic()->graphic(i).pen.color()); + QAction * a = new QAction(QIcon(icon), graphic->graphic()->graphic(i).name, this); + a->setData(i); + connect(a, SIGNAL(triggered(bool)), this, SLOT(removeX())); + menu_x->addAction(a); + } + QMenu * mwm = da->createPopupMenu(); + menu->clear(); + menu->addActions(dactions); + menu->addMenu(menu_x); + menu->addSeparator(); + menu->addActions(mwm->actions()); + menu->popup(e->globalPos()); + mwm->deleteLater(); +} + + +CDGraphicWidget * GDockWidget::viewportGraphic(QObject * o) const { + if (!o) return 0; + while (!qobject_cast(o) && o) + o = o->parent(); + return qobject_cast(o); +} + + +void GDockWidget::rename() { + QString nn = QInputDialog::getText(this, trUtf8("Rename area"), trUtf8("New area name:"), + QLineEdit::Normal, windowTitle()); + if (nn.isEmpty()) return; + setWindowTitle(nn); +} + + +void GDockWidget::removeX() { + QAction * a = qobject_cast(sender()); + if (!a) return; + int ind = a->data().toInt(); + if (ind < 0 || ind >= x_list.size_s()) return; + x_list.remove(ind); + graphic->graphic()->removeGraphic(ind); +} + + + + +CDGraphics::CDGraphics(QWidget * parent) : QWidget(parent), Ui::CDGraphics() { + setupUi(this); + da = new QMainWindow(); + da->setWindowFlags(frame->windowFlags()); + da->setDockNestingEnabled(true); + layoutMain->addWidget(da); +} + + +CDGraphics::~CDGraphics() { +} + + +void CDGraphics::reset() { + qDeleteAll(docks); + docks.clear(); +} + + +QByteArray CDGraphics::save() const { + ChunkStream cs; + QVector dstates; + foreach (GDockWidget * d, docks) { + dstates << d->save(); + } + cs.add(1, docks.size()) + .add(2, dstates) + .add(3, da->saveState()); + X.lock(); + cs.add(4, getList(X.enabledList())); + X.unlock(); + cs.add(5, buttonConfigVisible->isChecked()); + return cs.data(); +} + + +void CDGraphics::load(QByteArray ba) { + reset(); + if (ba.isEmpty()) return; + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: { + int s = cs.getData(); + piForTimes (s) + addGraphic(); + } break; + case 2: { + QVector dstates = cs.getData >(); + for (int i = 0; i < piMini(dstates.size(), docks.size()); ++i) + docks[i]->load(dstates[i]); + } break; + case 3: da->restoreState(cs.getData()); break; + case 4: + X.lock(); + X.setEnabledList(setList(cs.getData())); + X.unlock(); + break; + case 5: + buttonConfigVisible->setChecked(cs.getData()); + break; + default: break; + } + } +} + + +GDockWidget * CDGraphics::graphicDock(Graphic * o) const { + if (!o) return 0; + foreach (GDockWidget * d, docks) + if (d->widget() == o) + return d; + return 0; +} + + +void CDGraphics::addGraphic() { + GDockWidget * dw = new GDockWidget(QString("area %1").arg(docks.size()), da); + connect(dw, SIGNAL(removeRequest()), this, SLOT(removeGraphic())); + connect(buttonConfigVisible, SIGNAL(toggled(bool)), dw->graphic, SLOT(setConfigVisible(bool))); + connect(buttonLegendVisible, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setLegendVisible(bool))); + connect(buttonBorderInputsVisible, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setBorderInputsVisible(bool))); + connect(buttonPause, SIGNAL(clicked(bool)), dw->graphic->graphic(), SLOT(setPaused(bool))); + dw->graphic->setConfigVisible(buttonConfigVisible->isChecked()); + dw->graphic->graphic()->setLegendVisible(buttonLegendVisible->isChecked()); + dw->graphic->graphic()->setBorderInputsVisible(buttonBorderInputsVisible->isChecked()); + da->addDockWidget(Qt::RightDockWidgetArea, dw); + docks << dw; + for (int i = 0; i < docks.size(); ++i) + docks[i]->setObjectName(QString("dock_%1").arg(i)); +} + + +void CDGraphics::receivedX() { + PIMap > data; + X.lock(); + PIVector > x_list = X.enabledList(); + PIVector ch; + piForeachC (PIDeque & p, x_list) { + CDType & t(X[p]); + if (t.xmode_rec() == CDType::X_Current) + ch.resize(1).fill(t.toDouble()); + else + ch = t.history; + t.history.clear(); + data[CDCore::pathToString(t.path())] = ch; + } + //piCout << data; + X.unlock(); + foreach (GDockWidget * d, docks) + d->drawX(data); +} + + +void CDGraphics::changedGlobal() { + foreach (GDockWidget * d, docks) + d->changedGlobal(); +} + + +void CDGraphics::removeGraphic() { + GDockWidget * d = qobject_cast(sender()); + if (!d) return; + docks.removeAll(d); + d->deleteLater(); + for (int i = 0; i < docks.size(); ++i) + docks[i]->setObjectName(QString("dock_%1").arg(i)); +} + + +void CDGraphics::on_buttonAdd_clicked() { + addGraphic(); +} + + +void CDGraphics::on_buttonClear_clicked() { + foreach (GDockWidget * d, docks) + d->graphic->graphic()->clear(); +} + + +void CDGraphics::on_buttonRemoveAll_clicked() { + qDeleteAll(docks); + docks.clear(); +} diff --git a/test/qcd_utils/pult/cdgraphics.h b/test/qcd_utils/pult/cdgraphics.h new file mode 100644 index 0000000..33ca214 --- /dev/null +++ b/test/qcd_utils/pult/cdgraphics.h @@ -0,0 +1,92 @@ +#ifndef CDGRAPHICS_H +#define CDGRAPHICS_H + +#include +#include +#include +#include "ui_cdgraphics.h" +#include "qcd_graphic.h" +#include + +namespace CDUtils { + class CDType; + class CDSection; + QStringList getList(const PIVector > & x_list); + PIVector > setList(const QStringList & l); +} + +class QMainWindow; +class Graphic; + + + + +class GDockWidget: public QDockWidget { + Q_OBJECT +public: + GDockWidget(QString title = QString(), QMainWindow * p = 0); + + void addX(const CDUtils::CDType & t); + void drawX(const PIMap > & data); + QByteArray save() const; + void load(QByteArray ba); + void changedGlobal(); + + CDGraphicWidget * graphic; + +private: + bool eventFilter(QObject * o, QEvent * e); + void contextMenuEvent(QContextMenuEvent * e); + CDGraphicWidget * viewportGraphic(QObject * o) const; + + QMenu * menu, * menu_x; + QList dactions; + QMainWindow * da; + PIVector > x_list; + +private slots: + void rename(); + void removeX(); + +signals: + void removeRequest(); + +}; + + + + +class CDGraphics : public QWidget, public Ui::CDGraphics +{ + Q_OBJECT +public: + explicit CDGraphics(QWidget *parent = 0); + ~CDGraphics(); + + void reset(); + QByteArray save() const; + void load(QByteArray ba); + +private: + GDockWidget * graphicDock(Graphic * o) const; + void addXToGraphic(const QString & xp, Graphic * g); + void addGraphic(); + + QList docks; + QMainWindow * da; + +public slots: + void receivedX(); + void changedGlobal(); + +private slots: + void removeGraphic(); + void on_buttonAdd_clicked(); + void on_buttonClear_clicked(); + void on_buttonRemoveAll_clicked(); + +signals: + +}; + +#endif // CDGRAPHICS_H diff --git a/test/qcd_utils/pult/cdgraphics.ui b/test/qcd_utils/pult/cdgraphics.ui new file mode 100644 index 0000000..9974f72 --- /dev/null +++ b/test/qcd_utils/pult/cdgraphics.ui @@ -0,0 +1,168 @@ + + + CDGraphics + + + + 0 + 0 + 624 + 411 + + + + CD Pult + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + + 0 + + + 0 + + + + + Add new + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Clear all + + + + :/icons/edit-clear.png:/icons/edit-clear.png + + + + + + + Remove all + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Remove all + + + + :/icons/layer-visible-off.png + :/icons/layer-visible-on.png:/icons/layer-visible-off.png + + + true + + + true + + + + + + + Remove all + + + + :/icons/legend.png:/icons/legend.png + + + true + + + true + + + + + + + Remove all + + + + :/icons/media-playback-pause.png:/icons/media-playback-pause.png + + + true + + + false + + + + + + + Remove all + + + + :/icons/border-line.png:/icons/border-line.png + + + true + + + false + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + + + + + + + + diff --git a/test/qcd_utils/pult/cdpult.qrc b/test/qcd_utils/pult/cdpult.qrc new file mode 100644 index 0000000..76a8d63 --- /dev/null +++ b/test/qcd_utils/pult/cdpult.qrc @@ -0,0 +1,16 @@ + + + icons/db-export.png + icons/db-import.png + icons/Apps-accessories-calculator-icon.png + icons/dialog-information.png + icons/dialog-cancel.png + icons/dialog-ok-apply.png + icons/timer.png + icons/document-revert.png + icons/flame.png + icons/view-refresh.png + icons/format-stroke-color.png + icons/accessories-text-editor.png + + diff --git a/test/qcd_utils/pult/cdpultwindow.cpp b/test/qcd_utils/pult/cdpultwindow.cpp new file mode 100644 index 0000000..e4acefa --- /dev/null +++ b/test/qcd_utils/pult/cdpultwindow.cpp @@ -0,0 +1,292 @@ +#include "edockwidget.h" +#include "cdpultwindow.h" +#include "cdutils_core.h" +#include "cdutils_k.h" +#include "cdutils_x.h" +#include "cdutils_m.h" +#include "qcd_core.h" +#include "qcd_view.h" +#include "qcd_model.h" +#include "qcd_modedialog.h" +#include "chunkstream.h" +#include "qvariantedit.h" +#include "piqt.h" +#include "piqt_highlighter.h" +#include "qcodeedit.h" +#include +#include +#include +#include + +using namespace CDUtils; + + +CDPultWindow::CDPultWindow(QWidget *parent) : EMainWindow(parent), Ui::CDPultWindow() { + setupUi(this); + centralWidget()->hide(); + setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + CDCore::instance()->initPult(); + def_config = codeConfig->text(); + checkDefaultConfig->setChecked(true); + new ConfigHighlighter(codeConfig->document()); + widgetK->setType(CDUtils::CDType::cdK); + widgetX->setType(CDUtils::CDType::cdX); + widgetC->setType(CDUtils::CDType::cdC); + widgetM->setType(CDUtils::CDType::cdM); + editFileK->setValue(QVariant::fromValue(QAD::File("", "*.dat"))); + editFileX->setValue(QVariant::fromValue(QAD::File("", "*.dat"))); + editFileC->setValue(QVariant::fromValue(QAD::File("", "*.dat"))); + editFileM->setValue(QVariant::fromValue(QAD::File("", "*.dat"))); + log_icons[CDViewWidget::OKIcon] = QIcon(":/icons/dialog-ok-apply.png"); + log_icons[CDViewWidget::FailIcon] = QIcon(":/icons/dialog-cancel.png"); + log_icons[CDViewWidget::WaitIcon] = QIcon(":/icons/timer.png"); + setRecentMenu(menuOpen_recent); + ribbon = new Ribbon(this); + session.setFile("session_cdpult.conf"); + session.addEntry(this); + session.addEntry(ribbon->tabWidget()); + session.load(); + reset(); + connect(widgetK, SIGNAL(addToLog(CDViewWidget::LogIcon,QString)), this, SLOT(addToLog(CDViewWidget::LogIcon,QString))); + connect(widgetX, SIGNAL(addToLog(CDViewWidget::LogIcon,QString)), this, SLOT(addToLog(CDViewWidget::LogIcon,QString))); + connect(widgetC, SIGNAL(addToLog(CDViewWidget::LogIcon,QString)), this, SLOT(addToLog(CDViewWidget::LogIcon,QString))); + connect(widgetM, SIGNAL(addToLog(CDViewWidget::LogIcon,QString)), this, SLOT(addToLog(CDViewWidget::LogIcon,QString))); + connect(widgetK->view, SIGNAL(changedGlobal()), widgetDirectK, SLOT(changedGlobal())); + connect(widgetX->view, SIGNAL(changedGlobal()), widgetGraphics, SLOT(changedGlobal())); + connect(widgetX->view, SIGNAL(receivedX()), widgetGraphics, SLOT(receivedX())); + connect(widgetM->view, SIGNAL(messageReceived(QString,int,QString)), this, SLOT(messageReceived(QString,int,QString))); + QCDCore::instance()->bindWidget(widgetK->view); + QCDCore::instance()->setDirectKEnabled(true); + X.start(); + if (windowState() == Qt::WindowMinimized) + setWindowState(Qt::WindowNoState); +} + + +CDPultWindow::~CDPultWindow() { +} + + +void CDPultWindow::loadFile(const QString & fp) { + load(fp); +} + + +void CDPultWindow::apply(bool sessions) { + CDCore::instance()->stop(); + widgetK->setFile(editFileK->value().value().file); + widgetX->setFile(editFileX->value().value().file); + widgetC->setFile(editFileC->value().value().file); + widgetM->setFile(editFileM->value().value().file); + if (checkDefaultConfig->isChecked()) + CDCore::instance()->initPult(); + else + CDCore::instance()->init(Q2PIString(codeConfig->text()), true); + widgetX->view->startX(); + if (sessions) { + widgetGraphics->load(session_gr); + widgetDirectK->load(session_dk); + if (!session_mw.isEmpty()) + restoreState(session_mw); + X.lock(); + PIVector > x_list = X.enabledList(); + X.unlock(); + piForeachC (PIDeque & p, x_list) + X.enable(X[p]); + ((CDItemModel*)widgetX->view->model())->updateModel(); + widgetX->view->expandAll(); + } + dockCDKView->setVisible(checkHasK->isChecked()); + dockCDXView->setVisible(checkHasX->isChecked()); + dockCDCView->setVisible(checkHasC->isChecked()); + dockCDMView->setVisible(checkHasM->isChecked()); + QMetaObject::invokeMethod(this, "changedDock", Qt::QueuedConnection); +} + + +void CDPultWindow::closeEvent(QCloseEvent *e) { + EMainWindow::closeEvent(e); + if (!e->isAccepted()) + return; + QApplication::closeAllWindows(); + session.save(); + session.setFile(QString()); +} + + +void CDPultWindow::reset(bool full) { + setWindowTitle(QString("CD Pult")); + widgetK->reset(); + setChanged(false); +} + + +bool CDPultWindow::load(const QString & path) { + qApp->setOverrideCursor(Qt::WaitCursor); + QPIConfig conf(path, QIODevice::ReadOnly); + QAD::File f = editFileK->value().value(); + checkSyncFiles->setChecked(false); + editFileK->setValue(QVariant::fromValue(QAD::File(conf.getValue("file_k").value(), f.filter))); + editFileX->setValue(QVariant::fromValue(QAD::File(conf.getValue("file_x").value(), f.filter))); + editFileC->setValue(QVariant::fromValue(QAD::File(conf.getValue("file_c").value(), f.filter))); + editFileM->setValue(QVariant::fromValue(QAD::File(conf.getValue("file_m").value(), f.filter))); + checkSyncFiles->setChecked(conf.getValue("sync_files")); + lineSessionName->setText(conf.getValue("session_name")); + last_icon = conf.getValue("icon_path", "").value(); + setAppIcon(conf.getValue("icon", QByteArray())); + checkHasK->setChecked(conf.getValue("has_k")); + checkHasX->setChecked(conf.getValue("has_x")); + checkHasC->setChecked(conf.getValue("has_c")); + checkHasM->setChecked(conf.getValue("has_m")); + checkDefaultConfig->setChecked(conf.getValue("default_config")); + codeConfig->setText(QByteArray2QString(conf.getValue("config", QByteArray()))); + if (codeConfig->text().isEmpty()) + codeConfig->setText(def_config); + session_gr = conf.getValue("session_gr", QByteArray()); + session_dk = conf.getValue("session_dk", QByteArray()); + session_mw = conf.getValue("session_mw", QByteArray()); + setChanged(false); + file_name = path; + apply(true); + qApp->restoreOverrideCursor(); + return true; +} + + +bool CDPultWindow::save(const QString & path) { + session_gr = widgetGraphics->save(); + session_dk = widgetDirectK->save(); + session_mw = saveState(); + QPIConfig conf(path, QIODevice::ReadWrite); + conf.clear(); + conf.setValue("file_k", editFileK->value().value().file); + conf.setValue("file_x", editFileX->value().value().file); + conf.setValue("file_c", editFileC->value().value().file); + conf.setValue("file_m", editFileM->value().value().file); + conf.setValue("sync_files", checkSyncFiles->isChecked()); + conf.setValue("session_name", lineSessionName->text()); + conf.setValue("icon_path", last_icon); + conf.setValue("icon", appIcon()); + conf.setValue("has_k", checkHasK->isChecked()); + conf.setValue("has_x", checkHasX->isChecked()); + conf.setValue("has_c", checkHasC->isChecked()); + conf.setValue("has_m", checkHasM->isChecked()); + conf.setValue("default_config", checkDefaultConfig->isChecked()); + conf.setValue("config", QString2QByteArray(codeConfig->text())); + conf.setValue("session_gr", session_gr); + conf.setValue("session_dk", session_dk); + conf.setValue("session_mw", session_mw); + file_name = path; + return true; + + //widgetK->setFile(path); + //widgetK->save(); +} + + +void CDPultWindow::loadingSession(QPIConfig & conf) { + setRecentFiles(conf.getValue("recent files", QStringList())); +} + + +void CDPultWindow::savingSession(QPIConfig & conf) { + conf.setValue("recent files", recentFiles()); +} + + +QByteArray CDPultWindow::appIcon() const { + QByteArray ret; + if (icon.isNull()) return ret; + QBuffer buff(&ret); + buff.open(QIODevice::WriteOnly); + icon.save(&buff, "png"); + //qDebug() << "s" << ret.size(); + return ret; +} + + +void CDPultWindow::setAppIcon(QByteArray ba) { + if (ba.isEmpty()) { + icon = QImage(); + setWindowIcon(QIcon()); + return; + } + //qDebug() << "l" << ba.size(); + icon = QImage::fromData(ba); + setWindowIcon(QIcon(QPixmap::fromImage(icon))); + //qDebug() << QApplication::windowIcon().availableSizes(); +} + + +void CDPultWindow::addToLog(CDViewWidget::LogIcon icon, const QString & msg) { + QListWidgetItem * ni = new QListWidgetItem(log_icons[icon], "(" + QTime::currentTime().toString() + ") " + msg); + bool s = listLog->verticalScrollBar()->value() == listLog->verticalScrollBar()->maximum(); + listLog->addItem(ni); + if (s) listLog->scrollToBottom(); +} + + +void CDPultWindow::messageReceived(QString path, int type, QString msg) { + MessageType mt = (MessageType)type; + const CDType & t(M.root()[CDCore::stringToPath(Q2PIString(path))]); + if (t.cd_type() != CDType::cdM) return; + if (mt == MessageBox) + QMessageBox::information(this, windowTitle(), QString("[%1]\n%2").arg(PI2QString(t.name()), msg)); +} + + +void CDPultWindow::on_editFileK_valueChanged(const QVariant & p) { + if (!checkSyncFiles->isChecked()) return; + QFileInfo fi(p.value().file); + if (fi.path().isEmpty()) return; + QString n = fi.baseName(); + QString xn(n), cn(n), mn(n); + if (n.contains("k")) { + xn.replace(n.indexOf("k"), 1, "x"); + cn.replace(n.indexOf("k"), 1, "c"); + mn.replace(n.indexOf("k"), 1, "m"); + } else { + xn += "_x"; + cn += "_c"; + mn += "_m"; + } + QString ext = fi.suffix(), dot, dir; + QString kfn(fi.filePath()); + if (!ext.isEmpty()) { + kfn.chop(ext.size()); + if (kfn.endsWith(".")) { + kfn.chop(1); + dot = "."; + } + } + if (!fi.path().isEmpty() && fi.path() != ".") { + dir = fi.path(); + if (!dir.endsWith("/")) + dir += "/"; + } + QAD::File f = editFileK->value().value(); + f.file = dir + xn + dot + ext; editFileX->setValue(QVariant::fromValue(f)); + f.file = dir + cn + dot + ext; editFileC->setValue(QVariant::fromValue(f)); + f.file = dir + mn + dot + ext; editFileM->setValue(QVariant::fromValue(f)); +} + + +void CDPultWindow::on_buttonSessionApply_clicked() { + apply(false); +} + + +void CDPultWindow::on_lineSessionName_textChanged(const QString & t) { + setWindowTitle(QString("CD Pult - %1").arg(t)); +} + + +void CDPultWindow::on_buttonIcon_clicked() { + QList ifl = QImageReader::supportedImageFormats(); + QStringList sfl; foreach (QByteArray s, ifl) sfl << ("*." + QString(s).toLower()); + QString f = QFileDialog::getOpenFileName(this, tr("Select icon"), last_icon, tr("Images") + " (" + sfl.join(" ") + ")"); + if (f.isEmpty()) return; + last_icon = f; + icon = QImage(last_icon); + setWindowIcon(QIcon(QPixmap::fromImage(icon))); +} diff --git a/test/qcd_utils/pult/cdpultwindow.h b/test/qcd_utils/pult/cdpultwindow.h new file mode 100644 index 0000000..6e8c499 --- /dev/null +++ b/test/qcd_utils/pult/cdpultwindow.h @@ -0,0 +1,53 @@ +#ifndef CDPULTWINDOW_H +#define CDPULTWINDOW_H + +#include "emainwindow.h" +#include "ui_cdpultwindow.h" +#include "cdviewwidget.h" +#include "ribbon.h" +#include "piobject.h" + + +class CDPultWindow : public EMainWindow, public Ui::CDPultWindow +{ + Q_OBJECT + Q_ENUMS(LogIcon) +public: + + explicit CDPultWindow(QWidget *parent = 0); + ~CDPultWindow(); + void loadFile(const QString & fp); + void apply(bool sessions); + +private: + + void closeEvent(QCloseEvent *); + void reset(bool full = false); + bool load(const QString & path); + bool save(const QString & path); + QString loadFilter() {return "Pult session(*.conf)";} + QString saveFilter() {return loadFilter();} + void loadingSession(QPIConfig & conf); + void savingSession(QPIConfig & conf); + + QByteArray appIcon() const; + void setAppIcon(QByteArray ba); + + Ribbon * ribbon; + QMap log_icons; + QByteArray session_gr, session_dk, session_mw; + QString def_config, last_icon; + QImage icon; + +private slots: + void addToLog(CDViewWidget::LogIcon icon, const QString & msg); + void messageReceived(QString path, int type, QString msg); + + void on_editFileK_valueChanged(const QVariant & p); + void on_buttonSessionApply_clicked(); + + void on_lineSessionName_textChanged(const QString & t); + void on_buttonIcon_clicked(); +}; + +#endif // CDPULTWINDOW_H diff --git a/test/qcd_utils/pult/cdpultwindow.ui b/test/qcd_utils/pult/cdpultwindow.ui new file mode 100644 index 0000000..e7a1904 --- /dev/null +++ b/test/qcd_utils/pult/cdpultwindow.ui @@ -0,0 +1,612 @@ + + + CDPultWindow + + + + 0 + 0 + 798 + 593 + + + + CD Pult + + + true + + + QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AnimatedDocks + + + + + + 0 + 0 + 798 + 24 + + + + + Main + + + + Open recent + + + + :/icons/document-open-recent.png:/icons/document-open-recent.png + + + + + + + + + + + + + :/icons/document-edit.png:/icons/document-edit.png + + + K + + + 1 + + + + + 0 + + + + + + + + + + + :/icons/dialog-information.png:/icons/dialog-information.png + + + Log + + + 8 + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + + :/icons/qvariantedit.png:/icons/qvariantedit.png + + + X + + + 1 + + + + + + + + + + + + + :/icons/configure.png:/icons/configure.png + + + Session + + + 2 + + + + + + + Apply + + + + :/icons/dialog-ok-apply.png:/icons/dialog-ok-apply.png + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Name: + + + + + + + + + + + + Select icon... + + + + :/icons/view-preview.png:/icons/view-preview.png + + + + + + + + + K file: + + + + + + + + + + X file: + + + + + + + + + + C file: + + + + + + + + + + Sync files + + + + + + + + + K + + + + + + + X + + + + + + + C + + + + + + + M + + + + + + + + + Default config + + + + + + + M file: + + + + + + + + + + + + + 0 + 0 + + + + include = ip.conf +[connection] +device.cd = eth://udp:${ip.pult}:27002:${ip.app}:27001 #s +[] + + + + + DejaVu Sans Mono + 9 + + + + + + + + + + + :/icons/legend.png:/icons/legend.png + + + C + + + 1 + + + + + + + + + + + + + :/icons/format-stroke-color.png:/icons/format-stroke-color.png + + + Graphics + + + 8 + + + + + + + + + + + + + :/icons/tools-wizard.png:/icons/tools-wizard.png + + + Direct K + + + 8 + + + + + + + + + + + + + :/icons/accessories-text-editor.png:/icons/accessories-text-editor.png + + + M + + + 1 + + + + + + + + + + + + + :/icons/document-open.png:/icons/document-open.png + + + Open... + + + Ctrl+O + + + + + + :/icons/document-save.png:/icons/document-save.png + + + Save + + + Ctrl+S + + + + + + :/icons/document-save-as.png:/icons/document-save-as.png + + + Save As... + + + Ctrl+Shift+S + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EMainWindow + QMainWindow +
emainwindow.h
+ 1 +
+ + CLineEdit + QLineEdit +
clineedit.h
+
+ + QCodeEdit + QWidget +
qcodeedit.h
+
+ + QVariantEdit + QWidget +
qvariantedit.h
+
+ + EDockWidget + QDockWidget +
edockwidget.h
+ 1 +
+ + CDViewWidget + QWidget +
cdviewwidget.h
+ 1 +
+ + CDGraphics + QWidget +
cdgraphics.h
+ 1 +
+ + CDDirectK + QWidget +
cddirectk.h
+ 1 +
+
+ + + + + + + + + actionSave + triggered() + CDPultWindow + saveFile() + + + -1 + -1 + + + 399 + 299 + + + + + actionSaveAs + triggered() + CDPultWindow + saveAsFile() + + + -1 + -1 + + + 399 + 299 + + + + + actionOpen + triggered() + CDPultWindow + openFile() + + + -1 + -1 + + + 399 + 299 + + + + + checkSyncFiles + toggled(bool) + editFileX + setDisabled(bool) + + + 786 + 220 + + + 786 + 151 + + + + + checkSyncFiles + toggled(bool) + editFileC + setDisabled(bool) + + + 786 + 220 + + + 786 + 172 + + + + + checkDefaultConfig + toggled(bool) + codeConfig + setDisabled(bool) + + + 786 + 276 + + + 581 + 304 + + + + + checkSyncFiles + toggled(bool) + editFileM + setDisabled(bool) + + + 767 + 205 + + + 767 + 187 + + + + +
diff --git a/test/qcd_utils/pult/cdviewwidget.cpp b/test/qcd_utils/pult/cdviewwidget.cpp new file mode 100644 index 0000000..a01c87a --- /dev/null +++ b/test/qcd_utils/pult/cdviewwidget.cpp @@ -0,0 +1,78 @@ +#include "cdviewwidget.h" +#include "cdutils_core.h" +#include "qcd_core.h" +#include "qcd_model.h" +#include "qcd_modedialog.h" +#include "qvariantedit.h" +#include + + +CDViewWidget::CDViewWidget(QWidget * parent) : QWidget(parent), Ui::CDViewWidget() { + qRegisterMetaType("CDViewWidget::LogIcon"); + setupUi(this); + connect(view, SIGNAL(sendSucceed()), this, SLOT(sended())); + connect(view, SIGNAL(receiveSucceed()), this, SLOT(received())); + connect(view, SIGNAL(sendFailed()), this, SLOT(sendFailed())); + connect(view, SIGNAL(receiveFailed()), this, SLOT(receiveFailed())); +} + + +CDViewWidget::~CDViewWidget() { +} + + +void CDViewWidget::reset() { + setFile(""); +} + + +void CDViewWidget::setType(int t) { + view->setType((CDUtils::CDType::cdT)t); + tl_u = view->typeLetter().toUpper(); + tl_l = view->typeLetter().toLower(); + view->refresh(); +} + + +void CDViewWidget::setFile(const QString & f) { + view->setFile(f); + view->load(); +} + + +void CDViewWidget::on_buttonSend_clicked() { + if (view->inProgress()) {addToLog(WaitIcon, "processing..."); return;} + addToLog(WaitIcon, "Sending " + tl_u + "..."); + view->send(); +} + + +void CDViewWidget::on_buttonReceive_clicked() { + if (view->inProgress()) {addToLog(WaitIcon, "processing..."); return;} + addToLog(WaitIcon, "Receiving " + tl_u + "..."); + view->receive(); +} + + +void CDViewWidget::on_buttonLoad_clicked() { + view->load(); +} + + +void CDViewWidget::on_buttonSave_clicked() { + view->save(); +} + + +void CDViewWidget::on_buttonParse_clicked() { + QString path = QFileDialog::getOpenFileName(this, "Select header file", "", + QString("%1 Description(%2_description.h);;Headers(*.h)").arg(tl_u, tl_l)); + if (path.isEmpty()) return; + CDUtils::UpdateModeFlags mode = CDUtils::SaveByName; + if (!view->root()->isEmpty()) { + QCDModeDialog cdm; + if (cdm.exec() != QDialog::Accepted) return; + mode = cdm.mode(); + } + view->buildFromHeader(path, mode); +} diff --git a/test/qcd_utils/pult/cdviewwidget.h b/test/qcd_utils/pult/cdviewwidget.h new file mode 100644 index 0000000..3145666 --- /dev/null +++ b/test/qcd_utils/pult/cdviewwidget.h @@ -0,0 +1,41 @@ +#ifndef CDVIEWWIDGET_H +#define CDVIEWWIDGET_H + +#include +#include "ui_cdviewwidget.h" + + +class CDViewWidget : public QWidget, public Ui::CDViewWidget +{ + Q_OBJECT +public: + explicit CDViewWidget(QWidget *parent = 0); + ~CDViewWidget(); + + enum LogIcon {NoIcon, OKIcon, FailIcon, WaitIcon}; + + void reset(); + void setType(int t); + void setFile(const QString & f); + +private: + QString tl_u, tl_l; + +private slots: + void sended() {addToLog(OKIcon, tl_u + " " + tr("sended succesfull"));} + void received() {addToLog(OKIcon, tl_u + " " + tr("received succesfull"));} + void sendFailed() {addToLog(FailIcon, tl_u + " " + tr("NOT sended"));} + void receiveFailed() {addToLog(FailIcon, tl_u + " " + tr("NOT received"));} + void on_buttonSend_clicked(); + void on_buttonReceive_clicked(); + void on_buttonLoad_clicked(); + void on_buttonSave_clicked(); + void on_buttonParse_clicked(); + //void on_buttonCalculate_clicked(); + +signals: + void addToLog(CDViewWidget::LogIcon icon, const QString & msg); + +}; + +#endif // CDVIEWWIDGET_H diff --git a/test/qcd_utils/pult/cdviewwidget.ui b/test/qcd_utils/pult/cdviewwidget.ui new file mode 100644 index 0000000..5b0ff0b --- /dev/null +++ b/test/qcd_utils/pult/cdviewwidget.ui @@ -0,0 +1,176 @@ + + + CDViewWidget + + + + 0 + 0 + 689 + 459 + + + + CD Pult + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + Send + + + + :/icons/flame.png:/icons/flame.png + + + + + + + Receive + + + + :/icons/document-revert.png:/icons/document-revert.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Load + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + Save + + + + :/icons/document-save.png:/icons/document-save.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Update description ... + + + + :/icons/view-refresh.png:/icons/view-refresh.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 1 + 20 + + + + + + + + + + + + Qt::CustomContextMenu + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::DragOnly + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + + + + + + CDView + QTreeView +
qcd_view.h
+
+
+ + + + + +
diff --git a/test/qcd_utils/pult/icons/Apps-accessories-calculator-icon.png b/test/qcd_utils/pult/icons/Apps-accessories-calculator-icon.png new file mode 100644 index 0000000..37a3035 Binary files /dev/null and b/test/qcd_utils/pult/icons/Apps-accessories-calculator-icon.png differ diff --git a/test/qcd_utils/pult/icons/accessories-text-editor.png b/test/qcd_utils/pult/icons/accessories-text-editor.png new file mode 100644 index 0000000..1d9dca2 Binary files /dev/null and b/test/qcd_utils/pult/icons/accessories-text-editor.png differ diff --git a/test/qcd_utils/pult/icons/db-export.png b/test/qcd_utils/pult/icons/db-export.png new file mode 100644 index 0000000..7cfba88 Binary files /dev/null and b/test/qcd_utils/pult/icons/db-export.png differ diff --git a/test/qcd_utils/pult/icons/db-import.png b/test/qcd_utils/pult/icons/db-import.png new file mode 100644 index 0000000..85bc121 Binary files /dev/null and b/test/qcd_utils/pult/icons/db-import.png differ diff --git a/test/qcd_utils/pult/icons/dialog-cancel.png b/test/qcd_utils/pult/icons/dialog-cancel.png new file mode 100644 index 0000000..ace88ab Binary files /dev/null and b/test/qcd_utils/pult/icons/dialog-cancel.png differ diff --git a/test/qcd_utils/pult/icons/dialog-information.png b/test/qcd_utils/pult/icons/dialog-information.png new file mode 100644 index 0000000..dfe70b9 Binary files /dev/null and b/test/qcd_utils/pult/icons/dialog-information.png differ diff --git a/test/qcd_utils/pult/icons/dialog-ok-apply.png b/test/qcd_utils/pult/icons/dialog-ok-apply.png new file mode 100644 index 0000000..3260f60 Binary files /dev/null and b/test/qcd_utils/pult/icons/dialog-ok-apply.png differ diff --git a/test/qcd_utils/pult/icons/document-revert.png b/test/qcd_utils/pult/icons/document-revert.png new file mode 100644 index 0000000..e04514b Binary files /dev/null and b/test/qcd_utils/pult/icons/document-revert.png differ diff --git a/test/qcd_utils/pult/icons/flame.png b/test/qcd_utils/pult/icons/flame.png new file mode 100644 index 0000000..d267f72 Binary files /dev/null and b/test/qcd_utils/pult/icons/flame.png differ diff --git a/test/qcd_utils/pult/icons/format-stroke-color.png b/test/qcd_utils/pult/icons/format-stroke-color.png new file mode 100644 index 0000000..4c4d290 Binary files /dev/null and b/test/qcd_utils/pult/icons/format-stroke-color.png differ diff --git a/test/qcd_utils/pult/icons/timer.png b/test/qcd_utils/pult/icons/timer.png new file mode 100644 index 0000000..c0b8361 Binary files /dev/null and b/test/qcd_utils/pult/icons/timer.png differ diff --git a/test/qcd_utils/pult/icons/view-refresh.png b/test/qcd_utils/pult/icons/view-refresh.png new file mode 100644 index 0000000..66f0ceb Binary files /dev/null and b/test/qcd_utils/pult/icons/view-refresh.png differ diff --git a/test/qcd_utils/pult/main.cpp b/test/qcd_utils/pult/main.cpp new file mode 100644 index 0000000..80ed8e9 --- /dev/null +++ b/test/qcd_utils/pult/main.cpp @@ -0,0 +1,56 @@ +#include +#include "cdpultwindow.h" + + +inline uint qHash(const PIString &t) { + return t.hash(); +} + + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); +#if QT_VERSION >= 0x050000 + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); +#endif + //################################ + QHash h2; + QMap m2; + PIVector keys; + PIString prefix = "1234567890fgbjyfjyjbghjkbgkbgjgsfh jhfgj ghfjhjfjf"; + PITimeMeasurer tm; + double el = 0.; + for (int i=0; i<100000; ++i) keys << randomi(); + piCout << keys.size(); + + tm.reset(); + for (int i=0; i<100000; ++i) { + h2[keys[i]] = prefix; + } + el = tm.elapsed_m(); piCout << el << h2.capacity() << h2.size(); + + tm.reset(); + for (int i=0; i<100000; ++i) { + m2[keys[i]] = prefix; + } + el = tm.elapsed_m(); piCout << el; + piCout << "*********"; + + PIString _s; + tm.reset(); + for (int i=0; i<100000; ++i) { + _s = h2.value(keys[i]); + } + el = tm.elapsed_m(); piCout << el << h2.capacity(); + + tm.reset(); + for (int i=0; i<100000; ++i) { + _s = m2.value(keys[i]); + } + el = tm.elapsed_m(); piCout << el; + //################################ + CDPultWindow w; + w.show(); + if (a.arguments().size() > 1) + w.loadFile(a.arguments()[1]); + return a.exec(); +} diff --git a/test/qcd_utils/qcd_core.cpp b/test/qcd_utils/qcd_core.cpp new file mode 100644 index 0000000..c77e70e --- /dev/null +++ b/test/qcd_utils/qcd_core.cpp @@ -0,0 +1,297 @@ +#include "qcd_core.h" +#include "cdutils_k.h" +#include "cdutils_core.h" +#include "piqt.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace CDUtils; + + +int __QCore_Initializer__::count_(0); +QCDCore * __QCore_Initializer__::__instance__(0); + + +__QCore_Initializer__::__QCore_Initializer__() { + count_++; + if (count_ > 1) return; + __instance__ = new QCDCore(); +} + + +__QCore_Initializer__::~__QCore_Initializer__() { + count_--; + if (count_ > 0) return; + if (__instance__ != 0) { + delete __instance__; + __instance__ = 0; + } +} + + + + +QCDCore::QCDCore() { + setObjectName("QCDCore"); + setName("QCDCore"); + CONNECTU(&K, changedGlobal, this, K_ChangedGlobal); + updating = direct_on = false; +} + + +QCDCore::~QCDCore() { +} + + +void QCDCore::K_ChangedGlobal() { + QMetaObject::invokeMethod(this, "updateBindedWidgets", Qt::QueuedConnection); +} + + +void QCDCore::slotBool(bool v) { + QWidget * w = (QWidget*)sender(); + if (!w || updating) return; + QList > pathes = binded_widgets.values(w); + foreach (const PIDeque & path, pathes) + K[path].setValue(PIString::fromBool(v)); + K.calculate(); + emit updateViewRequest(); +} + + +void QCDCore::slotInt(int v) { + QWidget * w = (QWidget*)sender(); + if (!w || updating) return; + QList > pathes = binded_widgets.values(w); + foreach (const PIDeque & path, pathes) + K[path].setValue(PIString::fromNumber(v)); + finishEdit(pathes); +} + + +void QCDCore::slotDouble(double v) { + QWidget * w = (QWidget*)sender(); + if (!w || updating) return; + QList > pathes = binded_widgets.values(w); + foreach (const PIDeque & path, pathes) + K[path].setValue(PIString::fromNumber(v)); + finishEdit(pathes); +} + + +void QCDCore::slotText(QString v) { + QWidget * w = (QWidget*)sender(); + if (!w || updating) return; + QList > pathes = binded_widgets.values(w); + foreach (const PIDeque & path, pathes) + K[path].setValue(Q2PIString(v)); + finishEdit(pathes); +} + + +void QCDCore::slotVariant(QVariant v) { + QWidget * w = (QWidget*)sender(); + if (!w || updating) return; + QList > pathes = binded_widgets.values(w); + foreach (const PIDeque & path, pathes) + K[path].setVariantValue(Q2PIVariant(v)); + finishEdit(pathes); +} + + +void QCDCore::slotDestroyed(QObject * o) { + if (!o) return; + if (!binded_widgets.contains((QWidget*)o)) return; + binded_widgets.remove((QWidget*)o); +} + + +int QCDCore::bindWindow(QWidget * wnd) { + if (!wnd) return 0; + //K.root().makePath(); + return bindWidgets(wnd->findChildren()); +} + + +int QCDCore::bindWidgets(QList wl) { + int ret = 0; + foreach (QWidget * w, wl) + if (bindWidget(w)) ++ret; + return ret; +} + + +bool QCDCore::bindWidget(QWidget * w) { + if (!w) return false; + QString on = w->objectName(); + QString cn = w->metaObject()->className(); + if (cn == "CDView") { + bindView(w); + return false; + } + PIVector ak = K.root().children(); + piForeachC (CDType * k, ak) { + if (!on.endsWith(PI2QString(k->pathString().join("_")))) continue; + if (bindWidget(w, *k)) return true; + } + return false; +} + + +bool QCDCore::bindWidget(QWidget * w, const CDType & k) { + if (!w) return false; + //piCout << "bind..." << k.name() << k.path(); + QString cn = w->metaObject()->className(); + bool ok = false; + if (cn == "QCheckBox" || cn == "QGroupBox") { + connect(w, SIGNAL(toggled(bool)), this, SLOT(slotBool(bool)), Qt::UniqueConnection); + ok = true; + } + if (cn == "QSpinBox" || cn == "QSlider" || cn == "QScrollBar") { + connect(w, SIGNAL(valueChanged(int)), this, SLOT(slotInt(int)), Qt::UniqueConnection); + ok = true; + } + if (cn == "QDoubleSpinBox" || cn == "SpinSlider" || cn == "EvalSpinBox") { + connect(w, SIGNAL(valueChanged(double)), this, SLOT(slotDouble(double)), Qt::UniqueConnection); + ok = true; + } + if (cn == "QLineEdit" || cn == "CLineEdit") { + connect(w, SIGNAL(textChanged(QString)), this, SLOT(slotText(QString)), Qt::UniqueConnection); + ok = true; + } + if (cn == "QVariantEdit") { + connect(w, SIGNAL(valueChanged(QVariant)), this, SLOT(slotVariant(QVariant)), Qt::UniqueConnection); + ok = true; + } + if (cn == "CDView") { + bindView(w); + } + connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(slotDestroyed(QObject*)), Qt::UniqueConnection); + setWidgetValue(w, k); + if (!ok) return false; + //piCout << k.name() << k.path() << "ok"; + binded_widgets.insert(w, k.path()); + return true; +} + + +void QCDCore::updateBindedWidgets() { + QMapIterator > it(binded_widgets); + QWidgetList to_remove; + updating = true; + while (it.hasNext()) { + QWidget * w = it.next().key(); + if (!K.exists(it.value())) + to_remove << w; + else + setWidgetValue(w, K[it.value()]); + } + foreach (QWidget * w, to_remove) + unbindWidget(w); + updating = false; +} + + +void QCDCore::bindView(QWidget * v) { + CDView * w = qobject_cast(v); + if (!w) return; + connect(this, SIGNAL(updateViewRequest()), w, SLOT(refreshValues()), Qt::UniqueConnection); +} + + +void QCDCore::setWidgetValue(QWidget * w, const CDType & k) { + if (!w) return; + QString cn = w->metaObject()->className(); + if (cn == "QCheckBox") qobject_cast(w)->setChecked(k.toBool()); + if (cn == "QGroupBox") qobject_cast(w)->setChecked(k.toBool()); + if (cn == "QSpinBox") qobject_cast(w)->setValue(k.toInt()); + if (cn == "QSlider") qobject_cast(w)->setValue(k.toInt()); + if (cn == "QScrollBar") qobject_cast(w)->setValue(k.toInt()); + if (cn == "QDoubleSpinBox") qobject_cast(w)->setValue(k.toDouble()); + if (cn == "SpinSlider") qobject_cast(w)->setValue(k.toDouble()); + if (cn == "QLineEdit") qobject_cast(w)->setText(PI2QString(k.value())); + if (cn == "CLineEdit") qobject_cast(w)->setText(PI2QString(k.value())); + if (cn == "EvalSpinBox") qobject_cast(w)->setValue(k.toDouble()); + if (cn == "QVariantEdit") qobject_cast(w)->setValue(PI2QVariant(k.variantValue())); +} + + +void QCDCore::finishEdit(const QList > & pathes) { + K.calculate(); + if (direct_on) { + foreach (const PIDeque & path, pathes) + K.directChange(K[path]); + } + emit updateViewRequest(); +} + + +int QCDCore::unbindWindow(QWidget * wnd) { + if (!wnd) return 0; + return unbindWidgets(wnd->findChildren()); +} + + +int QCDCore::unbindWidgets(QList wl) { + int ret = 0; + foreach (QWidget * w, wl) + if (unbindWidget(w)) ++ret; + return ret; +} + + +bool QCDCore::unbindWidget(QWidget * w) { + if (!w) return false; + //qDebug() << "unbind" << w; + if (!binded_widgets.contains(w)) return false; + QString cn = w->metaObject()->className(); + if (cn == "QCheckBox" || cn == "QGroupBox") + disconnect(w, SIGNAL(toggled(bool)), this, SLOT(slotBool(bool))); + if (cn == "QSpinBox" || cn == "QSlider" || cn == "QScrollBar") + disconnect(w, SIGNAL(valueChanged(int)), this, SLOT(slotInt(int))); + if (cn == "QDoubleSpinBox" || cn == "SpinSlider" || cn == "EvalSpinBox") + disconnect(w, SIGNAL(valueChanged(double)), this, SLOT(slotDouble(double))); + if (cn == "QLineEdit" || cn == "CLineEdit") + disconnect(w, SIGNAL(textChanged(QString)), this, SLOT(slotText(QString))); + if (cn == "QVariantEdit") + disconnect(w, SIGNAL(valueChanged(QVariant)), this, SLOT(slotVariant(QVariant))); + //qDebug() << "remove b" << binded_widgets.size(); + binded_widgets.remove(w); + //qDebug() << "remove a" << binded_widgets.size(); + return true; +} + + +void QCDCore::unbindAllWidgets() { + QMap > bwm = binded_widgets; + QMapIterator > it(bwm); + while (it.hasNext()) { + QWidget * w = it.next().key(); + unbindWidget(w); + } + binded_widgets.clear(); +} + + +void QCDCore::updateBindedWidget(const CDType & k_) { + QMapIterator > it(binded_widgets); + updating = true; + while (it.hasNext()) { + QWidget * w = it.next().key(); + const CDType & k(K[it.value()]); + if (k.path() != k_.path()) continue; + setWidgetValue(w, k); + } + updating = false; +} diff --git a/test/qcd_utils/qcd_core.h b/test/qcd_utils/qcd_core.h new file mode 100644 index 0000000..84d38bb --- /dev/null +++ b/test/qcd_utils/qcd_core.h @@ -0,0 +1,77 @@ +#ifndef QCD_CORE_H +#define QCD_CORE_H + +#include +#include +#include +#include "piobject.h" +#include "cdutils_types.h" + + +class QCDCore; + + +class __QCore_Initializer__ { +public: + __QCore_Initializer__(); + ~__QCore_Initializer__(); + static int count_; + static QCDCore * __instance__; +}; + + +class QCDCore: public QObject, public PIObject +{ + Q_OBJECT + PIOBJECT(QCDCore) + friend class __QCore_Initializer__; +public: + static QCDCore * instance() {return __QCore_Initializer__::__instance__;} + + int bindWindow(QWidget * wnd); + int bindWidgets(QList wl); + bool bindWidget(QWidget * w); + bool bindWidget(QWidget * w, const CDUtils::CDType & k); + + int unbindWindow(QWidget * wnd); + int unbindWidgets(QList wl); + bool unbindWidget(QWidget * w); + void unbindAllWidgets(); + + void updateBindedWidget(const CDUtils::CDType & k_); + void setDirectKEnabled(bool yes) {direct_on = yes;} + bool isDirectKEnabled() const {return direct_on;} + +private: + QCDCore(); + ~QCDCore(); + + void bindView(QWidget * v); + void setWidgetValue(QWidget * w, const CDUtils::CDType & k); + void finishEdit(const QList > & pathes); + EVENT_HANDLER(void, K_ChangedGlobal); + + QMultiMap > binded_widgets; + bool updating, direct_on; + +private slots: + void slotBool(bool v); + void slotInt(int v); + void slotDouble(double v); + void slotText(QString v); + void slotVariant(QVariant v); + void slotDestroyed(QObject * ); + +public slots: + void updateBindedWidgets(); + +signals: + void updateViewRequest(); + +}; + + +static __QCore_Initializer__ __QCore_initializer__; + + +#endif // QCD_CORE_H diff --git a/test/qcd_utils/qcd_graphic.cpp b/test/qcd_utils/qcd_graphic.cpp new file mode 100644 index 0000000..5583495 --- /dev/null +++ b/test/qcd_utils/qcd_graphic.cpp @@ -0,0 +1,30 @@ +#include "qcd_graphic.h" +#include "ui_qcd_graphic.h" +#include "cdutils_core.h" +#include "cdutils_x.h" +#include "graphic.h" +#include "piqt.h" + +using namespace CDUtils; + + +CDGraphicWidget::CDGraphicWidget(QWidget * p): QWidget(p) { + ui = new Ui::CDGraphicWidget(); + ui->setupUi(this); + ui->graphic->setGraphicsCount(0); +} + + +Graphic * CDGraphicWidget::graphic() const { + return ui->graphic; +} + + +void CDGraphicWidget::setConfigVisible(bool on) { + ui->widgetConfig->setVisible(on); +} + + +bool CDGraphicWidget::isConfigVisible() const { + return ui->widgetConfig->isVisible(); +} diff --git a/test/qcd_utils/qcd_graphic.h b/test/qcd_utils/qcd_graphic.h new file mode 100644 index 0000000..d082fea --- /dev/null +++ b/test/qcd_utils/qcd_graphic.h @@ -0,0 +1,42 @@ +#ifndef QCD_GRAPHIC_H +#define QCD_GRAPHIC_H + +#include +#include + +namespace CDUtils { + class CDType; + class CDSection; +} + +namespace Ui { + class CDGraphicWidget; +} + +class Graphic; + + +class CDGraphicWidget: public QWidget { + Q_OBJECT + friend class CDGraphics; + friend class GDockWidget; +public: + CDGraphicWidget(QWidget * p = 0); + + Graphic * graphic() const; + bool isConfigVisible() const; + +public slots: + void setConfigVisible(bool on); + +private: + Ui::CDGraphicWidget * ui; + +private slots: + +signals: + +}; + + +#endif // QCD_GRAPHIC_H diff --git a/test/qcd_utils/qcd_graphic.ui b/test/qcd_utils/qcd_graphic.ui new file mode 100644 index 0000000..511ed61 --- /dev/null +++ b/test/qcd_utils/qcd_graphic.ui @@ -0,0 +1,169 @@ + + + CDGraphicWidget + + + + 0 + 0 + 470 + 380 + + + + CD Pult + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Graphic::Autofit|Graphic::BorderInputs|Graphic::Clear|Graphic::Configure|Graphic::CursorAxis|Graphic::Fullscreen|Graphic::Legend|Graphic::OnlyExpandY|Graphic::Pause|Graphic::Save + + + false + + + false + + + 100.000000000000000 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + History: + + + + + + + 100.000000000000000 + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + Visible: + + + + + + + -1.000000000000000 + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + + + Graphic + QFrame +
graphic.h
+
+ + EvalSpinBox + QWidget +
evalspinbox.h
+
+
+ + + + evalHistory + valueChanged(double) + graphic + setHistorySize(double) + + + 148 + 363 + + + 156 + 297 + + + + + evalVisible + valueChanged(double) + graphic + setMaxVisibleTime(double) + + + 345 + 361 + + + 342 + 337 + + + + +
diff --git a/test/qcd_utils/qcd_modedialog.cpp b/test/qcd_utils/qcd_modedialog.cpp new file mode 100644 index 0000000..9612588 --- /dev/null +++ b/test/qcd_utils/qcd_modedialog.cpp @@ -0,0 +1,46 @@ +#include "qcd_modedialog.h" +#include "ui_qcd_modedialog.h" + + +QCDModeDialog::QCDModeDialog(QWidget * parent): QDialog(parent) { + ui = new Ui::QCDModeDialog(); + ui->setupUi(this); +} + + +QCDModeDialog::~QCDModeDialog() { + delete ui; +} + + +CDUtils::UpdateModeFlags QCDModeDialog::mode() const { + CDUtils::UpdateModeFlags ret = 0; + if (ui->checkSaveIndex->isChecked()) ret |= CDUtils::SaveByIndex; + if (ui->checkSaveName->isChecked()) ret |= CDUtils::SaveByName; + if (ui->checkMerge->isChecked()) ret |= CDUtils::Merge; + return ret; +} + + +void QCDModeDialog::changeEvent(QEvent *e) { + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void QCDModeDialog::on_checkSaveIndex_clicked(bool checked) { + if (!checked) return; + ui->checkSaveName->setChecked(false); +} + + +void QCDModeDialog::on_checkSaveName_clicked(bool checked) { + if (!checked) return; + ui->checkSaveIndex->setChecked(false); +} diff --git a/test/qcd_utils/qcd_modedialog.h b/test/qcd_utils/qcd_modedialog.h new file mode 100644 index 0000000..702cd20 --- /dev/null +++ b/test/qcd_utils/qcd_modedialog.h @@ -0,0 +1,31 @@ +#ifndef QCD_MODEDIALOG_H +#define QCD_MODEDIALOG_H + +#include +#include + +namespace Ui { + class QCDModeDialog; +} + +class QCDModeDialog: public QDialog +{ + Q_OBJECT +public: + explicit QCDModeDialog(QWidget * parent = 0); + ~QCDModeDialog(); + + CDUtils::UpdateModeFlags mode() const; + +protected: + void changeEvent(QEvent *e); + + Ui::QCDModeDialog * ui; + +private slots: + void on_checkSaveIndex_clicked(bool checked); + void on_checkSaveName_clicked(bool checked); + +}; + +#endif // QCD_MODEDIALOG_H diff --git a/test/qcd_utils/qcd_modedialog.ui b/test/qcd_utils/qcd_modedialog.ui new file mode 100644 index 0000000..0c3d1dd --- /dev/null +++ b/test/qcd_utils/qcd_modedialog.ui @@ -0,0 +1,98 @@ + + + QCDModeDialog + + + + 0 + 0 + 251 + 152 + + + + Update description mode + + + + + + Save by index + + + + + + + Save by name + + + + + + + Merge + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QCDModeDialog + accept() + + + 106 + 131 + + + 101 + 146 + + + + + buttonBox + rejected() + QCDModeDialog + reject() + + + 182 + 127 + + + 169 + 146 + + + + + diff --git a/test/qcd_utils/qcd_model.cpp b/test/qcd_utils/qcd_model.cpp new file mode 100644 index 0000000..f9264d1 --- /dev/null +++ b/test/qcd_utils/qcd_model.cpp @@ -0,0 +1,460 @@ +#include "qcd_model.h" +#include "cdutils_interface.h" +#include "cdutils_core.h" +#include "cdutils_x.h" +#include "piqt.h" +#include +#include +#include +#include +#include "qvariantedit.h" +#include "qad_types.h" + + +using namespace CDUtils; + +// CDKItem + +CDItem::CDItem(CDUtils::Interface * i, int _index, CDItem::CDItemType type, CDItem *parent) { + interface = i; + index_ = _index; + parent_ = parent; + type_ = type; + item_count = 0; + expanded = true; +} + + +CDItem::~CDItem() { + qDeleteAll(childs); +} + + +QVariant CDItem::data(int column, int role) const { + if (role == Qt::BackgroundRole) { + switch (type_) { + case ItemCDType: { + CDType & t(interface->section(buildPath())[index_]); + if (t.errorString().isEmpty()) return QBrush(QColor(255, 250, 230)); + else return QBrush(QColor(255, 128, 128)); + } + case ItemCDSection: return QBrush(QColor(230, 250, 230)); + } + } + if (role == Qt::CheckStateRole && type_ == ItemCDType) { + CDType & t(interface->section(buildPath())[index_]); + if (column == cValue && t.cd_type() == CDType::cdK) { + if (t.type() == "b") return t.toBool() ? Qt::Checked : Qt::Unchecked; + else QVariant(); + } + if (column == cName_Cmd && t.cd_type() == CDType::cdX) { + return t.isSelectedX() ? Qt::Checked : Qt::Unchecked; + } + } + if (role == Qt::ToolTipRole && type_ == ItemCDType) { + CDType & t(interface->section(buildPath())[index_]); + return PI2QString(t.errorString()); + } + if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); + PIDeque path = buildPath(); + CDSection & rs = interface->section(path); + CDSection s; + switch (type_) { + case ItemCDType: + switch (column) { + case cID: return QString::number(index_); + case cName_Cmd: return PI2QString(rs[index_].name()); + case cType: return stringType(rs[index_].type()); + case cXMode: return QVariant::fromValue(xModeEnum(rs[index_].xmode())); + case cXAvg: return rs[index_].avg(); + case cExpression: return PI2QString(rs[index_].formula()); + case cValue: return value(rs[index_], role); + case cComment: return PI2QString(rs[index_].comment()); + default: break; + } + break; + case ItemCDSection: + s = rs.section(index_); +// piCout << rs.name << rs.alias << s.name << s.alias; + switch (column) { + case cID: return QString("[") + QString::number(index_) + QString("]"); + case cName_Cmd: return PI2QString(s.alias); + case cType: return PI2QString(s.name); + default: break; + } + break; + } + return QVariant(); +} + + +QVariant CDItem::value(CDType & t, int role) const { + if (t.type() == "f") return t.toDouble(); + if (t.type() == "n") return t.toInt(); + if (t.type() == "b") return t.toBool(); + if (t.type() == "e") { + QAD::Enum et = PI2QADEnum(t.enumValues()); + et.selectValue(t.toInt()); + if (role == Qt::EditRole) return QVariant::fromValue(et); + else return et.selectedName(); + } + return PI2QString(t.value()); +} + + +bool CDItem::setData(int column, const QVariant & value) { + if (type_ == ItemCDType) { + CDType & t(interface->section(buildPath())[index_]); + if ((column == cExpression || column == cValue) && (t.cd_type() == CDType::cdK)) { + interface->section(buildPath())[index_].setValue(Q2PIString(value.toString())); + interface->calculate(); + return true; + } + if (t.cd_type() == CDType::cdX) { + switch (column) { + case cName_Cmd: + X.setEnabled(t, value.toBool()); + return true; + case cXMode: + t.setXMode((CDType::XMode)value.toInt()); + return true; + case cXAvg: + t.setAvg(piMax(value.toInt(), 1)); + return true; + default: break; + } + } + } + return false; +} + + +PIDeque CDItem::buildPath() const { + PIDeque path; + CDItem * p = parent_; + while (p) { + path.push_front(p->index_); + p = p->parent_; + } + path.take_front(); + return path; +} + + +QString CDItem::stringType(const PIString & t) const { + QString n = PI2QString(t); + if (n.isEmpty()) return QString(""); + switch (n[0].toLatin1()) { + case 'l': return QString("list"); break; + case 'b': return QString("bool"); break; + case 'n': return QString("int"); break; + case 'f': return QString("double"); break; + case 'c': return QString("color"); break; + case 'r': return QString("rect"); break; + case 'a': return QString("rect"); break; + case 'p': return QString("point"); break; + case 'v': return QString("vector"); break; + case 'i': return QString("IP"); break; + case 'e': return QString("enum"); break; + case 'F': return QString("file"); break; + case 'D': return QString("dir"); break; + } + return QString("string"); +} + + +QAD::Enum CDItem::xModeEnum(int v) const { + QAD::Enum ret; + ret << QAD::Enumerator(CDType::X_Current, "Current") + << QAD::Enumerator(CDType::X_All_Avg, "All, Averaging"); + ret.selectValue(v); + return ret; +} + + +// CDKDelegate + +CDDelegate::CDDelegate(QObject *parent) : QStyledItemDelegate(parent) { +} + + +void CDDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + CDItem * item = ((CDItemModel*)index.model())->getItem(index); + if (item) { + if (item->itemType() == CDItem::ItemCDType && item->interface->cdType() == CDType::cdC) { + QStyleOptionButton bo; + bo.direction = option.direction; + bo.fontMetrics = option.fontMetrics; + bo.palette = option.palette; + bo.rect = option.rect; + bo.state = option.state;// & ~(QStyle::State_HasFocus | QStyle::State_MouseOver); + bo.text = item->data(1, Qt::DisplayRole).toString(); + QWidget * v = (QWidget*)(painter->device()); + if (v) { + QPoint cp = v->mapFromGlobal(QCursor::pos()); + if (bo.rect.contains(cp, true)) { + //bo.state |= QStyle::State_MouseOver; + if (qApp->mouseButtons().testFlag(Qt::LeftButton)) + bo.state |= QStyle::State_On; + } + } + qApp->style()->drawControl(QStyle::CE_PushButton, &bo, painter); + return; + } + } + QStyledItemDelegate::paint(painter, option, index); + +} + + +QWidget * CDDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { + return new QVariantEdit(parent); +} + + +void CDDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + QVariantEdit *edit = static_cast(editor); + edit->setValue(index.model()->data(index, Qt::EditRole)); +} + + +void CDDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + QVariantEdit *edit = static_cast(editor); + QVariant v = edit->value(); + if (v.canConvert()) { + QAD::Enum et = v.value(); + model->setData(index, et.selectedValue(), Qt::EditRole); + } else model->setData(index, v, Qt::EditRole); +} + + +void CDDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { + editor->setGeometry(option.rect); +} + + +QSize CDDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { + QSize s = QStyledItemDelegate::sizeHint(option, index); + s.setWidth(s.width() + 20); + return s; +} + + +// CDItemModel + +CDItemModel::CDItemModel(int type_, QObject *parent) : QAbstractItemModel(parent) { + interface = new Interface((CDType::cdT)type_); + root = 0; + internalRebuild(); +} + + +CDItemModel::~CDItemModel() { + delete root; + delete interface; +} + + +QVariant CDItemModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) return QVariant(); + CDItem * item = getItem(index); + return item->data(index.column(), role); +} + + +QVariant CDItemModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch (section) { + case cID: return trUtf8("Index"); + case cName_Cmd: return interface->cdType() == CDType::cdC ? trUtf8("Command") : trUtf8("Name"); + case cType: return trUtf8("Type"); + case cXMode: return trUtf8("Mode"); + case cXAvg: return trUtf8("Averaging"); + case cExpression: return trUtf8("Expression"); + case cValue: return trUtf8("Value"); + case cComment: return trUtf8("Comment"); + } + } + return QVariant(); +} + + +QModelIndex CDItemModel::index(int row, int column, const QModelIndex &parent) const { + if (parent.isValid() && parent.column() != cID) return QModelIndex(); + CDItem * p = getItem(parent); + CDItem * c = p->childs.value(row, 0); + if (c) return createIndex(row, column, c); + else return QModelIndex(); +} + + +QModelIndex CDItemModel::parent(const QModelIndex &index) const { + if (!index.isValid()) return QModelIndex(); + CDItem * c = getItem(index); + CDItem * p = c->parent_; + if (p == root) return QModelIndex(); + return createIndex(p->parent_->childs.indexOf(p), cID, p); +} + + +int CDItemModel::rowCount(const QModelIndex &parent) const { + CDItem * p = getItem(parent); + return p->childs.count(); +} + + +int CDItemModel::columnCount(const QModelIndex &parent) const { + return cLastColumn; +} + + +Qt::ItemFlags CDItemModel::flags(const QModelIndex & index) const { + if (!index.isValid()) return 0; + Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + CDItem * item = getItem(index); + if (!item) return 0; + if (item->type_ == CDItem::ItemCDType) { + CDType & t(interface->section(item->buildPath())[item->index_]); + if (t.cd_type() == CDType::cdK) { + if (index.column() == cExpression || index.column() == cValue) + f |= Qt::ItemIsEditable; + if (index.column() == cValue && t.type() == "b") + f |= Qt::ItemIsUserCheckable; + if (index.column() == cName_Cmd) + f |= Qt::ItemIsDragEnabled; + } + if (t.cd_type() == CDType::cdX) { + if (index.column() == cXMode || index.column() == cXAvg) + f |= Qt::ItemIsEditable; + if (index.column() == cName_Cmd) + f |= Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; + } + } + return f; +} + + +bool CDItemModel::setData(const QModelIndex & index, const QVariant & value, int role) { + if (role == Qt::CheckStateRole && (index.column() == cName_Cmd || index.column() == cValue)) { + CDItem * item = getItem(index); + if (item->type_ == CDItem::ItemCDType) { + CDType & t(interface->section(item->buildPath())[item->index_]); + if (index.column() == cValue && (t.cd_type() == CDType::cdK)) { + if (t.type() == "b") { + bool result = item->setData(index.column(), PI2QString(PIString::fromBool(value.toBool()))); + QModelIndex rin(CDItemModel::index(index.row(), cExpression, index.parent())); + emit dataChanged(rin, rin); + return result; + } + } + if (index.column() == cName_Cmd && (t.cd_type() == CDType::cdX)) { + bool result = item->setData(index.column(), value); + //QModelIndex rin(CDItemModel::index(index.row(), 1, index.parent())); + //emit dataChanged(rin, rin); + return result; + } + } + } + if (role != Qt::EditRole) return false; + CDItem * item = getItem(index); + bool result = item->setData(index.column(), value); + if (result) { + QModelIndex rin(CDItemModel::index(index.row(), cExpression, index.parent())); + emit dataChanged(rin, rin); + emit dataChanged(index, index); + } + return result; +} + + +QMimeData * CDItemModel::mimeData(const QModelIndexList & indexes) const { + if (indexes.size() == 1) { + QModelIndex index = indexes[0]; + if (index.isValid()/* && interface->cdType() == CDType::cdX*/) { + CDItem * item = getItem(index); + if (item) { + CDType & t(interface->section(item->buildPath())[item->index_]); + QMimeData * mime = new QMimeData(); + mime->setText(PI2QString(CDCore::instance()->typeLetter(interface->cdType()) + + CDCore::pathToString(t.path()))); + return mime; + } + } + } + return QAbstractItemModel::mimeData(indexes); +} + + +void CDItemModel::rebuildModel() { + beginResetModel(); + internalRebuild(); + endResetModel(); +} + + +void CDItemModel::buildItem(CDItem * it, CDSection & r) { + //piCout << "build item" << r.name << r.alias; + PIMap::iterator i; + for (i = r.cd.begin(); i != r.cd.end(); ++i) { + it->childs << new CDItem(interface, i.key(), CDItem::ItemCDType, it); + } + it->item_count = it->childs.size(); + PIMap::iterator j; + for (j = r.s.begin(); j != r.s.end(); ++j) { + it->childs << new CDItem(interface, j.key(), CDItem::ItemCDSection, it); + buildItem(it->childs.back(), j.value()); + } +} + + +void CDItemModel::updateModel() { + beginResetModel(); + endResetModel(); +} + + +void CDItemModel::internalRebuild() { + //qDebug() << "[CDKItemModel]" << "internalRebuild()"; + if (root) delete root; + root = new CDItem(interface, 0, CDItem::ItemCDSection, 0); + CDSection & r = interface->root(); + buildItem(root, r); +} + + +CDItem * CDItemModel::getItem(const QModelIndex &index) const { + if (index.isValid()) { + CDItem * item = static_cast(index.internalPointer()); + if (item) return item; + } + return root; +} + + +QModelIndex CDItemModel::indexByPath(const PIDeque & path, int column) const { + if (path.isEmpty()) return QModelIndex(); + CDItem * item = root; + //piCout << path << "..."; + bool ok = false; + for (int i = 0; i < path.size_s() - 1; ++i) { + ok = false; + foreach (CDItem * j, item->childs) + if (j->type_ == CDItem::ItemCDSection && j->index_ == path[i]) { + item = j; + ok = true; + break; + } + if (!ok) return QModelIndex(); + } + ok = false; + foreach (CDItem * j, item->childs) + if (j->type_ == CDItem::ItemCDType && j->index_ == path.back()) { + item = j; + ok = true; + break; + } + if (!ok || !item->parent_) return QModelIndex(); + QModelIndex ret = createIndex(item->parent_->childs.indexOf(item), column, item); + //piCout << path << Q2PIString(item->data(cName_Cmd, Qt::DisplayRole).toString()) << getItem(ret)->buildPath(); + return ret; +} diff --git a/test/qcd_utils/qcd_model.h b/test/qcd_utils/qcd_model.h new file mode 100644 index 0000000..4021015 --- /dev/null +++ b/test/qcd_utils/qcd_model.h @@ -0,0 +1,113 @@ +#ifndef QCD_MODEL_H +#define QCD_MODEL_H + +#include +#include +#include +#include "pistring.h" + +namespace CDUtils { + class CDType; + class CDSection; + class Interface; + + enum Column { + cID , + cName_Cmd , + cType , + cXMode , + cXAvg , + cExpression, + cValue , + cComment , + cLastColumn, + }; +} + +namespace QAD { + struct Enum; +} + +class CDItemModel; + + +class CDItem { + friend class CDItemModel; + friend class CDView; +public: + enum CDItemType{ItemCDType, ItemCDSection}; + CDItem(CDUtils::Interface * interface, int _index, CDItemType type, CDItem * parent); + ~CDItem(); + QVariant data(int column, int role) const; + QVariant value(CDUtils::CDType & t, int role) const; + bool setData(int column, const QVariant & value); + CDItemType itemType() const {return type_;} + PIDeque buildPath() const; + int index() const {return index_;} + + CDUtils::Interface * interface; + bool expanded; + +private: + QString stringType(const PIString & t) const; + QAD::Enum xModeEnum(int v) const; + + CDItem * parent_; + int index_, item_count; + CDItemType type_; + QList childs; +}; + + +class CDDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + CDDelegate(QObject *parent = 0); + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; + QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + +}; + + +class CDItemModel : public QAbstractItemModel { + Q_OBJECT + friend class CDView; +public: + explicit CDItemModel(int type_, QObject *parent = 0); + ~CDItemModel(); + + QVariant data(const QModelIndex & index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QMimeData * mimeData(const QModelIndexList & indexes) const; + CDItem * getItem(const QModelIndex & index) const; + QModelIndex indexByPath(const PIDeque & path, int column = CDUtils::cID) const; + + void buildItem(CDItem * it, CDUtils::CDSection &r); + +public slots: + void rebuildModel(); + void updateModel(); + +private: + void internalRebuild(); + + CDUtils::Interface * interface; + CDItem * root; + +signals: + +}; + +#endif // QCD_MODEL_H diff --git a/test/qcd_utils/qcd_view.cpp b/test/qcd_utils/qcd_view.cpp new file mode 100644 index 0000000..1edec79 --- /dev/null +++ b/test/qcd_utils/qcd_view.cpp @@ -0,0 +1,353 @@ +#include +#include +#include "cdutils_k.h" +#include "cdutils_x.h" +#include "cdutils_c.h" +#include "cdutils_m.h" +#include "cdutils_core.h" +#include "qcd_view.h" +#include "qcd_model.h" +#include "piqt.h" +#include "pifile.h" + +using namespace CDUtils; + + +CDView::CDView(QWidget * parent) : QTreeView(parent) { + type_ = -1; + model_ = 0; + connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(indexClicked(QModelIndex))); + connect(this, SIGNAL(_qcd_sendFailed()), this, SLOT(cd_sendFailed()), Qt::QueuedConnection); + connect(this, SIGNAL(_qcd_sendSucceed()), this, SLOT(cd_sendSucceed()), Qt::QueuedConnection); + connect(this, SIGNAL(_qcd_receiveFailed()), this, SLOT(cd_receiveFailed()), Qt::QueuedConnection); + connect(this, SIGNAL(_qcd_receiveSucceed()), this, SLOT(cd_receiveSucceed()), Qt::QueuedConnection); + connect(this, SIGNAL(_qcd_receivedX()), this, SLOT(cd_receivedX()), Qt::QueuedConnection); + connect(this, SIGNAL(_qcd_changedGlobal()), this, SLOT(cd_changedGlobal()), Qt::QueuedConnection); +} + + +CDView::~CDView() { + if (model_) delete model_; + model_ = 0; +} + + +void CDView::setType(int cdt) { + if (cdt < 0) return; + if (type_ >= 0) return; + type_ = cdt; + switch ((CDType::cdT)type_) { + case CDType::cdK: + CONNECTU(&K, sended, this, pi_cd_sendSucceed); + CONNECTU(&K, sendFailed, this, pi_cd_sendFailed); + CONNECTU(&K, received, this, pi_cd_receiveSucceed); + CONNECTU(&K, receiveFailed, this, pi_cd_receiveFailed); + CONNECTU(&K, changedGlobal, this, pi_cd_changedGlobal); + break; + case CDType::cdX: + CONNECTU(&X, sended, this, pi_cd_sendSucceed); + CONNECTU(&X, sendFailed, this, pi_cd_sendFailed); + CONNECTU(&X, received, this, pi_cd_receiveSucceed); + CONNECTU(&X, receiveFailed, this, pi_cd_receiveFailed); + CONNECTU(&X, receivedX, this, pi_cd_receivedX); + CONNECTU(&X, changedGlobal, this, pi_cd_changedGlobal); + break; + case CDType::cdC: + CONNECTU(&C, sended, this, pi_cd_sendSucceed); + CONNECTU(&C, sendFailed, this, pi_cd_sendFailed); + CONNECTU(&C, received, this, pi_cd_receiveSucceed); + CONNECTU(&C, receiveFailed, this, pi_cd_receiveFailed); + CONNECTU(&C, changedGlobal, this, pi_cd_changedGlobal); + break; + case CDType::cdM: + CONNECTU(&M, sended, this, pi_cd_sendSucceed); + CONNECTU(&M, sendFailed, this, pi_cd_sendFailed); + CONNECTU(&M, received, this, pi_cd_receiveSucceed); + CONNECTU(&M, receiveFailed, this, pi_cd_receiveFailed); + CONNECTU(&M, changedGlobal, this, pi_cd_changedGlobal); + CONNECTU(&M, messageReceived, this, pi_cd_messageReceived); + break; + default: break; + } + +} + + +void CDView::mousePressEvent(QMouseEvent * e) { + if (type_ == CDType::cdC) { + QModelIndex i = indexAt(e->pos()); + if (i.isValid() && i.column() == cName_Cmd) + update(i); + } + QTreeView::mousePressEvent(e); +} + + +void CDView::mouseReleaseEvent(QMouseEvent * e) { + if (type_ == CDType::cdC) { + QModelIndex i = indexAt(e->pos()); + if (i.isValid() && i.column() == cName_Cmd) + update(i); + } + QTreeView::mouseReleaseEvent(e); +} + + +void CDView::currentChanged(const QModelIndex & cur, const QModelIndex & prev) { + if (type_ == CDType::cdC) { + if (prev.isValid() && prev.column() == cName_Cmd) + update(prev); + } + QTreeView::currentChanged(cur, prev); +} + + +void CDView::refresh() { + if (type_ < 0) return; + if (!model_) { + model_ = new CDItemModel(type_); + setModel(model_); + setItemDelegateForColumn(type_ == CDType::cdC ? cName_Cmd : cValue, new CDDelegate()); + if (type_ == CDType::cdX) + setItemDelegateForColumn(cXMode, new CDDelegate()); + } + model_->rebuildModel(); + switch ((CDType::cdT)type_) { + case CDType::cdK: + setColumnHidden(cXMode, true); + setColumnHidden(cXAvg, true); + break; + case CDType::cdX: + setColumnHidden(cExpression, true); + break; + case CDType::cdC: + case CDType::cdM: + setColumnHidden(cType, true); + setColumnHidden(cXMode, true); + setColumnHidden(cXAvg, true); + setColumnHidden(cExpression, true); + setColumnHidden(cValue, true); + break; + default: break; + } + expandAll(); + for (int i = 0; i < model_->columnCount(); i++) resizeColumnToContents(i); +} + + +void CDView::refreshValues() { + if (!model_) return; + model_->dataChanged(model_->index(0, 0), model_->index(model_->columnCount() - 1, model_->rowCount() - 1)); +} + + +void CDView::setFile(const QString & filename) { + switch ((CDType::cdT)type_) { + case CDType::cdK: K.setFileName(Q2PIString(filename)); break; + case CDType::cdX: X.setFileName(Q2PIString(filename)); break; + case CDType::cdC: C.setFileName(Q2PIString(filename)); break; + case CDType::cdM: M.setFileName(Q2PIString(filename)); break; + default: break; + } +} + + +bool CDView::inProgress() const { + switch ((CDType::cdT)type_) { + case CDType::cdK: return K.inProgress(); break; + case CDType::cdX: return X.inProgress(); break; + case CDType::cdC: return C.inProgress(); break; + case CDType::cdM: return M.inProgress(); break; + default: break; + } + return false; +} + + +void CDView::startX(double freq) { + switch ((CDType::cdT)type_) { + case CDType::cdX: X.start(freq); break; + default: break; + } +} + + +CDSection * CDView::root() { + return CDCore::instance()->root((CDType::cdT)type_); +} + + +QString CDView::typeLetter() const { + return PI2QString(CDCore::instance()->typeLetter((CDType::cdT)type_)); +} + + +void CDView::send() { + busyStatusChanged(true); + switch ((CDType::cdT)type_) { + case CDType::cdK: K.send(); break; + case CDType::cdX: X.send(); break; + case CDType::cdC: C.send(); break; + case CDType::cdM: M.send(); break; + default: break; + } +} + + +void CDView::receive() { + busyStatusChanged(true); + switch ((CDType::cdT)type_) { + case CDType::cdK: K.request(); break; + case CDType::cdX: X.request(); break; + case CDType::cdC: C.request(); break; + case CDType::cdM: M.request(); break; + default: break; + } +} + + +void CDView::save() { + switch ((CDType::cdT)type_) { + case CDType::cdK: K.writeFile(); break; + case CDType::cdX: X.writeFile(); break; + case CDType::cdC: C.writeFile(); break; + case CDType::cdM: M.writeFile(); break; + default: break; + } +} + + +void CDView::load() { + switch ((CDType::cdT)type_) { + case CDType::cdK: + K.readFile(); + K.calculate(); + break; + case CDType::cdX: + X.readFile(); + X.calculate(); + break; + case CDType::cdC: + C.readFile(); + C.calculate(); + break; + case CDType::cdM: + M.readFile(); + M.calculate(); + break; + default: break; + } + refresh(); +} + + +void CDView::clear() { + //piCout << "clearK"; + switch ((CDType::cdT)type_) { + case CDType::cdK: K.root() = CDSection(); break; + case CDType::cdX: X.root() = CDSection(); break; + case CDType::cdC: C.root() = CDSection(); break; + case CDType::cdM: M.root() = CDSection(); break; + default: break; + } + refresh(); +} + + +void CDView::buildFromHeader(const QString & description, int mode) { + if (description.isEmpty()) return; + PIString desc_file = Q2PIString(QDir::current().relativeFilePath(description)); + PIFile f(desc_file, PIIODevice::ReadOnly); + switch ((CDType::cdT)type_) { + case CDType::cdK: K.update(&f, mode); break; + case CDType::cdX: X.update(&f, mode); break; + case CDType::cdC: C.update(&f, mode); break; + case CDType::cdM: M.update(&f, mode); break; + default: break; + } + refresh(); +} + + +void CDView::calculate() { + switch ((CDType::cdT)type_) { + case CDType::cdK: K.calculate(); break; + case CDType::cdX: X.calculate(); break; + case CDType::cdC: C.calculate(); break; + case CDType::cdM: M.calculate(); break; + default: break; + } +} + + +void CDView::indexClicked(const QModelIndex & i) { + if (!model_ || !i.isValid() || type_ != CDType::cdC || i.column() != cName_Cmd) return; + CDItem * item = model_->getItem(i); + if (!item) return; + if (item->itemType() != CDItem::ItemCDType) return; + CDType & t(model_->interface->section(item->buildPath())[item->index()]); + C.sendCommand(t); + emit commandSended(PI2QString(t.pathString().join("."))); + //piCout << t; + qDebug() << PI2QString(t.pathString().join(".")); +} + + +void CDView::cd_sendFailed() { + busyStatusChanged(false); + emit messageStatus("send failed"); + emit sendFailed(); +} + + +void CDView::cd_sendSucceed() { + busyStatusChanged(false); + emit messageStatus("send success"); + emit sendSucceed(); +} + + +void CDView::cd_receiveFailed() { + busyStatusChanged(false); + emit messageStatus("receive failed"); + emit receiveFailed(); +} + + +void CDView::cd_receiveSucceed() { + refresh(); + busyStatusChanged(false); + emit messageStatus("receive success"); + emit receiveSucceed(); +} + + +void CDView::cd_receivedX() { + X.lock(); + PIVector > xl = X.enabledList(); + //piCout << "X" << xl.size(); + piForeachC (PIDeque & x, xl) { + CDType & t(X[x]); + //piCout << t; + //piCout << t.path(); + if (t.cd_type() != CDType::cdX) continue; + update(model_->indexByPath(t.path(), cValue)); + //piCout << CDCore::pathToString(t.path()) << t.toDouble() << "model"; + //qDebug() << "val" << model_->data(model_->indexByPath(t.path(), cValue), Qt::DisplayRole).toDouble(); + } + X.unlock(); + emit receivedX(); +} + + +void CDView::cd_changedGlobal() { + emit changedGlobal(); +} + + +void CDView::pi_cd_messageReceived(PIDeque path, int type, PIString msg) { + QMetaObject::invokeMethod(this, "messageReceived", Qt::QueuedConnection, + Q_ARG(QString, PI2QString(CDCore::pathToString(path))), + Q_ARG(int, type), + Q_ARG(QString, PI2QString(msg))); +} diff --git a/test/qcd_utils/qcd_view.h b/test/qcd_utils/qcd_view.h new file mode 100644 index 0000000..de21062 --- /dev/null +++ b/test/qcd_utils/qcd_view.h @@ -0,0 +1,89 @@ +#ifndef QCD_VIEW_H +#define QCD_VIEW_H + +#include "piobject.h" +#include + +namespace CDUtils { + class CDType; + class CDSection; +} + +class CDItemModel; + +class CDView: public QTreeView, public PIObject +{ + Q_OBJECT + PIOBJECT(CDView) +public: + explicit CDView(QWidget *parent = 0); + ~CDView(); + + void setType(int cdt); + void setFile(const QString & filename); + bool inProgress() const; + void startX(double freq = 20.); + CDUtils::CDSection * root(); + QString typeLetter() const; + + CDItemModel * CDModel() {return model_;} + +protected: + void mousePressEvent(QMouseEvent * ); + void mouseReleaseEvent(QMouseEvent * ); + void currentChanged(const QModelIndex & cur, const QModelIndex & prev); + +public slots: + void refresh(); + void refreshValues(); + void send(); + void receive(); + void save(); + void load(); + void clear(); + void buildFromHeader(const QString & description, int mode = 2); + void calculate(); + +private slots: + void indexClicked(const QModelIndex & i); + void cd_sendFailed(); + void cd_sendSucceed(); + void cd_receiveFailed(); + void cd_receiveSucceed(); + void cd_receivedX(); + void cd_changedGlobal(); + +private: + EVENT_HANDLER(void, pi_cd_sendFailed) {emit _qcd_sendFailed();} + EVENT_HANDLER(void, pi_cd_sendSucceed) {emit _qcd_sendSucceed();} + EVENT_HANDLER(void, pi_cd_receiveFailed) {emit _qcd_receiveFailed();} + EVENT_HANDLER(void, pi_cd_receiveSucceed) {emit _qcd_receiveSucceed();} + EVENT_HANDLER(void, pi_cd_receivedX) {emit _qcd_receivedX();} + EVENT_HANDLER(void, pi_cd_changedGlobal) {emit _qcd_changedGlobal();} + EVENT_HANDLER3(void, pi_cd_messageReceived, PIDeque, path, int, type, PIString, msg); + + CDItemModel * model_; + int type_; + +signals: + void sendFailed(); + void sendSucceed(); + void receiveFailed(); + void receiveSucceed(); + void receivedX(); + void changedGlobal(); + void messageStatus(QString msg); + void commandSended(QString msg); + void messageReceived(QString path, int type, QString msg); + void busyStatusChanged(bool busy); + + void _qcd_sendFailed(); // PRIVATE + void _qcd_sendSucceed(); // PRIVATE + void _qcd_receiveFailed(); // PRIVATE + void _qcd_receiveSucceed(); // PRIVATE + void _qcd_receivedX(); // PRIVATE + void _qcd_changedGlobal(); // PRIVATE + +}; + +#endif // QCD_VIEW_H diff --git a/test/qglview/CMakeLists.txt b/test/qglview/CMakeLists.txt new file mode 100644 index 0000000..48fed03 --- /dev/null +++ b/test/qglview/CMakeLists.txt @@ -0,0 +1,61 @@ +project(qglview) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (IBPROJECT) + include(SDKMacros) +else() + option(LIB "System install" 0) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +if (MINGW) + find_package(MinGW REQUIRED) +endif() +find_package(QAD REQUIRED) +find_package(OpenGL REQUIRED) +include_directories(${QAD_INCLUDES}) +find_qt(${QtVersions} Core Gui OpenGL Xml) +qt_sources(SRC) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets ${OPENGL_LIBRARIES}) +message(STATUS "Building ${PROJECT_NAME}") +if (LIBPROJECT) + sdk_install("qad" "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") +else() + if (LIB) + if (WIN32) + qt_install(FILES ${H} DESTINATION ${MINGW_INCLUDE}/qad) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtBin) + else() + qt_install(FILES ${H} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/qad) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() +foreach(_v ${_QT_VERSIONS_}) + set(MULTILIB_qglview_SUFFIX_Qt${_v} ${_v}) + set(MULTILIB_qglview_SUFFIX_Qt${_v} ${_v} PARENT_SCOPE) +endforeach() +list(APPEND QT_MULTILIB_LIST qglview) +if (NOT DEFINED ANDROID_PLATFORM) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/plugin") + add_subdirectory(plugin) + endif() +endif() + +qt_sources(test_SRC DIR "qglview_test") +qt_wrap(${test_SRC} CPPS test_CPP) +qt_add_executable(qglview_test test_CPP) +qt_target_link_libraries(qglview_test ${PROJECT_NAME}) diff --git a/test/qglview/glcamera.cpp b/test/qglview/glcamera.cpp new file mode 100644 index 0000000..84ee029 --- /dev/null +++ b/test/qglview/glcamera.cpp @@ -0,0 +1,260 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "qglview.h" + + +Camera::Camera() { + type_ = glCamera; + fov_ = 60.; + angle_limit_lower_xy = 0.f; + angle_limit_upper_xy = 360.f; + angles_.setY(270.f); + depth_start = 0.1f; + depth_end = 1000.f; + mirror_x = mirror_y = false; +} + + +void Camera::anglesFromPoints() { + QVector3D dv = aim_ - pos_, tv; + tv = QVector3D(dv.x(), dv.y(), 0.); + angles_.setZ(atan2f(tv.x(), tv.y()) * rad2deg); + angles_.setY(piClamp(atan2f(tv.length(), dv.z()) * rad2deg, angle_limit_lower_xy, angle_limit_upper_xy) + 180.f); +} + + +void Camera::apply(const GLfloat & aspect) { + glMatrixMode(GL_PROJECTION); + if (aspect <= 1.f) + glScalef(aspect, aspect, 1.f); + QMatrix4x4 pm = glMatrixPerspective(fov_, aspect, depth_start, depth_end); + //pm.perspective(fov_, aspect, depth_start, depth_end); + //qDebug() << pm;// << glMatrixPerspective(fov_, aspect, depth_start, depth_end); + //qDebug() << pm; + setGLMatrix(pm); + glMatrixMode(GL_MODELVIEW); + pm.setToIdentity(); + pm.translate(0., 0., -distance()); + pm.rotate(angles_.y(), 1., 0., 0.); + pm.rotate(angles_.x(), 0., 1., 0.); + pm.rotate(angles_.z(), 0., 0., 1.); + //pm.translate(-aim_); + if (parent_) { + QMatrix4x4 pmat = parent_->worldTransform(); + offset_ = pmat.column(3).toVector3D(); + pmat(0, 3) = pmat(1, 3) = pmat(2, 3) = 0.; + pmat.translate(aim_); + pm *= pmat.inverted(); + //qDebug() << pmat; + } + setGLMatrix(pm); + //qDebug() << angles_; +} + + +QMatrix4x4 Camera::offsetMatrix() const { + QMatrix4x4 ret; + ret.translate(parent_ ? -offset_ : -aim_); + return ret; +} + +/* +void Camera::localTransform(QMatrix4x4 & m) { + return; + if (parent_) + m *= parent_->worldTransform(); + QMatrix4x4 ret; + //qDebug() << "local camera"; + ret.translate(0., 0., -distance()); + ret.rotate(angles_.y(), 1., 0., 0.); + ret.rotate(angles_.x(), 0., 1., 0.); + ret.rotate(angles_.z(), 0., 0., 1.); + //m *= ret.inverted(); +} +*/ + +void Camera::assign(const Camera & c) { + pos_ = c.pos_; + aim_ = c.aim_; + fov_ = c.fov_; + angles_ = c.angles_; + angle_limit_lower_xy = c.angle_limit_lower_xy; + angle_limit_upper_xy = c.angle_limit_upper_xy; + buildTransform(); +} + + +void Camera::panZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::panXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + aim_ = pos_ + dv * tl; + buildTransform(); +} + + +void Camera::rotateZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::rotateXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + aim_ = pos_ + dv * tl; + buildTransform(); +} + + +void Camera::orbitZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + pos_ = aim_ - dv; + buildTransform(); +} + + +void Camera::orbitXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::setAngleZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::setAngleXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(a); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + //pos_ = aim_ - dv; + aim_ = pos_ + dv * tl; + buildTransform(); + //anglesFromPoints(); +} + + +void Camera::moveForward(const float & x, bool withZ) { + QVector3D dv;// = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, 0.); + if (withZ) dv.setZ(-cosf(angles_.y() * deg2rad)); + dv.normalize(); + dv *= x; + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::moveLeft(const float & x, bool withZ) { + QVector3D dv;// = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad - float(M_PI_2)) * tc, cosf(angles_.z() * deg2rad - float(M_PI_2)) * tc, 0.f); + if (withZ) dv.setZ(-sinf(angles_.x() * deg2rad)); + dv.normalize(); + dv *= x; + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::moveUp(const float & x, bool onlyZ) { + QVector3D dv; + if (onlyZ) + dv = QVector3D(0., 0., x); + else { + float tc = cosf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -sinf(angles_.y() * deg2rad)); + dv.normalize(); + dv *= x; + } + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::flyCloser(const float & s) { + QVector3D dv = aim_ - pos_; + float tl = dv.length() / (1.f + s), tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::flyFarer(const float & s) { + QVector3D dv = aim_ - pos_; + float tl = dv.length() * (1.f + s), tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::flyToDistance(const float & d) { + QVector3D dv = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * d; + buildTransform(); +} + diff --git a/test/qglview/glcamera.h b/test/qglview/glcamera.h new file mode 100644 index 0000000..b427b16 --- /dev/null +++ b/test/qglview/glcamera.h @@ -0,0 +1,104 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLCAMERA_H +#define GLCAMERA_H + +#include "globject.h" + +class Camera; + +//extern QMatrix4x4 globCameraMatrix; +//extern Camera * currentCamera; + +class Camera: public GLObjectBase +{ + friend class QGLView; + friend class GLParticlesSystem; + friend QDataStream & operator <<(QDataStream & s, const GLObjectBase * p); + friend QDataStream & operator >>(QDataStream & s, GLObjectBase *& p); +public: + Camera(); + + void setPos(const QVector3D & p) {pos_ = p; anglesFromPoints(); buildTransform();} + void setAim(const QVector3D & p) {aim_ = p; anglesFromPoints(); buildTransform();} + void move(const QVector3D & p) {pos_ += p; aim_ += p; buildTransform();} + void move(const float & x, const float & y = 0., const float & z = 0.) {pos_ += QVector3D(x, y, z); aim_ += QVector3D(x, y, z); buildTransform();} + void moveForward(const float & x, bool withZ = true); + void moveBackward(const float & x, bool withZ = true) {moveForward(-x, withZ);} + void moveLeft(const float & x, bool withZ = true); + void moveRight(const float & x, bool withZ = true) {moveLeft(-x, withZ);} + void moveUp(const float & x, bool onlyZ = false); + void moveDown(const float & x, bool onlyZ = false) {moveUp(-x, onlyZ);} + void rotateZ(const float & a); + void rotateXY(const float & a); + void rotateRoll(const float & a) {angles_.setX(angles_.x() + a); buildTransform();} + void orbitZ(const float & a); + void orbitXY(const float & a); + void panZ(const float & a); + void panXY(const float & a); + void setFOV(const float & f) {fov_ = f;} + void setAngles(const QVector3D & a) {setRotation(a);} + void setAngleZ(const float & a); + void setAngleXY(const float & a); + void setAngleRoll(const float & a) {angles_.setX(a); buildTransform();} + void setAngleLowerLimitXY(const float & a) {angle_limit_lower_xy = a; buildTransform();} + void setAngleUpperLimitXY(const float & a) {angle_limit_upper_xy = a; buildTransform();} + void setAngleLimitsXY(const float & lower, const float & upper) {angle_limit_lower_xy = lower; angle_limit_upper_xy = upper; buildTransform();} + void setDepthStart(const float & d) {depth_start = d;} + void setDepthEnd(const float & d) {depth_end = d;} + void setMirrorX(bool yes) {mirror_x = yes;} + void setMirrorY(bool yes) {mirror_y = yes;} + void flyCloser(const float & s); + void flyFarer(const float & s); + void flyToDistance(const float & d); + + QVector3D aim() const {return aim_;} + QVector3D angles() const {return rotation();} + QVector3D direction() const {return (aim_ - pos_).normalized();} + QVector3D directionXY() const {QVector3D tv = aim_ - pos_; return QVector3D(tv.x(), tv.y(), 0.).normalized();} + float FOV() const {return fov_;} + float distance() const {return (pos_ - aim_).length();} + float angleZ() const {return angles_.z();} + float angleXY() const {return angles_.y();} + float angleRoll() const {return angles_.x();} + float angleLowerLimitXY() const {return angle_limit_lower_xy;} + float angleUpperLimitXY() const {return angle_limit_upper_xy;} + float depthStart() const {return depth_start;} + float depthEnd() const {return depth_end;} + bool isMirrorX() const {return mirror_x;} + bool isMirrorY() const {return mirror_y;} + void anglesFromPoints(); + void apply(const GLfloat & aspect = 1.); + void assign(const Camera & c); + + QMatrix4x4 offsetMatrix() const; + +private: + QVector3D aim_, offset_; + GLfloat fov_; + GLfloat depth_start; + GLfloat depth_end; + GLfloat angle_limit_lower_xy; + GLfloat angle_limit_upper_xy; + bool mirror_x; + bool mirror_y; + +}; + +#endif // GLCAMERA_H diff --git a/test/qglview/glframebuffer.cpp b/test/qglview/glframebuffer.cpp new file mode 100644 index 0000000..57004a4 --- /dev/null +++ b/test/qglview/glframebuffer.cpp @@ -0,0 +1,145 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glframebuffer.h" + + +GLFramebuffer::GLFramebuffer(int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum target__) { + is_depth = withDepth; + color_format = colorFormat_; + target_ = target__; + colors.fill(0, colorAttachments_); + fbo = drbo = 0; + tex_d = 0; + wid = hei = 0; + is_changed = false; +} + + +GLFramebuffer::~GLFramebuffer() { + deleteGLFramebuffer(fbo); + deleteGLRenderbuffer(drbo); + for (int i = 0; i < colors.size(); ++i) + deleteGLTexture(colors[i]); + deleteGLTexture(tex_d); +} + + +void GLFramebuffer::resize(int width, int height, bool force) { + if ((wid == width) && (hei == height) && !force) return; + initializeOpenGLFunctions(); + wid = width; + hei = height; + deleteGLFramebuffer(fbo); + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + for (int i = 0; i < colors.size(); ++i) { + deleteGLTexture(colors[i]); + createGLTexture(colors[i], width, height, color_format, target_); + glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4); + glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0); + } + if (is_depth) { + deleteGLTexture(tex_d); + deleteGLRenderbuffer(drbo); + glGenRenderbuffers(1, &drbo); + glBindRenderbuffer(GL_RENDERBUFFER, drbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo); + createGLTexture(tex_d, width, height, GL_DEPTH_COMPONENT); + glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0); + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + is_changed = false; +} + + +QImage GLFramebuffer::grab() const { + //glReadPixels(0, 0, wid, hei, GL_RGBA, ); + //QImage ret(); + return QImage(); +} + + +void GLFramebuffer::bind() { + if (is_changed) resize(wid, hei); + if (fbo == 0) return; + initializeOpenGLFunctions(); + //glFlush(); + glGetIntegerv(GL_VIEWPORT, prev_view); + //glClearError(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + //qDebug() << QString::number(glGetError(), 16); + QVector buffers; + for (int i = 0; i < colors.size(); ++i) + buffers << GL_COLOR_ATTACHMENT0 + i; + glDrawBuffers(buffers.size(), buffers.constData()); + glReadBuffer(GL_COLOR_ATTACHMENT0); + //glDrawBuffer(GL_COLOR_ATTACHMENT0); + glViewport(0, 0, wid, hei); +} + + +void GLFramebuffer::release() { + is_changed = false; + if (fbo == 0) return; + //glFlush(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]); +} + + +void GLFramebuffer::setWriteBuffer(int index) { + //QVector buffers; buffers << GL_COLOR_ATTACHMENT0 + index; + glDrawBuffer(GL_COLOR_ATTACHMENT0 + index); + //glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void GLFramebuffer::setWriteBuffers(int * indeces, int count) { + QVector buffers; + for (int i = 0; i < count; ++i) + buffers << GL_COLOR_ATTACHMENT0 + indeces[i]; + glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void GLFramebuffer::bindColorTextures() { + for (int i = colors.size() - 1; i >= 0; --i) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, colors[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } +} + + +void GLFramebuffer::bindDepthTexture(int channel) { + glActiveTexture(GL_TEXTURE0 + channel); + glBindTexture(GL_TEXTURE_2D, tex_d); +} diff --git a/test/qglview/glframebuffer.h b/test/qglview/glframebuffer.h new file mode 100644 index 0000000..a1d2e8f --- /dev/null +++ b/test/qglview/glframebuffer.h @@ -0,0 +1,66 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLFRAMEBUFFER_H +#define GLFRAMEBUFFER_H + +#include "gltypes.h" +#include + + +class GLFramebuffer : protected QOpenGLExtraFunctions +{ +public: + GLFramebuffer(int colorAttachments = 1, bool withDepth = true, GLenum colorFormat = GL_RGBA8, GLenum target = GL_TEXTURE_2D); + virtual ~GLFramebuffer(); + + GLuint id() const {return fbo;} + GLuint colorTexture(int index = 0) const {return colors[index];} + GLenum colorFormat() const {return color_format;} + GLuint depthTexture() const {return tex_d;} + GLenum target() const {return target_;} + int width() const {return wid;} + int height() const {return hei;} + QSize size() const {return QSize(wid, hei);} + QImage grab() const; + + void resize(int width, int height, bool force = false); + void bind(); + void release(); + void setReadBuffer(int index) {glReadBuffer(GL_COLOR_ATTACHMENT0 + index);} + void setWriteBuffer(int index); + void setWriteBuffers(int * indeces, int count); + void setColorFormat(GLenum format) {color_format = format; is_changed = true;} + + void copyDepthFrom(GLuint tex) {;} + void bindColorTextures(); + void bindDepthTexture(int channel); + +private: + void deleteGLRenderbuffer(GLuint & drbo) {if (drbo != 0) glDeleteRenderbuffers(1, &drbo); drbo = 0;} + void deleteGLFramebuffer(GLuint & fbo) {if (fbo != 0) glDeleteFramebuffers(1, &fbo); fbo = 0;} + + bool is_depth, is_changed; + QVector colors; + GLenum color_format, target_; + GLuint fbo, drbo, tex_d; + GLint prev_view[4], wid, hei; + +}; + +#endif // GLFRAMEBUFFER_H diff --git a/test/qglview/glmaterial.cpp b/test/qglview/glmaterial.cpp new file mode 100644 index 0000000..683c2bb --- /dev/null +++ b/test/qglview/glmaterial.cpp @@ -0,0 +1,266 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "qglview.h" + +QStringList GLTextureManagerBase::search_pathes("."); + + +bool GLCubeTexture::create() { + //qDebug("create"); + destroy(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_CUBE_MAP, id_); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR/*_MIPMAP_LINEAR*/); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + //glClearError(); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + //qDebug() << glGetError(); + changed_ = false; + return id_ > 0; +} + + +void GLCubeTexture::load() { + if (isEmpty()) return; + create(); + if (!path(0).isEmpty()) loadFront(path(0)); + if (!path(1).isEmpty()) loadBack(path(1)); + if (!path(2).isEmpty()) loadLeft(path(2)); + if (!path(3).isEmpty()) loadRight(path(3)); + if (!path(4).isEmpty()) loadTop(path(4)); + if (!path(5).isEmpty()) loadBottom(path(5)); +} + + +void GLCubeTexture::loadFromDirectory(const QString & dir) { + QDir d(dir); QFileInfoList sl; + sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadFront(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBack(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadLeft(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadRight(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadTop(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBottom(sl[0].absoluteFilePath()); +} + + +void GLCubeTexture::loadPathesFromDirectory(const QString & dir) { + QDir d(dir); QFileInfoList sl; + sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[0] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[1] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[2] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[3] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[4] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[5] = sl[0].absoluteFilePath(); +} + + + +QString GLTextureManagerBase::findFile(const QString & path) { + return ::findFile(path, search_pathes); +} + + +GLuint GLTextureManagerBase::loadTexture(const QString & path, bool ownership, bool bump) { + QString p = findFile(path); + if (p.isEmpty()) return 0; + int tid = textureID(p, bump); + if (tid > 0) { + //qDebug() << "[TextureManager] Found" << path << "as" << tid; + return tid; + } + QImage image(p); + if (bump) convertToNormal(image); + //qDebug() << p << image.width() << image.height() << image.format() << bump; + ///tid = currentQGLView->bindTexture(image, GL_TEXTURE_2D/*, GL_RGBA, __GLContext__::MipmapBindOption*/); + //GLuint tid = 0; + GLuint _tid = tid; + createGLTexture(_tid, image);///currentQGLView->bindTexture(image, GL_TEXTURE_2D); + tid = _tid; + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load" << p; + return tid; + } + qDebug() << "[TextureManager] Loaded" << p << "as" << tid; + if (ownership) tex_ids[bump ? 1 : 0].insert(p, tid); + return tid; +} + + +GLuint GLTextureManagerBase::loadTexture(const QImage & im, bool ownership, bool bump) { + if (im.isNull()) return 0; + QImage image(im); + if (bump) convertToNormal(image); + GLuint tid = 0; + createGLTexture(tid, im);///currentQGLView->bindTexture(image, GL_TEXTURE_2D); + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load image"; + return tid; + } + //qDebug() << "[TextureManager] Loaded image as" << tid; + if (ownership) tex_ids[bump ? 1 : 0].insert(QString(), tid); + return tid; +} + + +void GLTextureManagerBase::reloadTexture(GLuint tid, const QString & path) { + QString p = findFile(path); + if (p.isEmpty() || (tid == 0)) return; + QImage image(p); + createGLTexture(tid, image); + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load" << p; + return; + } + qDebug() << "[TextureManager] Reloaded" << p << "as" << tid; +} + + +void GLTextureManagerBase::reloadTexture(GLuint tid, const QImage & im) { + if (im.isNull() || (tid == 0)) return; + QImage image(im); + createGLTexture(tid, image); + qDebug() << "[TextureManager] Reloaded" << tid; +} + + +Vector3d colorVector(QRgb c) { + return Vector3d(((uchar*)(&c))[0] / 255., ((uchar*)(&c))[1] / 255., ((uchar*)(&c))[2] / 255.); +} + + +void GLTextureManagerBase::convertToNormal(QImage & im) { + if (im.isNull()) return; + QImage sim = im.convertToFormat(QImage::Format_ARGB32); + float sum[3] = {0., 0., 0.}; + llong a = 0; + const uchar * sd = sim.constBits(); + for (int i = 0; i < sim.height(); i++) { + for (int j = 0; j < sim.width(); j++) { + sum[2] += sd[a] / 255.f - 0.5f; ++a; + sum[1] += sd[a] / 255.f - 0.5f; ++a; + sum[0] += sd[a] / 255.f - 0.5f; ++a; + ++a; + } + } + float wh = sim.width() * sim.height(); + sum[0] /= wh; + sum[1] /= wh; + sum[2] /= wh; + qDebug() << sum[0] << sum[1] << sum[2]; + if ((qAbs(sum[0]) <= 0.05f) && (qAbs(sum[1]) <= 0.05f) && (sum[2] >= 0.25f)) /// already normal + return; + qDebug() << "convert to bump"; + QImage dim = QImage(sim.width(), sim.height(), QImage::Format_ARGB32); + int tx, ty, w = sim.width(), h = sim.height(); + a = 0; + uchar * dd = dim.bits(); + for (int i = 0; i < sim.height(); i++) { + for (int j = 0; j < sim.width(); j++) { + tx = j - 1; + tx = tx < 0 ? w + tx : tx % w; + ty = i - 1; + ty = ty < 0 ? h + ty : ty % h; + Vector3d p[3], res; + p[0] = colorVector(sim.pixel(j, i)); + p[1] = colorVector(sim.pixel(j, ty)); + p[2] = colorVector(sim.pixel(tx, i)); + res.y = piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f); + res.x = piClamp(0.5f + (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f); + tx = (j + 1) % w; + ty = (i + 1) % h; + p[1] = colorVector(sim.pixel(j, ty)); + p[2] = colorVector(sim.pixel(tx, i)); + res.y = piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f); + res.x = piClamp(0.5f + (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f); + res.z = 1.; + dd[a] = res.z * 255; ++a; + dd[a] = res.x * 255; ++a; + dd[a] = res.y * 255; ++a; + dd[a] = 255; ++a; + } + } + im = dim; + //im.save("_bump.png"); +} + + + + +Material::Material(): map_reflection(512) { + color_diffuse = color_specular = Qt::white; + color_self_illumination = Qt::black; + glass = false; + transparency = reflectivity = 0.f; + map_specularity.color_amount = 0.5f; + map_specular.color_amount = 1.f; + iof = 1.f; + dispersion = 0.05f; +} + + +void Material::apply(QOpenGLShaderProgram * prog) { + if (prog) { + setUniformMaterial(prog, *this); + } else { + GLfloat mat_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + GLfloat mat_specular[4] = {0.9f, 0.9f, 0.9f, 1.0f}; + GLfloat mat_emission[4] = {0.f, 0.f, 0.f, 1.0f}; + mat_diffuse[0] = map_diffuse.color_amount * color_diffuse.redF(); + mat_diffuse[1] = map_diffuse.color_amount * color_diffuse.greenF(); + mat_diffuse[2] = map_diffuse.color_amount * color_diffuse.blueF(); + mat_diffuse[3] = map_diffuse.color_amount * color_diffuse.alphaF() * (1.f - transparency); + mat_specular[0] = map_specular.color_amount * color_specular.redF(); + mat_specular[1] = map_specular.color_amount * color_specular.greenF(); + mat_specular[2] = map_specular.color_amount * color_specular.blueF(); + mat_emission[0] = map_self_illumination.color_amount * color_self_illumination.redF(); + mat_emission[1] = map_self_illumination.color_amount * color_self_illumination.greenF(); + mat_emission[2] = map_self_illumination.color_amount * color_self_illumination.blueF(); + glColor4f(mat_diffuse[0], mat_diffuse[1], mat_diffuse[2], mat_diffuse[3]); + glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); + //qDebug() << (map_specularity.color_amount)*128.; + glMaterialf(GL_FRONT, GL_SHININESS, (map_specularity.color_amount)*128.f); + glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); + glMaterialfv(GL_FRONT, GL_AMBIENT, mat_diffuse); + } +} + + +void Material::loadTextures(GLTextureManagerBase * tm) { + //qDebug() << "load textures"; + if (!tm) return; + if (!map_diffuse.bitmap_path.isEmpty()) map_diffuse.bitmap_id = tm->loadTexture(map_diffuse.bitmap_path); + if (!map_normal.bitmap_path.isEmpty()) map_normal.bitmap_id = tm->loadTexture(map_normal.bitmap_path, true, true); + if (!map_relief.bitmap_path.isEmpty()) map_relief.bitmap_id = tm->loadTexture(map_relief.bitmap_path); + if (!map_specularity.bitmap_path.isEmpty()) map_specularity.bitmap_id = tm->loadTexture(map_specularity.bitmap_path); + if (!map_specular.bitmap_path.isEmpty()) map_specular.bitmap_id = tm->loadTexture(map_specular.bitmap_path); + if (!map_self_illumination.bitmap_path.isEmpty()) map_self_illumination.bitmap_id = tm->loadTexture(map_self_illumination.bitmap_path); + //if (!map_diffuse_2.bitmap_path.isEmpty()) map_diffuse_2.bitmap_id = tm->loadTexture(map_diffuse_2.bitmap_path); + map_reflection.load(); +} diff --git a/test/qglview/glmaterial.h b/test/qglview/glmaterial.h new file mode 100644 index 0000000..57fb96d --- /dev/null +++ b/test/qglview/glmaterial.h @@ -0,0 +1,188 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLMATERIAL_H +#define GLMATERIAL_H + +#include "gltypes.h" +#include "chunkstream.h" + +class GLTexture { +public: + GLTexture(int _width, int _height, const GLenum & _format = GL_RGBA8, const GLenum & _target = GL_TEXTURE_2D) {wid = _width; hei = _height; format_ = _format; target_ = _target; id_ = 0;} + bool create() {destroy(); createGLTexture(id_, wid, hei, format_, target_); return id_ > 0;} + void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;} + void bind() {if (id_ > 0) glBindTexture(target_, id_);} + void release() {glBindTexture(target_, 0);} + int width() const {return wid;} + int height() const {return hei;} + GLenum format() const {return format_;} + GLenum target() const {return target_;} + GLuint id() const {return id_;} +private: + int wid, hei; + GLenum format_, target_; + GLuint id_; +}; + + +class GLCubeTexture { +public: + GLCubeTexture(int _size, const GLenum & _format = GL_RGBA8) {size = _size; format_ = _format; id_ = 0; changed_ = false; pathes.resize(6);} + bool create(); + void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;} + void bind() {if (changed_) {changed_ = false; create();} if (id_ > 0) glBindTexture(GL_TEXTURE_CUBE_MAP, id_);} + void release() {glBindTexture(GL_TEXTURE_CUBE_MAP, 0);} + void resize(int _size) {size = _size; changed_ = true;} + void loadFromDirectory(const QString & dir); + void loadFront(const QString & path) {bind(); pathes[0] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_X);} + void loadBack(const QString & path) {bind(); pathes[1] = path; createGLTexture(id_, rotateQImageRight(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);} + void loadLeft(const QString & path) {bind(); pathes[2] = path; createGLTexture(id_, QImage(path).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);} + void loadRight(const QString & path) {bind(); pathes[3] = path; createGLTexture(id_, rotateQImage180(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);} + void loadTop(const QString & path) {bind(); pathes[4] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);} + void loadBottom(const QString & path) {bind(); pathes[5] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);} + void load(); + bool isEmpty() const {foreach (const QString & i, pathes) if (!i.isEmpty()) return false; return true;} + GLenum format() const {return format_;} + void setFormat(GLenum f) {format_ = f; changed_ = true;} + GLuint id() const {return id_;} + const QString & path(int side) const {return pathes[side];} + void setPath(int side, const QString & p) {pathes[side] = p;} + void loadPathesFromDirectory(const QString & dir); +private: + bool changed_; + int size; + GLenum format_; + GLuint id_; + QVector pathes; +}; + +class GLTextureManager; + +class GLTextureManagerBase { +public: + GLTextureManagerBase() {} + virtual ~GLTextureManagerBase() {} + void addSearchPath(const QString & path) {search_pathes << path;} + static QStringList searchPathes() {return search_pathes;} + QString findFile(const QString & path); + GLuint loadTexture(const QString & path, bool ownership = true, bool bump = false); + GLuint loadTexture(const QImage & image, bool ownership = true, bool bump = false); + void reloadTexture(GLuint tid, const QString & path); + void reloadTexture(GLuint tid, const QImage & image); + int textureID(const QString & path, bool bump = false) {return tex_ids[bump ? 1 : 0][path];} + virtual void addTexture(const QString & path) = 0; + virtual void addAnimation(const QString & dir, const QString & name) = 0; + virtual bool loadTextures() = 0; + +protected: + static void convertToNormal(QImage & im); + static QStringList search_pathes; + QMap tex_ids[2]; + +}; + +struct Map { + Map() {bitmap_id = 0; color_amount = 1.f; color_offset = 0.f; animation_frame_rate = -1.f;} + QString bitmap_path; + GLuint bitmap_id; + QPointF bitmap_offset; + float color_amount; + float color_offset; + QString animation; + float animation_frame_rate; +}; + +struct Material { + Material(); + void apply(QOpenGLShaderProgram * prog); + void loadTextures(GLTextureManagerBase * tm); + QString name; + QColor color_diffuse; + QColor color_specular; + QColor color_self_illumination; + bool glass; + float transparency; + float reflectivity; + float iof; + float dispersion; + Map map_diffuse; + Map map_normal; + Map map_relief; + Map map_self_illumination; + Map map_specularity; + Map map_specular; + /*Map map_diffuse_2; + Map map_diffuse_3; + Map map_diffuse_4;*/ + GLCubeTexture map_reflection; +}; + + +inline QDataStream & operator <<(QDataStream & s, const Map & m) { + ChunkStream cs; + cs << cs.chunk(1, m.bitmap_path) << cs.chunk(2, m.color_amount) << cs.chunk(3, m.color_offset) << cs.chunk(4, m.animation) << cs.chunk(5, m.animation_frame_rate); + s << cs.data(); return s; +} +inline QDataStream & operator >>(QDataStream & s, Map & m) { + ChunkStream cs(s); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: m.bitmap_path = cs.getData(); break; + case 2: m.color_amount = cs.getData(); break; + case 3: m.color_offset = cs.getData(); break; + case 4: m.animation = cs.getData(); break; + case 5: m.animation_frame_rate = cs.getData(); break; + } + } + return s; +} + +inline QDataStream & operator <<(QDataStream & s, const Material & m) { + ChunkStream cs; + cs << cs.chunk(1, m.name) << cs.chunk(2, m.color_diffuse) << cs.chunk(3, m.color_specular) << cs.chunk(4, m.color_self_illumination) + << cs.chunk(5, m.transparency) << cs.chunk(6, m.reflectivity) << cs.chunk(7, m.glass) << cs.chunk(8, m.map_diffuse) << cs.chunk(9, m.map_normal) + << cs.chunk(10, m.map_relief) << cs.chunk(11, m.map_specular) << cs.chunk(12, m.map_specularity) << cs.chunk(13, m.map_self_illumination); + s << qCompress(cs.data()); return s; +} +inline QDataStream & operator >>(QDataStream & s, Material & m) { + QByteArray ba; + s >> ba; + ba = qUncompress(ba); + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: m.name = cs.getData(); break; + case 2: m.color_diffuse = cs.getData(); break; + case 3: m.color_specular = cs.getData(); break; + case 4: m.color_self_illumination = cs.getData(); break; + case 5: m.transparency = cs.getData(); break; + case 6: m.reflectivity = cs.getData(); break; + case 7: m.glass = cs.getData(); break; + case 8: m.map_diffuse = cs.getData(); break; + case 9: m.map_normal = cs.getData(); break; + case 10: m.map_relief = cs.getData(); break; + case 11: m.map_specular = cs.getData(); break; + case 12: m.map_specularity = cs.getData(); break; + case 13: m.map_self_illumination = cs.getData(); break; + } + } + return s; +} + +#endif // GLMATERIAL_H diff --git a/test/qglview/globject.cpp b/test/qglview/globject.cpp new file mode 100644 index 0000000..a49bdf8 --- /dev/null +++ b/test/qglview/globject.cpp @@ -0,0 +1,593 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "globject.h" +#include "glcamera.h" +#include "qglview.h" + + +GLObjectBase::GLObjectBase() { + type_ = glMesh; + render_mode = View; + pass_ = Solid; + geom_prim = Triangles; + scale_ = QVector3D(1., 1., 1.); + parent_ = nullptr; + is_root = is_init = is_tex_loaded = selected_ = false; + visible_ = accept_fog = accept_light = cast_shadow = rec_shadow = select_ = true; + line_width = -1.; + blend_src = GL_SRC_ALPHA; + blend_dest = GL_ONE_MINUS_SRC_ALPHA; + type_ = glMesh; + raw_matrix = false; + mat_.setToIdentity(); + view_ = nullptr; +} + + +GLObjectBase::~GLObjectBase() { + //qDebug() << "del" << name() << view_; + if (parent_) parent_->children_.removeAll(this); + if (view_) ((QGLView*)view_)->objectDeleted(this); + foreach (GLObjectBase * c, children_) { + c->parent_ = nullptr; + delete c; + } +} + + +GLObjectBase * GLObjectBase::clone(bool withChildren) { + GLObjectBase * o = new GLObjectBase(); + o->pass_ = pass_; + o->is_init = false; + o->accept_light = accept_light; + o->accept_fog = accept_fog; + o->visible_ = visible_; + o->type_ = type_; + o->raw_matrix = raw_matrix; + o->mat_ = mat_; + o->pos_ = pos_; + o->angles_ = angles_; + o->scale_ = scale_; + o->itransform_ = itransform_; + o->bound = bound; + o->name_ = name_ + "_copy"; + o->blend_src = blend_src; + o->blend_dest = blend_dest; + o->material_ = material_; + o->pos_h = pos_h; + o->points = points; + o->puvws = puvws; + o->faces = faces; + o->uvws = uvws; + o->norms = norms; + o->normals = normals; + o->vbo.vertices_ = vbo.vertices_; + o->vbo.normals_ = vbo.normals_; + o->vbo.texcoords_ = vbo.texcoords_; + o->vbo.colors_ = vbo.colors_; + o->view_ = nullptr; + o->children_.clear(); + if (withChildren) { + for (int i = 0; i < children_.size(); ++i) + o->addChild(children_[i]->clone(withChildren)); + } + return o; +} + + +void GLObjectBase::init() { + calculateBoundingBox(); + vbo.init(); + vbo.rebuffer(); + //material_.reflection.create(); + //qDebug() << "init" << vbo.buffer_; + is_init = true; +} + + +void GLObjectBase::draw(QOpenGLShaderProgram * prog, bool simplest) { + vbo.draw(geom_prim, prog, simplest); + /*if (!d_vertices.isEmpty()) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glVertexPointer(3, GL_FLOAT, 0, d_vertices.constData()); + glTexCoordPointer(2, GL_FLOAT, 0, d_uvs.constData()); + //glColorPointer(4, GL_FLOAT, 0, d_colors.constData()); + glNormalPointer(GL_FLOAT, 0, d_normals.constData()); + glDrawArrays(geom_prim, 0, d_vertices.size() / 3);*/ + /*if (pass_ == Reflection) { + glActiveTexture(GL_TEXTURE1); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + }*/ + //} +} + + +void GLObjectBase::setView(QGLView * v) { + view_ = v; + foreach (GLObjectBase * c, children_) + c->setView(v); +} + + +void GLObjectBase::addChild(GLObjectBase * o) { + if (o == this) return; + if (o->parent_) + o->parent_->children_.removeAll(o); + children_ << o; + o->parent_ = this; + o->setView((QGLView*)view_); + o->buildTransform(); + if (view_) { + view_->collectLights(); + QList cl = o->children(true); + cl << o; + foreach (GLObjectBase * i, cl) { + emit ((QGLView*)view_)->objectAdded(i); + } + } +} + + +void GLObjectBase::removeChild(GLObjectBase * o) { + if (o == this) return; + children_.removeAll(o); + o->parent_ = nullptr; + o->buildTransform(); + if (view_ != nullptr) view_->collectLights(); +} + + +void GLObjectBase::removeChild(int index) { + children_[index]->parent_ = nullptr; + children_[index]->buildTransform(); + children_.removeAt(index); + if (view_ != nullptr) view_->collectLights(); +} + + +void GLObjectBase::clearChildren(bool deleteAll) { + foreach (GLObjectBase * i, children_) { + i->view_ = nullptr; + i->parent_ = nullptr; + i->clearChildren(deleteAll); + if (deleteAll) { + delete i; + } else { + i->buildTransform(); + } + } + children_.clear(); + if (view_) view_->collectLights(); +} + + +GLObjectBase * GLObjectBase::child(int index) { + if (index < 0 || index >= children_.size()) return nullptr; + return children_[index]; +} + + +GLObjectBase * GLObjectBase::child(const QString & name) { + foreach (GLObjectBase * i, children_) + if (i->name_ == name) return i; + return nullptr; +} + + +const GLObjectBase * GLObjectBase::child(int index) const { + if (index < 0 || index >= children_.size()) return nullptr; + return children_[index]; +} + + +const GLObjectBase * GLObjectBase::child(const QString & name) const { + foreach (GLObjectBase * i, children_) + if (i->name_ == name) return i; + return nullptr; +} + + +QList GLObjectBase::children(bool all_) { + if (!all_) return children_; + QList cl; + addChildren(cl, this); + return cl; +} + + +void GLObjectBase::rotateX(GLfloat a) { + raw_matrix = false; + angles_.setX(angles_.x() + a); + buildTransform(); +} + + +void GLObjectBase::rotateY(GLfloat a) { + raw_matrix = false; + angles_.setY(angles_.y() + a); + buildTransform(); +} + + +void GLObjectBase::rotateZ(GLfloat a) { + raw_matrix = false; + angles_.setZ(angles_.z() + a); + while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f); + while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f); + buildTransform(); +} + + +void GLObjectBase::setRotationX(GLfloat a) { + raw_matrix = false; + angles_.setX(a); + buildTransform(); +} + + +void GLObjectBase::setRotationY(GLfloat a) { + raw_matrix = false; + angles_.setY(a); + buildTransform(); +} + + +void GLObjectBase::setRotationZ(GLfloat a) { + raw_matrix = false; + angles_.setZ(a); + while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f); + while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f); + buildTransform(); +} + + +void GLObjectBase::setRotation(const QVector3D & a) { + raw_matrix = false; + angles_= a; + buildTransform(); +} + + +void GLObjectBase::resetRotation() { + raw_matrix = false; + angles_ = QVector3D(0., 0., 0.); + buildTransform(); +} + + +void GLObjectBase::addChildren(QList & list, GLObjectBase * where) { + foreach (GLObjectBase * i, where->children_) { + list << i; + addChildren(list, i); + } +} + + +void GLObjectBase::loadTextures(bool with_children) { + material_.loadTextures(view_->textureManager()); + if (with_children) + foreach (GLObjectBase * i, children_) i->loadTextures(); + is_tex_loaded = true; + checkPass(); +} + + +void GLObjectBase::calculateBoundingBox() { + bound = vbo.boundingBox(); + QVector c = bound.corners(), tc; + //QMatrix4x4 mat = itransform_.inverted(); + //qDebug() << itransform_ << mat_ << mat; + foreach (QVector3D p, c) + tc << (itransform_ * QVector4D(p, 1)).toVector3D(); + bound = Box3D(tc); + foreach (GLObjectBase * i, children_) { + i->calculateBoundingBox(); + bound |= i->boundingBox(); + } +} + + +void GLObjectBase::setProperty(const QString & pn, const QVariant & v) { + meta[pn] = v; +} + + +QVariant GLObjectBase::property(const QString & pn, bool * exists) const { + if (exists) *exists = meta.contains(pn); + return meta.value(pn); +} + + +bool GLObjectBase::hasProperty(const QString & pn) const { + return meta.contains(pn); +} + + +void GLObjectBase::removeProperty(const QString & pn) { + meta.remove(pn); +} + + +void GLObjectBase::setTransform(const QMatrix4x4 & t) { + raw_matrix = true; + mat_ = t; + pos_ = mat_.column(3).toVector3D(); + mat_.setColumn(3, QVector4D(0., 0., 0., 1.)); + buildTransform(); +} + + +void GLObjectBase::select() { + //qDebug() << "select" << name() << view_; + selected_ = true; + if (view_) + ((QGLView*)view_)->selectObject(this); +} + + +void GLObjectBase::setMaterial(const Material & m, bool with_children) { + material_ = m; + if (with_children) + foreach (GLObjectBase * i, children_) i->setMaterial(m, true); + checkPass(); + is_tex_loaded = false; +} + + +void GLObjectBase::buildTransform() { + itransform_.setToIdentity(); + GLObjectBase * p = parent_; + if (p) + itransform_ = p->itransform_; + if (raw_matrix) { + itransform_.translate(pos_); + itransform_ *= mat_; + //qDebug() << "raw_matrix" << itransform_; + } else + localTransform(itransform_); + //qDebug() << name_ << itransform_; + foreach (GLObjectBase * i, children_) + i->buildTransform(); +} + + +void GLObjectBase::initInternal() { + init(); + loadTextures(); + foreach (GLObjectBase * i, children_) i->initInternal(); +} + + +void GLObjectBase::localTransform(QMatrix4x4 & m) { + m.translate(pos_); + m.rotate(angles_.z(), 0., 0., 1.); + m.rotate(angles_.y(), 0., 1., 0.); + m.rotate(angles_.x(), 1., 0., 0.); + m.scale(scale_); +} + + +void GLObjectBase::checkPass() { + if (float(material_.color_diffuse.alphaF()) * (1.f - material_.transparency) < 1.f) pass_ = Transparent; + else pass_ = Solid; +} + + +QMatrix4x4 GLObjectBase::worldMatrix(QMatrix4x4 parent) const { + QMatrix4x4 mat; + mat.translate(pos_); + if (raw_matrix) { + mat *= mat_; + } else { + if (angles_.z() != 0.f) mat.rotate(angles_.z(), 0., 0., 1.); + if (angles_.y() != 0.f) mat.rotate(angles_.y(), 0., 1., 0.); + if (angles_.x() != 0.f) mat.rotate(angles_.x(), 1., 0., 0.); + mat.scale(scale_); + } + return parent * mat; +} + + +void GLObjectBase::render(int * id, QMap * ids, int sh_id_loc) { + if (!visible_) return; + //glPushMatrix(); + ///qglMultMatrix TODO + material_.apply(nullptr); + if (id != nullptr) { + ++(*id); + ids->insert(*id, this); + //glVertexAttrib1f(sh_id_loc, (*id) / 255.f); + //qDebug() << "assign to" << sh_id_loc << (*id) / 255.f; + } + draw(nullptr); + foreach (GLObjectBase * i, children_) + i->render(id, ids, sh_id_loc); + //glPopMatrix(); +} + + + + +Light::Light(): GLObjectBase(), shadow_map(0, true, GL_R16F) { + type_ = glLight; + light_type = Omni; + intensity = 1.; + angle_start = angle_end = 180.; + decay_linear = decay_quadratic = decay_start = 0.; + decay_const = decay_end = 1.; + direction.setZ(1.); +} + + +Light::Light(const QVector3D & p, const QColor & c, float i): GLObjectBase(), shadow_map(0, true, GL_R16F) { + type_ = glLight; + light_type = Omni; + pos_ = p; + intensity = i; + /*color_ = c;*/ + angle_start = angle_end = 180.; + decay_linear = decay_quadratic = decay_start = 0.; + decay_const = decay_end = 1.; + direction.setZ(1.); +} + + +GLObjectBase * Light::clone(bool withChildren) { + Light * o = new Light(*this); + //GLObjectBase::clone(withChildren); + o->is_init = false; + o->name_ = name_ + "_copy"; + o->view_ = nullptr; + o->children_.clear(); + if (withChildren) { + for (int i = 0; i < children_.size(); ++i) + o->addChild(children_[i]->clone(withChildren)); + } + o->light_type = light_type; + o->direction = direction; + o->angle_start = angle_start; + o->angle_end = angle_end; + o->intensity = intensity; + o->decay_const = decay_const; + o->decay_linear = decay_linear; + o->decay_quadratic = decay_quadratic; + return o; +} + + +void Light::draw(QOpenGLShaderProgram * prog, bool simplest) { + bool l = glIsEnabled(GL_LIGHTING); + glDisable(GL_LIGHTING); + glPointSize(8.); + glColor3f(0., 0., 0.); + glBegin(GL_POINTS); + glVertex3f(0., 0., 0.); + glEnd(); + float s = 10; + if (light_type != Omni) { + glBegin(GL_LINES); + QVector4D dir = QVector4D(direction); + if (raw_matrix) + dir = transform().inverted() * dir; + glVertex3f(0., 0., 0.); + glVertex3f(dir.x() * s, dir.y() * s, dir.z() * s); + glEnd(); + } + if (l) glEnable(GL_LIGHTING); +} + + +QDataStream & operator <<(QDataStream & s, const GLObjectBase * p) { + ChunkStream cs; + //qDebug() << "place" << p->name() << "..."; + cs << cs.chunk(1, int(p->type_)) << cs.chunk(2, p->accept_light) << cs.chunk(3, p->accept_fog) << cs.chunk(4, p->visible_) + << cs.chunk(5, p->cast_shadow) << cs.chunk(6, p->rec_shadow) << cs.chunk(7, p->raw_matrix) << cs.chunk(8, p->line_width) + << cs.chunk(9, int(p->render_mode)) << cs.chunk(10, p->material_) << cs.chunk(11, p->pos_) << cs.chunk(12, p->angles_) + << cs.chunk(13, p->scale_) << cs.chunk(14, p->mat_) << cs.chunk(15, p->vbo) << cs.chunk(16, p->children_.size()) + << cs.chunk(17, p->name_) << cs.chunk(18, p->meta); + //qDebug() << "place self done"; + if (p->type_ == GLObjectBase::glLight) { + //qDebug() << "place light ..."; + const Light * l = (const Light*)p; + cs << cs.chunk(100, l->direction) << cs.chunk(101, l->angle_start) << cs.chunk(102, l->angle_end) << cs.chunk(103, l->intensity) + << cs.chunk(104, l->decay_const) << cs.chunk(105, l->decay_linear) << cs.chunk(106, l->decay_quadratic) + << cs.chunk(107, l->decay_start) << cs.chunk(108, l->decay_end) << cs.chunk(109, int(l->light_type)); + } + if (p->type_ == GLObjectBase::glCamera) { + //qDebug() << "place camera ..."; + const Camera * c = (const Camera*)p; + cs << cs.chunk(200, c->aim_) << cs.chunk(201, c->fov_) << cs.chunk(202, c->depth_start) << cs.chunk(203, c->depth_end) + << cs.chunk(204, c->angle_limit_lower_xy) << cs.chunk(205, c->angle_limit_upper_xy) + << cs.chunk(206, c->mirror_x) << cs.chunk(207, c->mirror_y); + } + //qDebug() << "place" << p->name() << cs.data().size() << s.device()->size(); + s << cs.data(); + foreach (const GLObjectBase * c, p->children_) + s << c; + return s; +} +QDataStream & operator >>(QDataStream & s, GLObjectBase *& p) { + ChunkStream cs(s); + p = nullptr; + int ccnt = 0; + Light * l = nullptr; + Camera * c = nullptr; + //qDebug() << "read obj ..."; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: + { + GLObjectBase::Type type = (GLObjectBase::Type)cs.getData(); + switch (type) { + case GLObjectBase::glMesh: p = new GLObjectBase(); break; + case GLObjectBase::glLight: p = new Light(); l = (Light*)p; break; + case GLObjectBase::glCamera: p = new Camera(); c = (Camera*)p; break; + default : break; + } + if (p) p->type_ = type; + } + break; + case 2: if (p) p->accept_light = cs.getData(); break; + case 3: if (p) p->accept_fog = cs.getData(); break; + case 4: if (p) p->visible_ = cs.getData(); break; + case 5: if (p) p->cast_shadow = cs.getData(); break; + case 6: if (p) p->rec_shadow = cs.getData(); break; + case 7: if (p) p->raw_matrix = cs.getData(); break; + case 8: if (p) p->line_width = cs.getData(); break; + case 9: if (p) p->render_mode = (GLObjectBase::RenderMode)cs.getData(); break; + case 10: if (p) p->material_ = cs.getData(); break; + case 11: if (p) p->pos_ = cs.getData(); break; + case 12: if (p) p->angles_ = cs.getData(); break; + case 13: if (p) p->scale_ = cs.getData(); break; + case 14: if (p) p->mat_ = cs.getData(); break; + case 15: if (p) p->vbo = cs.getData(); break; + case 16: if (p) ccnt = cs.getData(); break; + case 17: if (p) p->name_ = cs.getData(); break; + case 18: if (p) p->meta = cs.getData(); break; + case 100: if (l) l->direction = cs.getData(); break; + case 101: if (l) l->angle_start = cs.getData(); break; + case 102: if (l) l->angle_end = cs.getData(); break; + case 103: if (l) l->intensity = cs.getData(); break; + case 104: if (l) l->decay_const = cs.getData(); break; + case 105: if (l) l->decay_linear = cs.getData(); break; + case 106: if (l) l->decay_quadratic = cs.getData(); break; + case 107: if (l) l->decay_start = cs.getData(); break; + case 108: if (l) l->decay_end = cs.getData(); break; + case 109: if (l) l->light_type = (Light::Type)cs.getData(); break; + case 200: if (c) c->setAim(cs.getData()); break; + case 201: if (c) c->setFOV(cs.getData()); break; + case 202: if (c) c->setDepthStart(cs.getData()); break; + case 203: if (c) c->setDepthEnd(cs.getData()); break; + case 204: if (c) c->setAngleLowerLimitXY(cs.getData()); break; + case 205: if (c) c->setAngleUpperLimitXY(cs.getData()); break; + case 206: if (c) c->mirror_x = cs.getData(); break; + case 207: if (c) c->mirror_y = cs.getData(); break; + } + } + //qDebug() << p->name() << ccnt; + for (int i = 0; i < ccnt; ++i) { + GLObjectBase * c = nullptr; + s >> c; + if (!c) continue; + c->parent_ = p; + p->children_ << c; + } + return s; +} diff --git a/test/qglview/globject.h b/test/qglview/globject.h new file mode 100644 index 0000000..362a04a --- /dev/null +++ b/test/qglview/globject.h @@ -0,0 +1,268 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLOBJECT_H +#define GLOBJECT_H + +#include "glvbo.h" +#include "glframebuffer.h" +#include "glmaterial.h" + +class Camera; +class QGLView; + +class GLObjectBase +{ + friend class QGLView; + friend class GLRendererBase; + friend QDataStream & operator <<(QDataStream & s, const GLObjectBase * p); + friend QDataStream & operator >>(QDataStream & s, GLObjectBase *& p); + friend GLObjectBase * loadFromQGLFile(const QString & filepath); +public: + enum Type {glMesh, glLight, glCamera, glParticlesSystem}; + enum Pass {Solid, Transparent, Reflection, User}; + enum GeomPrimitives {Triangles = GL_TRIANGLES, Quads = GL_QUADS}; + enum RenderMode {View = 0, Point = GL_POINT, Line = GL_LINE, Fill = GL_FILL}; + + explicit GLObjectBase(); + virtual ~GLObjectBase(); + + virtual GLObjectBase * clone(bool withChildren = true); + + QString name() const {return name_;} + void setName(const QString & name) {name_ = name;} + //virtual GLuint hList() {return list;} + virtual void init(); + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); + virtual void update() {} + bool isInit() const {return is_init;} + bool isTexturesLoaded() const {return is_tex_loaded;} + Type type() const {return type_;} + + RenderMode renderMode() const {return render_mode;} + void setRenderMode(RenderMode mode) {render_mode = mode;} + + float lineWidth() const {return line_width;} + void setLineWidth(const float & width) {line_width = width;} + + GLObjectBase * parent() {return parent_;} + void setParent(GLObjectBase * o) {parent_ = o;} + bool hasParent() const {return parent_ != nullptr;} + bool hasChildren() const {return children_.size() != 0;} + void setView(QGLView * v); + + void addChild(GLObjectBase * o); + void removeChild(GLObjectBase * o); + void removeChild(int index); + void clearChildren(bool deleteAll = false); + int childCount() const {return children_.size();} + GLObjectBase * child(int index); + GLObjectBase * child(const QString & name); + const GLObjectBase * child(int index) const; + const GLObjectBase * child(const QString & name) const; + QList children(bool all_ = false); + + bool isVisible() const {return visible_;} + bool isHidden() const {return !visible_;} + void setVisible(bool v) {visible_ = v;} + void setHidden(bool v) {visible_ = !v;} + void show() {visible_ = true;} + void hide() {visible_ = false;} + + bool isReceiveShadows() const {return rec_shadow;} + bool isCastShadows() const {return cast_shadow;} + void setReceiveShadows(bool on) {rec_shadow = on;} + void setCastShadows(bool on) {cast_shadow = on;} + + void move(const QVector3D & dv) {pos_ += dv; buildTransform();} + void moveTo(const QVector3D & dv) {pos_ = dv; buildTransform();} + void move(GLfloat dx, GLfloat dy, GLfloat dz = 0.) {move(QVector3D(dx, dy, dz)); buildTransform();} + void moveTo(GLfloat dx, GLfloat dy, GLfloat dz = 0.) {moveTo(QVector3D(dx, dy, dz)); buildTransform();} + void moveX(GLfloat o) {pos_.setX(pos_.x() + o); buildTransform();} + void moveY(GLfloat o) {pos_.setY(pos_.y() + o); buildTransform();} + void moveZ(GLfloat o) {pos_.setZ(pos_.z() + o); buildTransform();} + void setPosX(GLfloat o) {pos_.setX(o); buildTransform();} + void setPosY(GLfloat o) {pos_.setY(o); buildTransform();} + void setPosZ(GLfloat o) {pos_.setZ(o); buildTransform();} + void setPos(GLfloat x, GLfloat y, GLfloat z) {pos_ = QVector3D(x, y, z); buildTransform();} + void setPos(const QVector3D & p) {pos_ = p; buildTransform();} + void resetPos() {pos_ = QVector3D(0., 0., 0.); buildTransform();} + + QVector3D pos() const {return pos_;} + float posX() const {return pos_.x();} + float posY() const {return pos_.y();} + float posZ() const {return pos_.z();} + QVector3D worldPos() const {return (itransform_ * QVector4D(0, 0, 0, 1.)).toVector3D();} + QMatrix4x4 worldTransform() const {return itransform_;} + + QVector3D rotation() const {return angles_;} + float rotationX() const {return angles_.x();} + float rotationY() const {return angles_.y();} + float rotationZ() const {return angles_.z();} + void rotateX(GLfloat a); + void rotateY(GLfloat a); + void rotateZ(GLfloat a); + void setRotationX(GLfloat a); + void setRotationY(GLfloat a); + void setRotationZ(GLfloat a); + void setRotation(const QVector3D & a); + void resetRotation(); + + QVector3D scale() {return scale_;} + float scaleX() {return scale_.x();} + float scaleY() {return scale_.y();} + float scaleZ() {return scale_.z();} + void scale(const QVector3D & sv) {raw_matrix = false; scale_ *= sv; buildTransform();} + void scale(GLfloat sx, GLfloat sy, GLfloat sz) {raw_matrix = false; scale(QVector3D(sx, sy, sz)); buildTransform();} + void scale(GLfloat sx, GLfloat sy) {raw_matrix = false; scale(QVector3D(sx, sy, sy)); buildTransform();} + void scale(GLfloat sx) {raw_matrix = false; scale(QVector3D(sx, sx, sx)); buildTransform();} + void scaleX(GLfloat a) {raw_matrix = false; scale_.setX(scale_.x() + a); buildTransform();} + void scaleY(GLfloat a) {raw_matrix = false; scale_.setY(scale_.y() + a); buildTransform();} + void scaleZ(GLfloat a) {raw_matrix = false; scale_.setZ(scale_.z() + a); buildTransform();} + void setScale(const QVector3D & a) {raw_matrix = false; scale_ = a; buildTransform();} + void setScale(GLfloat a) {raw_matrix = false; scale_ = QVector3D(a, a, a); buildTransform();} + void setScaleX(GLfloat a) {raw_matrix = false; scale_.setX(a); buildTransform();} + void setScaleY(GLfloat a) {raw_matrix = false; scale_.setY(a); buildTransform();} + void setScaleZ(GLfloat a) {raw_matrix = false; scale_.setZ(a); buildTransform();} + void resetScale() {raw_matrix = false; scale_ = QVector3D(1., 1., 1.); buildTransform();} + + QMatrix4x4 transform() {return mat_;} + void setTransform(const QMatrix4x4 & t); + bool isRawMatrix() {return raw_matrix;} + + bool isAcceptLight() const {return accept_light;} + void setAcceptLight(bool yes) {accept_light = yes;} + + bool isAcceptFog() const {return accept_fog;} + void setAcceptFog(bool yes) {accept_fog = yes;} + + bool isSelected() const {return selected_;} + void setSelected(bool yes) {selected_ = yes;} + void select(); + void deselect() {selected_ = false;} + + bool isSelectable() const {return select_;} + void setSelectable(bool yes) {select_ = yes;} + /* + bool isWriteDepth() const {return write_depth_;} + void setWriteDepth(bool yes) {write_depth_ = yes;}*/ + + QColor color() const {return material_.color_diffuse;} + void setColor(const QColor & c) {material_.color_diffuse = c; checkPass();} + + GLenum srcAlpha() const {return blend_src;} + GLenum destAlpha() const {return blend_dest;} + void setSrcAlpha(GLenum mode) {blend_src = mode;} + void setDestAlpha(GLenum mode) {blend_dest = mode;} + + void setMaterial(const Material & m, bool with_children = false); + Material & material() {return material_;} + + const Box3D & boundingBox(bool withChildren = true) const {return bound;} + GLVBO & VBO() {return vbo;} + + void calculateBoundingBox(); + + void setProperty(const QString & pn, const QVariant & v); + QVariant property(const QString & pn, bool * exists = 0) const; + bool hasProperty(const QString & pn) const; + void removeProperty(const QString & pn); + + QVector3D pos_h; + QVector points, puvws; + QVector faces, uvws, norms; + QVector normals; + + //QVector d_vertices, d_normals, d_uvs; + +protected: + void addChildren(QList & list, GLObjectBase * where); + void loadTextures(bool with_children = false); + //void deleteTextures() {foreach (GLuint i, textures) currentQGLView->deleteTexture(i); textures.clear();} + void buildTransform(); + void initInternal(); + void render(int * id = nullptr, QMap * ids = nullptr, int sh_id_loc = 0); + void checkPass(); + virtual void localTransform(QMatrix4x4 & m); + QMatrix4x4 worldMatrix(QMatrix4x4 parent) const; + + int pass_; // Pass + bool is_init, is_tex_loaded, accept_light, accept_fog, /*write_depth_,*/ visible_, cast_shadow, rec_shadow, select_, selected_, raw_matrix; + bool is_root; + float line_width; + Type type_; + GeomPrimitives geom_prim; + RenderMode render_mode; + Material material_; + Box3D bound; + QVector3D pos_, angles_, scale_; + QList children_; + QList textures; + QMatrix4x4 itransform_, mat_; + //QColor color_; + QString name_; + GLenum blend_src, blend_dest; + GLObjectBase * parent_; + QGLViewBase * view_; + GLVBO vbo; + QVariantMap meta; + +}; + +inline bool operator <(const GLObjectBase & f, const GLObjectBase & s) {return f.pos_h.z() < s.pos_h.z();} + +class Light: public GLObjectBase { + friend class QGLView; + friend class GLRendererBase; +public: + enum Type {Omni, Directional, Cone}; + + Light(); + Light(const QVector3D & p, const QColor & c = Qt::white, float i = 1.); + virtual GLObjectBase * clone(bool withChildren = true); + virtual void init() {shadow_map.resize(512, 512); is_init = true;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); + + QVector3D direction, dir0, dir1; + float angle_start; + float angle_end; + float intensity; + float decay_const; + float decay_linear; + float decay_quadratic; + float decay_start; + float decay_end; + Type light_type; + GLFramebuffer shadow_map; + QMatrix4x4 shadow_matrix; + +protected: + +}; + +template +inline T globject_cast(GLObjectBase * object) {return reinterpret_cast(object);} + +template +inline T globject_cast(const GLObjectBase * object) {return reinterpret_cast(object);} + + +QDataStream & operator <<(QDataStream & s, const GLObjectBase * p); +QDataStream & operator >>(QDataStream & s, GLObjectBase *& p); + +#endif // GLOBJECT_H diff --git a/test/qglview/globject_editor.cpp b/test/qglview/globject_editor.cpp new file mode 100644 index 0000000..af7d3c7 --- /dev/null +++ b/test/qglview/globject_editor.cpp @@ -0,0 +1,151 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "globject_editor.h" +#include "ui_globject_editor.h" + + +GLObjectEditor::GLObjectEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::GLObjectEditor(); + ui->setupUi(this); + active = true; + object = 0; + rmodes << GLObjectBase::View << GLObjectBase::Point << GLObjectBase::Line << GLObjectBase::Fill; + ui->groupLight->setEnabled(false); +} + + +void GLObjectEditor::changeEvent(QEvent * e) { + return; + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void GLObjectEditor::setObject(GLObjectBase * o) { + object = o; + if (object == 0) { + ui->groupLight->setEnabled(false); + return; + } + ui->buttonDiscardRawMatrix->setEnabled(o->isRawMatrix()); + active = false; + ui->spinPosX->setValue(object->posX()); + ui->spinPosY->setValue(object->posY()); + ui->spinPosZ->setValue(object->posZ()); + ui->spinRotationX->setValue(object->rotationX()); + ui->spinRotationY->setValue(object->rotationY()); + ui->spinRotationZ->setValue(object->rotationZ()); + ui->spinScaleX->setValue(object->scaleX()); + ui->spinScaleY->setValue(object->scaleY()); + ui->spinScaleZ->setValue(object->scaleZ()); + ui->spinLineWidth->setValue(object->lineWidth()); + ui->checkVisible->setChecked(object->isVisible()); + ui->checkAcceptLight->setChecked(object->isAcceptLight()); + ui->checkAcceptFog->setChecked(object->isAcceptFog()); + ui->checkCastShadows->setChecked(object->isCastShadows()); + ui->checkReceiveShadows->setChecked(object->isReceiveShadows()); + ui->comboRenderMode->setCurrentIndex(rmodes.indexOf(object->renderMode())); + ui->groupLight->setEnabled(object->type() == GLObjectBase::glLight); + if (object->type() == GLObjectBase::glLight) { + Light * l = globject_cast(object); + //bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone; + ui->buttonLightColor->setColor(l->color()); + ui->comboLightType->setCurrentIndex(l->light_type); + ui->spinLightIntensity->setValue(l->intensity); + ui->spinLightDecayConst->setValue(l->decay_const); + ui->spinLightDecayLinear->setValue(l->decay_linear); + ui->spinLightDecayQuadratic->setValue(l->decay_quadratic); + ui->spinLightAngleStart->setValue(l->angle_start); + ui->spinLightAngleEnd->setValue(l->angle_end); + ui->spinLightDirectionX->setValue(l->direction.x()); + ui->spinLightDirectionY->setValue(l->direction.y()); + ui->spinLightDirectionZ->setValue(l->direction.z()); + } + active = true; +} + + +void GLObjectEditor::objectChanged() { + if (!active || object == 0) return; + if (!object->isRawMatrix()) { + object->setPosX(ui->spinPosX->value()); + object->setPosY(ui->spinPosY->value()); + object->setPosZ(ui->spinPosZ->value()); + object->setRotationX(ui->spinRotationX->value()); + object->setRotationY(ui->spinRotationY->value()); + object->setRotationZ(ui->spinRotationZ->value()); + object->setScaleX(ui->spinScaleX->value()); + object->setScaleY(ui->spinScaleY->value()); + object->setScaleZ(ui->spinScaleZ->value()); + } + object->setLineWidth(ui->spinLineWidth->value()); + object->setVisible(ui->checkVisible->isChecked()); + object->setAcceptLight(ui->checkAcceptLight->isChecked()); + object->setAcceptFog(ui->checkAcceptFog->isChecked()); + object->setCastShadows(ui->checkCastShadows->isChecked()); + object->setReceiveShadows(ui->checkReceiveShadows->isChecked()); + object->setRenderMode((GLObjectBase::RenderMode)rmodes[ui->comboRenderMode->currentIndex()]); + if (object->type() == GLObjectBase::glLight) { + Light * l = globject_cast(object); + //bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone; + l->setColor(ui->buttonLightColor->color()); + l->light_type = (Light::Type)ui->comboLightType->currentIndex(); + l->intensity = ui->spinLightIntensity->value(); + l->decay_const = ui->spinLightDecayConst->value(); + l->decay_linear = ui->spinLightDecayLinear->value(); + l->decay_quadratic = ui->spinLightDecayQuadratic->value(); + l->angle_start = ui->spinLightAngleStart->value(); + l->angle_end = ui->spinLightAngleEnd->value(); + l->direction = QVector3D(ui->spinLightDirectionX->value(), ui->spinLightDirectionY->value(), ui->spinLightDirectionZ->value()).normalized(); + } + emit changed(); +} + + +void GLObjectEditor::on_spinLightAngleStart_valueChanged(double v) { + if (ui->spinLightAngleEnd->value() < v) + ui->spinLightAngleEnd->setValue(v); +} + + +void GLObjectEditor::on_spinLightAngleEnd_valueChanged(double v) { + if (ui->spinLightAngleStart->value() > v) + ui->spinLightAngleStart->setValue(v); +} + + +void GLObjectEditor::on_buttonDiscardRawMatrix_clicked() { + if (!active || !object) return; + object->setPosX(ui->spinPosX->value()); + object->setPosY(ui->spinPosY->value()); + object->setPosZ(ui->spinPosZ->value()); + object->setRotationX(ui->spinRotationX->value()); + object->setRotationY(ui->spinRotationY->value()); + object->setRotationZ(ui->spinRotationZ->value()); + object->setScaleX(ui->spinScaleX->value()); + object->setScaleY(ui->spinScaleY->value()); + object->setScaleZ(ui->spinScaleZ->value()); + ui->buttonDiscardRawMatrix->setEnabled(false); +} diff --git a/test/qglview/globject_editor.h b/test/qglview/globject_editor.h new file mode 100644 index 0000000..7caa3dc --- /dev/null +++ b/test/qglview/globject_editor.h @@ -0,0 +1,55 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLOBJECT_EDITOR_H +#define GLOBJECT_EDITOR_H + +#include "globject.h" + +namespace Ui { + class GLObjectEditor; +}; + +class GLObjectEditor: public QWidget +{ + Q_OBJECT +public: + explicit GLObjectEditor(QWidget * parent = 0); + + void setObject(GLObjectBase * o); + +protected: + void changeEvent(QEvent * e); + + Ui::GLObjectEditor * ui; + bool active; + GLObjectBase * object; + QList rmodes; + +private slots: + void objectChanged(); + void on_spinLightAngleStart_valueChanged(double v); + void on_spinLightAngleEnd_valueChanged(double v); + void on_buttonDiscardRawMatrix_clicked(); + +signals: + void changed(); + +}; + +#endif // GLOBJECT_EDITOR_H diff --git a/test/qglview/globject_editor.ui b/test/qglview/globject_editor.ui new file mode 100644 index 0000000..de26c1d --- /dev/null +++ b/test/qglview/globject_editor.ui @@ -0,0 +1,1087 @@ + + + GLObjectEditor + + + + 0 + 0 + 329 + 707 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 2 + + + 2 + + + + + Position X + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Position Y + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Position Z + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Rotation X + + + + + + + Rotation Y + + + + + + + Rotation Z + + + + + + + -180.000000000000000 + + + 180.000000000000000 + + + + + + + -180.000000000000000 + + + 180.000000000000000 + + + + + + + -180.000000000000000 + + + 180.000000000000000 + + + + + + + Scale X + + + + + + + Scale Y + + + + + + + Scale Z + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Render mode + + + + + + + + View + + + + + Point + + + + + Line + + + + + Fill + + + + + + + + Visible + + + + + + + Accept light + + + + + + + Accept fog + + + + + + + Line width + + + + + + + 0.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Cast shadows + + + + + + + Receive shadows + + + + + + + Light + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 2 + + + 0 + + + + + Color + + + + + + + + + + Type + + + + + + + + Omni + + + + + Directional + + + + + Cone + + + + + + + + Intensity + + + + + + + 0.000000000000000 + + + 128.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay const + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay linear + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay quadratic + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Angle Start + + + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + + + + + Angle End + + + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + + + + + Direcion X + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Direcion Y + + + + + + + Direcion Z + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + + + false + + + Discard raw transform + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+
+ + + + spinPosX + valueChanged(double) + GLObjectEditor + objectChanged() + + + 312 + 9 + + + 321 + 5 + + + + + spinPosY + valueChanged(double) + GLObjectEditor + objectChanged() + + + 307 + 40 + + + 320 + 36 + + + + + spinPosZ + valueChanged(double) + GLObjectEditor + objectChanged() + + + 317 + 73 + + + 321 + 61 + + + + + spinRotationX + valueChanged(double) + GLObjectEditor + objectChanged() + + + 97 + 78 + + + 82 + 73 + + + + + spinRotationY + valueChanged(double) + GLObjectEditor + objectChanged() + + + 317 + 123 + + + 320 + 96 + + + + + spinRotationZ + valueChanged(double) + GLObjectEditor + objectChanged() + + + 292 + 128 + + + 321 + 125 + + + + + spinScaleX + valueChanged(double) + GLObjectEditor + objectChanged() + + + 295 + 156 + + + 321 + 150 + + + + + spinScaleY + valueChanged(double) + GLObjectEditor + objectChanged() + + + 273 + 177 + + + 321 + 175 + + + + + spinScaleZ + valueChanged(double) + GLObjectEditor + objectChanged() + + + 297 + 202 + + + 321 + 200 + + + + + checkVisible + toggled(bool) + GLObjectEditor + objectChanged() + + + 185 + 268 + + + 76 + 242 + + + + + checkAcceptLight + toggled(bool) + GLObjectEditor + objectChanged() + + + 104 + 277 + + + 61 + 261 + + + + + checkAcceptFog + toggled(bool) + GLObjectEditor + objectChanged() + + + 210 + 314 + + + 79 + 288 + + + + + checkCastShadows + toggled(bool) + GLObjectEditor + objectChanged() + + + 208 + 337 + + + 55 + 310 + + + + + checkReceiveShadows + toggled(bool) + GLObjectEditor + objectChanged() + + + 212 + 360 + + + 78 + 334 + + + + + spinLineWidth + valueChanged(double) + GLObjectEditor + objectChanged() + + + 303 + 366 + + + 321 + 359 + + + + + comboRenderMode + currentIndexChanged(int) + GLObjectEditor + objectChanged() + + + 299 + 228 + + + 321 + 228 + + + + + buttonLightColor + colorChanged(QColor) + GLObjectEditor + objectChanged() + + + 270 + 423 + + + 320 + 393 + + + + + comboLightType + currentIndexChanged(int) + GLObjectEditor + objectChanged() + + + 292 + 447 + + + 320 + 429 + + + + + spinLightIntensity + valueChanged(double) + GLObjectEditor + objectChanged() + + + 255 + 469 + + + 318 + 463 + + + + + spinLightDecayConst + valueChanged(double) + GLObjectEditor + objectChanged() + + + 303 + 492 + + + 320 + 489 + + + + + spinLightDecayLinear + valueChanged(double) + GLObjectEditor + objectChanged() + + + 289 + 511 + + + 318 + 517 + + + + + spinLightDecayQuadratic + valueChanged(double) + GLObjectEditor + objectChanged() + + + 299 + 535 + + + 325 + 543 + + + + + spinLightAngleStart + valueChanged(double) + GLObjectEditor + objectChanged() + + + 295 + 559 + + + 321 + 569 + + + + + spinLightAngleEnd + valueChanged(double) + GLObjectEditor + objectChanged() + + + 301 + 585 + + + 320 + 595 + + + + + spinLightDirectionX + valueChanged(double) + GLObjectEditor + objectChanged() + + + 302 + 612 + + + 332 + 607 + + + + + spinLightDirectionY + valueChanged(double) + GLObjectEditor + objectChanged() + + + 313 + 636 + + + 330 + 633 + + + + + spinLightDirectionZ + valueChanged(double) + GLObjectEditor + objectChanged() + + + 313 + 654 + + + 326 + 659 + + + + + + objectChanged() + +
diff --git a/test/qglview/glparticles_system.cpp b/test/qglview/glparticles_system.cpp new file mode 100644 index 0000000..ca83fa4 --- /dev/null +++ b/test/qglview/glparticles_system.cpp @@ -0,0 +1,215 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glparticles_system.h" + + +GLParticlesSystem::GLParticlesSystem(const QVector3D & pos): GLObjectBase() { + pass_ = GLObjectBase::Transparent; + freq = 40.f; + birthRate_ = 10.f; + lifeDuration_ = 2.f; + fade_time = 0.5f; + size_ = 1.f; + additionalSpeed = 0.f; + initialSpeed_ = 1.f; + need_birth = -1.f; + tex_rect.setRect(0., 0., 1., 1.); + tex_scale = QSizeF(); + emitterPosition_ = pos; + emitterDirection_.setZ(1.); + speedDirection_.setZ(1.); + speedDecay_ = initialAngle_ = enlargeSpeed_ = enlargeSpeedJitter_ = baseAngle_ = 0.f; + lifeDurationJitter_ = speedJitter_ = speedDirectionJitter_ = sizeJitter_ = angleJitter_ = 0.f; + active_ = birthEnabled_ = true; + is_diffuse_anim = add_vert_face = false; + emitterType_ = Cone; + tick_life = 1.f / freq; + tick_birth = birthRate_ / freq; +} + + +void GLParticlesSystem::update() { + //qDebug() << "update" << need_birth << tick_birth; + if (!active_) return; + //QMutexLocker locker(&mutex); + Particle cp(lifeDuration_); + if (birthEnabled_) need_birth += tick_birth; + qDebug() << "update" << particles.size(); + if (need_birth >= 1.f) { + cp.pos = emitterPosition_; + //qDebug() << "speed" << cp.speed; + cp.speedDecay = 1.f + speedDecay_; + for (int i = 0; i < floor(need_birth); ++i) { + cp.lifeDuration = lifeDuration_ + urand(lifeDurationJitter_); + switch (emitterType_) { + case Omni: + cp.speed = QVector3D(urand(), urand(), urand()).normalized() * initialSpeed_ * (1.f + urand(speedJitter_)); + break; + case Cone: case Box: + cp.speed = emitterDirection_ * initialSpeed_ * (1.f + urand(speedJitter_)); + cp.speed += orthToVector(cp.speed, speedDirectionJitter_); + break; + } + if (emitterType_ == Box) + cp.pos = emitterRect_.randomPoint(); + //qDebug() << "before" << cp.speed.length(); + lengthenVector(cp.speed, additionalSpeed); + //qDebug() << "after" << cp.speed.length(); + cp.size = size_ + urand(sizeJitter_); + cp.angle = initialAngle_ + urand(angleJitter_); + cp.enlargeSpeed = (enlargeSpeed_ + urand(enlargeSpeedJitter_)) * tick_life; + /*if (is_diffuse_anim) { + if (material_.diffuse.animation_frame_rate < 0 && animation->bitmaps.size() > 0) + cp.animationFrameRate = animation->bitmaps.size() / cp.lifeDuration; + else + cp.animationFrameRate = material_.diffuse.animation_frame_rate; + }*/ + if (tex_scale.isEmpty()) cp.tex_rect.setSize(tex_rect.size()); + else cp.tex_rect.setSize(tex_rect.size() * tex_scale); + cp.tex_rect.moveTopLeft(tex_rect.topLeft() + QPointF(uprand(tex_rect.width() - cp.tex_rect.width()), uprand(tex_rect.height() - cp.tex_rect.height()))); + //cp.tex_rect = tex_rect; + particles.push_back(cp); + } + need_birth -= floor(need_birth); + } + for (int i = 0; i < particles.size(); ++i) { + Particle & c(particles[i]); + foreach (const QVector3D & f, forces) + c.speed += f; + c.lifeCurrent += tick_life; + //qDebug() << "life" << c.lifeCurrent << c.lifeDuration; + if (c.lifeCurrent > c.lifeDuration) { + //qDebug() << "remove" << i; + particles.remove(i); + i--; + continue; + } + c.pos += c.speed * tick_life; + c.speed /= c.speedDecay; + c.size += c.enlargeSpeed; + //if (c.lifeCurrent > 1.) c.angle += urand(5.); + } +} + + +void GLParticlesSystem::draw(QOpenGLShaderProgram * prog, bool) { + if (particles.isEmpty()) return; + if (view_ == nullptr) return; + pass_ = GLObjectBase::Transparent; + Camera * camera(view_->camera()); + QVector3D apos = camera->pos(), dir = camera->direction(); + //qDebug() << dir; + //qDebug() << camera.angles(); + //qDebug() << camera.angle_xy; + GLfloat cxyc, czs, czc; + GLfloat dx, dy, cdx, cdy, cdz, a, tr_r = material_.color_diffuse.redF(), + tr_g = material_.color_diffuse.greenF(), + tr_b = material_.color_diffuse.blueF(), + tr_a = material_.color_diffuse.alphaF() * (1.f - material_.transparency); + //cxys = sin(camera.angle_xy * deg2rad); + cxyc = cosf(camera->angles_.y() * deg2rad); + czs = sinf(camera->angles_.z() * deg2rad); + czc = cosf(camera->angles_.z() * deg2rad); + + dx = -czc; + dy = czs; + vertices.clear(); + texcoords.clear(); + colors.clear(); + for (int i = 0; i < particles.size(); ++i) + //particles[i].pos_h.setZ((particles[i].pos - apos).lengthSquared()); + particles[i].pos_h.setZ(particles[i].pos.distanceToPlane(apos, dir)); + qSort(particles.begin(), particles.end()); + glBegin(GL_POINTS); + foreach (const Particle & i, particles) { + //glVertex3f(i.pos.x(), i.pos.y(), i.pos.z()); + a = (i.lifeDuration - i.lifeCurrent) / fade_time; + if (a > 1.f) a = 1.f; + a *= tr_a; + cdx = dx * i.size; + cdy = dy * i.size; + cdz = i.size; + vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() - cdz; + vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z() + cdz; + vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() + cdz; + vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z() - cdz; + cdx = i.size; + cdy = i.size; + texcoords << i.tex_rect.right() << i.tex_rect.top(); + texcoords << i.tex_rect.right() << i.tex_rect.bottom(); + texcoords << i.tex_rect.left() << i.tex_rect.bottom(); + texcoords << i.tex_rect.left() << i.tex_rect.top(); + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + if (add_vert_face) { + if (cxyc > 0.f) { + vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z(); + vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z(); + vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z(); + vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z(); + } else { + vertices << i.pos.x() - cdx << i.pos.y() - cdy << i.pos.z(); + vertices << i.pos.x() - cdx << i.pos.y() + cdy << i.pos.z(); + vertices << i.pos.x() + cdx << i.pos.y() + cdy << i.pos.z(); + vertices << i.pos.x() + cdx << i.pos.y() - cdy << i.pos.z(); + } + texcoords << i.tex_rect.right() << i.tex_rect.top(); + texcoords << i.tex_rect.right() << i.tex_rect.bottom(); + texcoords << i.tex_rect.left() << i.tex_rect.bottom(); + texcoords << i.tex_rect.left() << i.tex_rect.top(); + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + colors << tr_r << tr_g << tr_b << a; + } + } + glEnd(); + //bool cae = glIsEnabled(GL_COLOR_ARRAY), nae = glIsEnabled(GL_NORMAL_ARRAY); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glNormal3f(vn.x(), vn.y(), vn.z()); + glNormal3f(0., 0., 1.); + glDepthMask(false); + glEnable(GL_COLOR_ARRAY); + glDisable(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vertices.constData()); + glTexCoordPointer(2, GL_FLOAT, 0, texcoords.constData()); + glColorPointer(4, GL_FLOAT, 0, colors.constData()); + //glEnable(GL_ALPHA_TEST); + //glAlphaFunc(); + glDrawArrays(GL_QUADS, 0, vertices.size() / 3); + glDepthMask(true); + //glDisable(GL_ALPHA_TEST); + //if (!cae) glDisable(GL_COLOR_ARRAY); + //if (nae) glEnable(GL_NORMAL_ARRAY); +} + + + +GLParticlesSystem::Particle::Particle(float life_dur) { + size = 1.; + angle = lifeCurrent = 0.; + speedDecay = 0.; + lifeDuration = life_dur; + tex_rect = QRectF(0, 0, 1, 1); +} diff --git a/test/qglview/glparticles_system.h b/test/qglview/glparticles_system.h new file mode 100644 index 0000000..d57525a --- /dev/null +++ b/test/qglview/glparticles_system.h @@ -0,0 +1,153 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLPARTICLES_SYSTEM_H +#define GLPARTICLES_SYSTEM_H + +#include +#include "gltexture_manager.h" +#include "globject.h" +#include "glcamera.h" + +class GLParticlesSystem: public QObject, public GLObjectBase, protected QOpenGLFunctions +{ + Q_OBJECT + Q_PROPERTY(float birthRate READ birthRate WRITE setBirthRate) + Q_PROPERTY(float lifeDuration READ lifeDuration WRITE setLifeDuration) + Q_PROPERTY(float size READ size WRITE setSize) + Q_PROPERTY(float enlargeSpeed READ enlargeSpeed WRITE setEnlargeSpeed) + Q_PROPERTY(float initialAngle READ initialAngle WRITE setInitialAngle) + Q_PROPERTY(float initialSpeed READ initialSpeed WRITE setInitialSpeed) + Q_PROPERTY(float speedDecay READ speedDecay WRITE setSpeedDecay) + Q_PROPERTY(float baseAngle READ baseAngle WRITE setBaseAngle) + Q_PROPERTY(QVector3D speedDirection READ speedDirection WRITE setSpeedDirection) + Q_PROPERTY(QVector3D emitterPosition READ emitterPosition WRITE setEmitterPosition) + Q_PROPERTY(QVector3D emitterDirection READ emitterDirection WRITE setEmitterDirection) + Q_PROPERTY(float lifeDurationJitter READ lifeDurationJitter WRITE setLifeDurationJitter) + Q_PROPERTY(float speedJitter READ speedJitter WRITE setSpeedJitter) + Q_PROPERTY(float speedDirectionJitter READ speedDirectionJitter WRITE setSpeedDirectionJitter) + Q_PROPERTY(float sizeJitter READ sizeJitter WRITE setSizeJitter) + Q_PROPERTY(float enlargeSpeedJitter READ enlargeSpeedJitter WRITE setEnlargeSpeedJitter) + Q_PROPERTY(float angleJitter READ angleJitter WRITE setAngleJitter) + Q_PROPERTY(bool active READ isActive WRITE setActive) + Q_PROPERTY(bool birthEnabled READ isBirthEnabled WRITE setBirthEnabled) + Q_PROPERTY(float fadeTime READ fadeTime WRITE setFadeTime) +public: + GLParticlesSystem(const QVector3D & pos = QVector3D()); + ~GLParticlesSystem() {;} + + enum Type {Cone, Omni, Box}; + + struct Particle { + Particle(float life_dur = 40.); + QVector3D pos; + QVector3D pos_h; + QVector3D speed; + QRectF tex_rect; + float speedDecay; + float size; + float angle; + float enlargeSpeed; + float lifeDuration; + float lifeCurrent; + float animationFrameRate; + }; + + void update(); + void draw(QOpenGLShaderProgram * prog, bool); + + float birthRate() const {return birthRate_;} + float lifeDuration() const {return lifeDuration_;} + float size() const {return size_;} + float enlargeSpeed() const {return enlargeSpeed_;} + float initialAngle() const {return initialAngle_;} + float initialSpeed() const {return initialSpeed_;} + float speedDecay() const {return speedDecay_;} + float baseAngle() const {return baseAngle_;} + QVector3D speedDirection() const {return speedDirection_;} + QVector3D emitterPosition() const {return emitterPosition_;} + QVector3D emitterDirection() const {return emitterDirection_;} + Box3D emitterRect() const {return emitterRect_;} + float lifeDurationJitter() const {return lifeDurationJitter_;} + float speedJitter() const {return speedJitter_;} + float speedDirectionJitter() const {return speedDirectionJitter_;} + float sizeJitter() const {return sizeJitter_;} + float enlargeSpeedJitter() const {return enlargeSpeedJitter_;} + float angleJitter() const {return angleJitter_;} + bool isActive() const {return active_;} + bool isBirthEnabled() const {return birthEnabled_;} + GLParticlesSystem::Type emitterType() const {return emitterType_;} + float fadeTime() const {return fade_time;} + bool isAddVerticalFaceEnabled() const {return add_vert_face;} + + void setBirthRate(const float & arg) {birthRate_ = arg; tick_birth = birthRate_ / freq;} + void setLifeDuration(const float & arg) {lifeDuration_ = arg;} + void setSize(const float & arg) {size_ = arg;} + void setEnlargeSpeed(const float & arg) {enlargeSpeed_ = arg;} + void setInitialAngle(const float & arg) {initialAngle_ = arg;} + void setInitialSpeed(const float & arg) {initialSpeed_ = arg;} + void setBaseAngle(const float & arg) {baseAngle_ = arg;} + void setSpeedDecay(const float & arg) {speedDecay_ = arg;} + void setSpeedDirection(const QVector3D & arg) {speedDirection_ = arg;} + void setEmitterPosition(const QVector3D & arg) {emitterPosition_ = arg;} + void setEmitterDirection(const QVector3D & arg) {emitterDirection_ = arg.normalized();} + void setEmitterRect(const Box3D & arg) {emitterRect_ = arg;} + void setLifeDurationJitter(const float & arg) {lifeDurationJitter_ = arg;} + void setSpeedJitter(const float & arg) {speedJitter_ = arg;} + void setSpeedDirectionJitter(const float & arg) {speedDirectionJitter_ = arg;} + void setSizeJitter(const float & arg) {sizeJitter_ = arg;} + void setEnlargeSpeedJitter(const float & arg) {enlargeSpeedJitter_ = arg;} + void setActive(const bool & arg) {active_ = arg;} + void setAngleJitter(const float & arg) {angleJitter_ = arg;} + void setBirthEnabled(const bool & arg) {birthEnabled_ = arg;} + void setEmitterType(const GLParticlesSystem::Type & arg) {emitterType_ = arg;} + void setFadeTime(const float & arg) {fade_time = arg;} + void setAddVerticalFaceEnabled(const bool & arg) {add_vert_face = arg;} + void setTextureRect(const QRectF & arg) {tex_rect = arg;} + void setTextureScale(const float & x, const float & y) {tex_scale = QSizeF(x, y);} + void setTextureScale(const QSizeF & arg) {tex_scale = arg;} + + void addForce(const QVector3D & f) {forces << f;} + void birthParticles(int count) {need_birth += count;} + + float frequency() const {return freq;} + void setFrequency(const float & f) {freq = f;} + + float additionalSpeed; + +private: + QVector3D speedDirection_, emitterPosition_, emitterDirection_; + QRectF tex_rect; + QSizeF tex_scale; + Box3D emitterRect_; + QMutex mutex; + GLParticlesSystem::Type emitterType_; + GLTextureManager::Animation * animation; + QVector vertices, texcoords, colors; + QVector particles; + QVector forces; + float birthRate_, initialSpeed_, speedDecay_, lifeDuration_, size_, freq, need_birth, tick_birth, tick_life, fade_time; + float lifeDurationJitter_, speedJitter_, speedDirectionJitter_, sizeJitter_, angleJitter_, initialAngle_; + float enlargeSpeed_, enlargeSpeedJitter_, baseAngle_; + bool active_, birthEnabled_, is_diffuse_anim, add_vert_face; + +}; + +inline bool operator <(const GLParticlesSystem::Particle & f, const GLParticlesSystem::Particle & s) {return f.pos_h.z() > s.pos_h.z();} + +#endif // GLPARTICLES_SYSTEM_H diff --git a/test/qglview/glprimitives.cpp b/test/qglview/glprimitives.cpp new file mode 100644 index 0000000..7d307db --- /dev/null +++ b/test/qglview/glprimitives.cpp @@ -0,0 +1,220 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glprimitives.h" + + +void GLPrimitivePoint::draw(QOpenGLShaderProgram * prog, bool simplest) { + glPointSize(sz); + glColor3f(material_.color_diffuse.redF(), material_.color_diffuse.greenF(), material_.color_diffuse.blueF()); + glBegin(GL_POINTS); + glVertex3d(0., 0., 0.); + glEnd(); +} + + + +void GLPrimitiveLine::draw(QOpenGLShaderProgram * prog, bool simplest) { + glColor3f(material_.color_diffuse.redF(), material_.color_diffuse.greenF(), material_.color_diffuse.blueF()); + glBegin(GL_LINES); + glVertex3f(p0.x(), p0.y(), p0.z()); + glVertex3f(p1.x(), p1.y(), p1.z()); + glEnd(); +} + + + +GLPrimitiveCube::GLPrimitiveCube(float width, float length, float height, QVector3D pos): GLObjectBase() { + geom_prim = Quads; + w = width; + l = length; + h = height; + moveTo(pos); + //init(); +} + + +void GLPrimitiveCube::init() { + float hw = w / 2.f, hl = l / 2.f, hh = h / 2.f; + //list = glGenLists(1); + //glNewList(list, GL_COMPILE); + //glColor4d(material_.color_diffuse.redF(), material_.color_diffuse.greenF(), material_.color_diffuse.blueF(), material_.color_diffuse.alphaF()); + vbo.init(); + QVector & d_vertices(vbo.vertices()), & d_normals(vbo.normals()), & d_uvs(vbo.texcoords()); + d_vertices.clear(); + d_normals.clear(); + d_uvs.clear(); + for (int i = 0; i < 4; ++i) + d_normals << 0. << -1. << 0.; + d_vertices << -hw << -hl << -hh; + d_vertices << hw << -hl << -hh; + d_vertices << hw << -hl << hh; + d_vertices << -hw << -hl << hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + for (int i = 0; i < 4; ++i) + d_normals << 0. << 1. << 0.; + d_vertices << -hw << hl << -hh; + d_vertices << -hw << hl << hh; + d_vertices << hw << hl << hh; + d_vertices << hw << hl << -hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + for (int i = 0; i < 4; ++i) + d_normals << -1. << 0. << 0.; + d_vertices << -hw << -hl << -hh; + d_vertices << -hw << -hl << hh; + d_vertices << -hw << hl << hh; + d_vertices << -hw << hl << -hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + for (int i = 0; i < 4; ++i) + d_normals << 1. << 0. << 0.; + d_vertices << hw << -hl << -hh; + d_vertices << hw << hl << -hh; + d_vertices << hw << hl << hh; + d_vertices << hw << -hl << hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + for (int i = 0; i < 4; ++i) + d_normals << 0. << 0. << -1.; + d_vertices << -hw << -hl << -hh; + d_vertices << -hw << hl << -hh; + d_vertices << hw << hl << -hh; + d_vertices << hw << -hl << -hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + for (int i = 0; i < 4; ++i) + d_normals << 0. << 0. << 1.; + d_vertices << -hw << -hl << hh; + d_vertices << hw << -hl << hh; + d_vertices << hw << hl << hh; + d_vertices << -hw << hl << hh; + d_uvs << 0. << 0. << 1. << 0. << 1. << 1. << 0. << 1.; + is_init = true; + vbo.rebuffer(); +} + + +GLPrimitiveEllipsoid::GLPrimitiveEllipsoid(float width, float length, float height, int seg_wl, int seg_h, QVector3D pos) { + geom_prim = GLObjectBase::Triangles; + w = width; + l = length; + h = height; + swl = seg_wl; + sh = seg_h; + moveTo(pos); + //init(); +} + + +void GLPrimitiveEllipsoid::putTriangle(const QVector3D & v0, const QVector3D & v1, const QVector3D & v2) { + vbo.vertices() << v0.x() << v0.y() << v0.z() << v1.x() << v1.y() << v1.z() << v2.x() << v2.y() << v2.z(); + QVector3D n = QVector3D::normal(v1 - v0, v2 - v0); + for (int i = 0; i < 3; ++i) + vbo.normals() << n.x() << n.y() << n.z(); + return; + QVector3D s(w, l, h); + n = (v0 * s).normalized(); vbo.normals() << n.x() << n.y() << n.z(); + n = (v1 * s).normalized(); vbo.normals() << n.x() << n.y() << n.z(); + n = (v2 * s).normalized(); vbo.normals() << n.x() << n.y() << n.z(); +} + + +void GLPrimitiveEllipsoid::init() { + QVector points; + vbo.clear(); + vbo.init(); + int ret = 0; + int hseg = sh + 1, wlseg = swl + 1; + float crw, crl, a, ch, twl; + QVector3D cp(0., 0., -h / 2.f); + points << cp; + for (int i = 1; i < hseg; i++) { + ch = -cosf((float)i / hseg * float(M_PI)); + cp.setZ(ch * h / 2.f); + twl = sqrtf(1.f - ch * ch) / 2.f; + crw = twl * w; + crl = twl * l; + for (int j = 0; j < wlseg * 2; j++) { + a = (float)j / wlseg * float(M_PI); + cp.setY(crw * sinf(a)); + cp.setX(crl * cosf(a)); + points << cp; + ret = points.size() - 1; + if (i == 1) + if (j > 0) putTriangle(points[0], points[ret], points[ret - 1]); + if (j > 0) { + if (i > 1) { + putTriangle(points[ret - wlseg * 2 - 1], points[ret], points[ret - 1]); + putTriangle(points[ret - wlseg * 2], points[ret], points[ret - wlseg * 2 - 1]); + } + } + } + if (i == 1) putTriangle(points[0], points[ret - wlseg * 2 + 1], points[ret]); + else { + putTriangle(points[ret - wlseg * 2 + 1], points[ret], points[ret - wlseg * 2]); + putTriangle(points[ret - wlseg * 2 + 1], points[ret - wlseg * 2], points[ret - wlseg * 4 + 1]); + } + } + points << QVector3D(0., 0., h / 2.f); + ret = points.size() - 1; + putTriangle(points[ret - 1], points[ret - wlseg * 2], points[ret]); + for (int j = 1; j < wlseg * 2; j++) + if (j > 0) putTriangle(points[ret - wlseg * 2 + j - 1], points[ret - wlseg * 2 + j], points[ret]); + is_init = true; + vbo.rebuffer(); +} + + +void GLPrimitiveAxis::draw(QOpenGLShaderProgram * prog, bool simplest) { + float bs = 1.f; + float as = 0.1f; + float aw = 0.07f; + float cr_x = 0.8f, cg_y = 0.75f, cb_z = 0.8f; + glBegin(GL_LINES); + glColor3f(cr_x, 0, 0); + glVertex3f(-bs, 0, 0); + glVertex3f(bs, 0, 0); + glVertex3f(bs, 0, 0); + glVertex3f(bs-as, aw, 0); + glVertex3f(bs, 0, 0); + glVertex3f(bs-as, -aw, 0); + glVertex3f(bs, 0, 0); + glVertex3f(bs-as, 0, aw); + glVertex3f(bs, 0, 0); + glVertex3f(bs-as, 0, -aw); + glColor3f(0, cg_y, 0); + glVertex3f(0, -bs, 0); + glVertex3f(0, bs, 0); + glVertex3f(0, bs, 0); + glVertex3f(0, bs-as, aw); + glVertex3f(0, bs, 0); + glVertex3f(0, bs-as, -aw); + glVertex3f(0, bs, 0); + glVertex3f(aw, bs-as, 0); + glVertex3f(0, bs, 0); + glVertex3f(-aw, bs-as, 0); + glColor3f(0, 0, cb_z); + glVertex3f(0, 0, -bs); + glVertex3f(0, 0, bs); + glVertex3f(0, 0, bs); + glVertex3f(aw, 0, bs-as); + glVertex3f(0, 0, bs); + glVertex3f(-aw, 0, bs-as); + glVertex3f(0, 0, bs); + glVertex3f(0, aw, bs-as); + glVertex3f(0, 0, bs); + glVertex3f(0, -aw, bs-as); + glEnd(); +} diff --git a/test/qglview/glprimitives.h b/test/qglview/glprimitives.h new file mode 100644 index 0000000..ad3e8f1 --- /dev/null +++ b/test/qglview/glprimitives.h @@ -0,0 +1,85 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLPRIMITIVE_CUBE_H +#define GLPRIMITIVE_CUBE_H + +#include "globject.h" + + +class GLPrimitivePoint: public GLObjectBase +{ +public: + GLPrimitivePoint(double size = 1., QVector3D pos = QVector3D()) {sz = 8.;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); +private: + double sz; +}; + + + + +class GLPrimitiveLine: public GLObjectBase +{ +public: + GLPrimitiveLine(QVector3D p0_ = QVector3D(), QVector3D p1_ = QVector3D()) {p0 = p0_; p1 = p1_;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); + QVector3D point0() const {return p0;} + QVector3D point1() const {return p1;} + void setPoint0(const QVector3D & p) {p0 = p;} + void setPoint1(const QVector3D & p) {p1 = p;} +private: + QVector3D p0, p1; +}; + + + + +class GLPrimitiveCube: public GLObjectBase +{ +public: + GLPrimitiveCube(float width = 1., float length = 1., float height = 1., QVector3D pos = QVector3D()); + virtual void init(); +private: + float w, l, h; +}; + + + + +class GLPrimitiveEllipsoid: public GLObjectBase +{ +public: + GLPrimitiveEllipsoid(float width = 1., float length = 1., float height = 1., int seg_wl = 10, int seg_h = 10, QVector3D pos = QVector3D()); + virtual void init(); +private: + void putTriangle(const QVector3D & v0, const QVector3D & v1, const QVector3D & v2); + float w, l, h; + int swl, sh; +}; + + +class GLPrimitiveAxis: public GLObjectBase +{ +public: + GLPrimitiveAxis() {accept_fog = accept_light = cast_shadow = rec_shadow = select_ = false;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); +}; + + +#endif // GLPRIMITIVE_CUBE_H diff --git a/test/qglview/glrendererbase.cpp b/test/qglview/glrendererbase.cpp new file mode 100644 index 0000000..8eac634 --- /dev/null +++ b/test/qglview/glrendererbase.cpp @@ -0,0 +1,321 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glrendererbase.h" +#include "globject.h" +#include "qglview.h" + + +GLRendererBase::GLRendererBase(QGLView * view_): view(*view_) { + white_image = QImage(1, 1, QImage::Format_ARGB32); + white_image.fill(0xFFFFFFFF); + white_image_id = 0; + violent_image = QImage(1, 1, QImage::Format_ARGB32); + violent_image.fill(QColor(127, 127, 255)); + violent_image_id = 0; +} + + +void GLRendererBase::setupLight(const Light & l, int inpass_index, int gl_index) { + QVector3D lp = l.worldPos(), ld = (l.itransform_ * QVector4D(l.direction, 0.)).toVector3D().normalized(); + GLfloat pos[] = {0.f, 0.f, 0.f, 0.f}; + GLfloat dir[] = {0.f, 0.f, 0.f}; + GLfloat col[] = {0.f, 0.f, 0.f}; + pos[0] = l.light_type == Light::Directional ? -l.direction.x() : lp.x(); + pos[1] = l.light_type == Light::Directional ? -l.direction.y() : lp.y(); + pos[2] = l.light_type == Light::Directional ? -l.direction.z() : lp.z(); + pos[3] = l.light_type == Light::Directional ? 0. : 1.; + dir[0] = ld.x(); + dir[1] = ld.y(); + dir[2] = ld.z(); + col[0] = l.visible_ ? l.color().redF() * l.intensity : 0.f; + col[1] = l.visible_ ? l.color().greenF() * l.intensity : 0.f; + col[2] = l.visible_ ? l.color().blueF() * l.intensity : 0.f; + glEnable(gl_index); + //glLightfv(gl_index, GL_AMBIENT, ambient); + glLightfv(gl_index, GL_DIFFUSE, col); + glLightfv(gl_index, GL_SPECULAR, col); + glLightfv(gl_index, GL_POSITION, pos); + glLightf(gl_index, GL_CONSTANT_ATTENUATION, l.decay_const); + glLightf(gl_index, GL_LINEAR_ATTENUATION, l.decay_linear); + glLightf(gl_index, GL_QUADRATIC_ATTENUATION, l.decay_quadratic); + if (l.light_type == Light::Cone) { + glLightfv(gl_index, GL_SPOT_DIRECTION, dir); + glLightf(gl_index, GL_SPOT_CUTOFF, l.angle_end / 2.f); + glLightf(gl_index, GL_SPOT_EXPONENT, (1.f - piClamp((l.angle_end - l.angle_start) / (l.angle_end + 0.001f), 0., 1.f)) * 128.f); + } else { + glLightf(gl_index, GL_SPOT_CUTOFF, 180.); + } + +} + + +void GLRendererBase::setupAmbientLight(const QColor & a, bool first_pass) { + GLfloat ambient[] = {0.0f, 0.0f, 0.0f, 1.f}; + if (first_pass) { + ambient[0] = view.ambientColor_.redF(); + ambient[1] = view.ambientColor_.greenF(); + ambient[2] = view.ambientColor_.blueF(); + } + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); +} + + +void GLRendererBase::setupShadersLights(int lights_count) { + /*foreach (QOpenGLShaderProgram * i, view.shaders_ppl) { + i->bind(); + i->setUniformValue("lightsCount", lights_count); + i->setUniformValue("acc_light", lights_count > 0); + //i->setUniformValue("mat", mvm); + }*/ +} + + +#define BIND_TEXTURE(ch, map) if (rp.prev_tex[ch] != mat.map.bitmap_id) { \ + rp.prev_tex[ch] = mat.map.bitmap_id; \ + glActiveTexture(GL_TEXTURE0 + ch); glBindTexture(GL_TEXTURE_2D, mat.map.bitmap_id); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view.feature(QGLView::qglAnisotropicLevel).toInt());} + +void GLRendererBase::setupTextures(GLObjectBase & o, GLRendererBase::RenderingParameters & rp, bool first_object) { + if (first_object) { + view.glReleaseTextures(); + return; + } + setupShadersTextures(o, rp); + Material & mat(o.material_); + if (rp.light) { + if (o.accept_light) {if (!rp.prev_light) {glSetLightEnabled(true); rp.prev_light = true;}} + else {if (rp.prev_light) {glSetLightEnabled(false); rp.prev_light = false;}} + } + if (rp.fog) { + if (o.accept_fog) {if (!rp.prev_fog) {glSetFogEnabled(true); rp.prev_fog = true;}} + else {if (rp.prev_fog) {glSetFogEnabled(false); rp.prev_fog = false;}} + } + if (rp.textures) { + BIND_TEXTURE(0, map_diffuse) + BIND_TEXTURE(1, map_normal) + BIND_TEXTURE(2, map_relief) + BIND_TEXTURE(3, map_self_illumination) + BIND_TEXTURE(4, map_specularity) + BIND_TEXTURE(5, map_specular) + glActiveTexture(GL_TEXTURE0); + } +} + +#undef BIND_TEXTURE + + +void GLRendererBase::setupLights(int pass, int lights_per_pass) { + int light_start, light_end, lmax; + light_start = pass * lights_per_pass; + light_end = qMin((pass + 1) * lights_per_pass, view.lights_.size()); + setupAmbientLight(view.ambientColor_, pass == 0); + if (!view.lights_.isEmpty()) { + setupShadersLights(light_end - light_start); + for (int i = light_start; i < light_end; ++i) + setupLight(*view.lights_[i], i - light_start, GL_LIGHT0 + i - light_start); + lmax = light_start + 8; + for (int i = light_end; i < lmax; ++i) + glDisable(GL_LIGHT0 + i - light_start); + } else { + setupShadersLights(0); + for (int i = 0; i < 8; ++i) + glDisable(GL_LIGHT0 + i); + } +} + + +void GLRendererBase::applyFilteringParameters() { + if (view.isFeatureEnabled(QGLView::qglLinearFiltering)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view.feature(QGLView::qglAnisotropicLevel).toInt()); +} + + +void GLRendererBase::renderObjects(int pass, int light_pass, void * shaders, bool textures, bool light, bool fog) { + RenderingParameters rpl; + rpl.pass = pass; + rpl.light_pass = light_pass; + rpl.shaders = shaders; + rpl.textures = textures; + rpl.light = rpl.prev_light = light; + rpl.fog = rpl.prev_fog = fog; + rpl.view_matrix = rp.view_matrix; + rpl.prev_view_matrix = rp.prev_view_matrix; + rpl.proj_matrix = rp.proj_matrix; + rpl.prev_proj_matrix = rp.prev_proj_matrix; + rpl.cam_offset_matrix = view.camera()->offsetMatrix(); + //qDebug() << "view:" << rp.view_matrix; + for (int i = 0; i < 32; ++i) rpl.prev_tex[i] = 0; + setupTextures(view.objects_, rpl, true); + glSetLightEnabled(rpl.prev_light); + glSetFogEnabled(rpl.prev_fog); + glSetCapEnabled(GL_TEXTURE_2D, rpl.textures); + glSetCapEnabled(GL_BLEND, pass == GLObjectBase::Transparent); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_CUBE_MAP); + glPushMatrix(); + renderSingleObject(view.objects_, rpl); + glPopMatrix(); +} + + +void GLRendererBase::renderSingleObject(GLObjectBase & o, RenderingParameters & rpl) { + if (!o.isInit()) + o.init(); + if (!o.isTexturesLoaded()) + o.loadTextures(); + if (!o.visible_) return; + if (rpl.pass == o.pass_) { + Material & mat(o.material_); + QMatrix4x4 curview = rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_, prevview = rpl.prev_view_matrix * rpl.cam_offset_matrix * o.itransform_; + setupTextures(o, rpl, false); + mat.apply((QOpenGLShaderProgram*)rpl.shaders); + glSetPolygonMode(o.render_mode != GLObjectBase::View ? o.render_mode : (view.rmode != GLObjectBase::View ? view.rmode : GL_FILL)); + glLineWidth(o.line_width > 0.f ? o.line_width : view.lineWidth_); + glPointSize(o.line_width > 0.f ? o.line_width : view.lineWidth_); + o.update(); + if (o.pass_ == GLObjectBase::Transparent) { + glActiveTexture(GL_TEXTURE0 + 3); + if (mat.reflectivity > 0.f) { + glEnable(GL_TEXTURE_CUBE_MAP); + if (!mat.map_reflection.isEmpty()) mat.map_reflection.bind(); + else glDisable(GL_TEXTURE_CUBE_MAP); + } else glDisable(GL_TEXTURE_CUBE_MAP); + if (rpl.light_pass > 0) glDisable(GL_TEXTURE_CUBE_MAP); + GLfloat gm[16], bc[4] = {mat.reflectivity, mat.reflectivity, mat.reflectivity, mat.reflectivity}; + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, bc); + glGetFloatv(GL_MODELVIEW_MATRIX, gm); + glMatrixMode(GL_TEXTURE); + ///glLoadTransposeMatrixf(gm); + glScalef(-1., -1., -1.); + glMatrixMode(GL_MODELVIEW); + glActiveTexture(GL_TEXTURE0); + } + if (rpl.shaders) { + //qDebug() << o.name() << curview << curview.determinant(); + setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, curview, rpl.prev_proj_matrix, prevview); + } else { + glMatrixMode(GL_MODELVIEW); + setGLMatrix(curview); + } + o.draw((QOpenGLShaderProgram*)rpl.shaders); + } + foreach (GLObjectBase * i, o.children_) + renderSingleObject(*i, rpl); +} + + +void GLRendererBase::renderShadow(Light * l, QOpenGLShaderProgram * prog, QMatrix4x4 mat) { + Camera cam; + QVector3D wp = l->worldPos(); + cam.setPos(wp); + cam.setAim(wp + (/*l->worldTransform() */ QVector4D(l->direction)).toVector3D()); + cam.setDepthStart(view.camera()->depthStart()); + cam.setDepthEnd(view.camera()->depthEnd()); + cam.setFOV(l->angle_end); + cam.apply(1.); + /*cam.rotateXY(l->angle_end); + QVector3D rdir = l->direction * cos(l->angle_end / 2. * deg2rad); + l->dir0 = cam.direction() - rdir; + cam.rotateXY(-l->angle_end); + cam.rotateZ(l->angle_end); + l->dir1 = cam.direction() - rdir;*/ + //qDebug() << rdir << l->dir0 << l->dir1; + RenderingParameters rpl; + rpl.pass = GLObjectBase::Solid; + rpl.shaders = prog; + rpl.textures = rpl.light = rpl.fog = false; + rpl.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + rpl.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); + rpl.cam_offset_matrix = cam.offsetMatrix(); + QMatrix4x4 mbias; + mbias.scale(0.5, 0.5, 0.5); + mbias.translate(1., 1., 1.); + l->shadow_matrix = mbias*rpl.proj_matrix*rpl.view_matrix*rpl.cam_offset_matrix*mat;//;// * mbias; + //qDebug() << mbias; + //glPushMatrix(); + renderSingleShadow(view.objects_, rpl); + //glPopMatrix(); +} + + +void GLRendererBase::renderSingleShadow(GLObjectBase & o, RenderingParameters & rpl) { + if (!o.isInit()) + o.init(); + if (!o.visible_) return; + if (rpl.shaders) { + //qDebug() << o.name() << curview << curview.determinant(); + setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); + } else { + glMatrixMode(GL_MODELVIEW); + setGLMatrix(rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); + } + glPolygonMode(GL_FRONT_AND_BACK, o.render_mode != GLObjectBase::View ? o.render_mode : (view.rmode != GLObjectBase::View ? view.rmode : GL_FILL)); + glLineWidth(o.line_width > 0.f ? o.line_width : view.lineWidth_); + glPointSize(o.line_width > 0.f ? o.line_width : view.lineWidth_); + o.draw((QOpenGLShaderProgram*)rpl.shaders, true); + foreach (GLObjectBase * i, o.children_) + renderSingleShadow(*i, rpl); +} + + + + +GLRendererBase::RenderingParameters::RenderingParameters() { + shaders = nullptr; + cur_shader = nullptr; +} + + +void GLRendererBase::RenderingParameters::prepare() { + proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); + view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + viewproj_matrix = proj_matrix * view_matrix; + normal_matrix = view_matrix.normalMatrix(); + proj_matrix_i = proj_matrix.inverted(); + view_matrix_i = view_matrix.inverted(); + viewproj_matrix_i = viewproj_matrix.inverted(); +} + + +void GLRendererBase::RenderingParameters::setUniform(QOpenGLShaderProgram * prog) { + if (!prog) return; + prog->setUniformValue("qgl_ModelViewMatrix", view_matrix); + prog->setUniformValue("qgl_ProjectionMatrix", proj_matrix); + prog->setUniformValue("qgl_ModelViewProjectionMatrix", viewproj_matrix); + prog->setUniformValue("qgl_NormalMatrix", normal_matrix); + prog->setUniformValue("qgl_ModelViewMatrixInverse", view_matrix_i); + prog->setUniformValue("qgl_ProjectionMatrixInverse", proj_matrix_i); + prog->setUniformValue("qgl_ModelViewProjectionMatrixInverse", viewproj_matrix_i); + prog->setUniformValue("qgl_ModelViewMatrixTranspose", view_matrix.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj_matrix.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", viewproj_matrix.transposed()); + prog->setUniformValue("qgl_ModelViewMatrixInverseTranspose", view_matrix_i.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixInverseTranspose", proj_matrix_i.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixInverseTranspose", viewproj_matrix_i.transposed()); +} diff --git a/test/qglview/glrendererbase.h b/test/qglview/glrendererbase.h new file mode 100644 index 0000000..74542f2 --- /dev/null +++ b/test/qglview/glrendererbase.h @@ -0,0 +1,80 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLRENDERERBASE_H +#define GLRENDERERBASE_H + +#include "glcamera.h" +#include "glshaders.h" + +class GLRendererBase: public QObject , protected QOpenGLExtraFunctions +{ + friend class QGLView; + Q_OBJECT +public: + GLRendererBase(QGLView * view_); + virtual void prepareScene() {;} + virtual void renderScene() = 0; + + struct RenderingParameters { + RenderingParameters(); + void prepare(); + void setUniform(QOpenGLShaderProgram * prog); + int pass; + int light_pass; + bool light; + bool fog; + bool textures; + bool prev_light; + bool prev_fog; + GLuint prev_tex[32]; + void * shaders; + QMatrix4x4 view_matrix, view_matrix_i, prev_view_matrix; + QMatrix4x4 proj_matrix, proj_matrix_i, prev_proj_matrix; + QMatrix4x4 viewproj_matrix, viewproj_matrix_i; + QMatrix3x3 normal_matrix; + QMatrix4x4 cam_offset_matrix; + QOpenGLShaderProgram * cur_shader; + }; + + RenderingParameters rp; + +protected: + virtual void setupLight(const Light & l, int inpass_index, int gl_index); + virtual void setupAmbientLight(const QColor & a, bool first_pass); + virtual void setupShadersLights(int lights_count); + virtual void setupTextures(GLObjectBase & object, GLRendererBase::RenderingParameters & rp, bool first_object = false); + virtual void setupShadersTextures(GLObjectBase & object, GLRendererBase::RenderingParameters & rp) {} + virtual void reloadShaders() {} + virtual void init(int width, int height) {} + virtual void resize(int width, int height) {} + + void setupLights(int pass, int lights_per_pass); + inline void applyFilteringParameters(); + void renderObjects(int pass, int light_pass, void * shaders = 0, bool textures = true, bool light = true, bool fog = true); + void renderSingleObject(GLObjectBase & o, RenderingParameters & rpl); + void renderShadow(Light * l, QOpenGLShaderProgram * prog = 0, QMatrix4x4 mat = QMatrix4x4()); + void renderSingleShadow(GLObjectBase & o, RenderingParameters & rpl); + + QGLView & view; + QImage white_image, violent_image; + GLuint white_image_id, violent_image_id; + +}; + +#endif // GLRENDERERBASE_H diff --git a/test/qglview/glshaders.cpp b/test/qglview/glshaders.cpp new file mode 100644 index 0000000..151e77b --- /dev/null +++ b/test/qglview/glshaders.cpp @@ -0,0 +1,253 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "qglview.h" + +const char qgl_vertex_head[] = + "in vec3 _qgl_Vertex;\n" + "in vec3 qgl_Normal;\n" + "in vec3 qgl_Tangent;\n" + "in vec3 qgl_Bitangent;\n" + "in vec2 qgl_Texture;\n" + "in vec4 qgl_Color;\n" + "out vec2 qgl_FragTexture;\n" + "out vec4 qgl_FragColor;\n" + "vec4 qgl_Vertex = vec4(_qgl_Vertex, 1.);\n" + "vec4 qgl_ftransform() {return qgl_ModelViewProjectionMatrix * qgl_Vertex;}\n"; + +const char qgl_fragment_head[] = + "in vec2 qgl_FragTexture;\n" + "in vec4 qgl_FragColor;\n" + "out vec4 qgl_FragData[gl_MaxDrawBuffers];\n"; + +const char qgl_uniform[] = + "uniform mat4 qgl_ModelViewMatrix;\n" + "uniform mat4 qgl_ProjectionMatrix;\n" + "uniform mat4 qgl_ModelViewProjectionMatrix;\n" + "uniform mat3 qgl_NormalMatrix;\n" + "uniform mat4 qgl_ModelViewMatrixInverse;\n" + "uniform mat4 qgl_ProjectionMatrixInverse;\n" + "uniform mat4 qgl_ModelViewProjectionMatrixInverse;\n" + "uniform mat4 qgl_ModelViewMatrixTranspose;\n" + "uniform mat4 qgl_ProjectionMatrixTranspose;\n" + "uniform mat4 qgl_ModelViewProjectionMatrixTranspose;\n" + "uniform mat4 qgl_ModelViewMatrixInverseTranspose;\n" + "uniform mat4 qgl_ProjectionMatrixInverseTranspose;\n" + "uniform mat4 qgl_ModelViewProjectionMatrixInverseTranspose;\n"; + +const char qgl_structs[] = + "const int qgl_MaxLights = 8;\n" + "struct QGLLight {\n" + " vec4 color;\n" + " vec4 position;\n" + " vec4 direction;\n" + " float intensity;\n" + " float startAngle;\n" + " float startAngleCos;\n" + " float endAngle;\n" + " float endAngleCos;\n" + " float constantAttenuation;\n" + " float linearAttenuation;\n" + " float quadraticAttenuation;\n" + " sampler2DShadow shadow;\n" + //" sampler2D shadowColor\n" + " mat4 shadowMatrix;\n" + //" vec4 shadowDir0;\n" + //" vec4 shadowDir1;\n" + "};\n" + "struct QGLMap {\n" + " float offset;\n" + " float amount;\n" + " sampler2D map;\n" + "};\n" + "struct QGLMaterial {\n" + " float transparency;\n" + " float reflectivity;\n" + " float iof;\n" + " float dispersion;\n" + " vec4 color_diffuse;\n" + " vec4 color_specular;\n" + " vec4 color_self_illumination;\n" + " QGLMap map_diffuse;\n" + " QGLMap map_normal;\n" + " QGLMap map_relief;\n" + " QGLMap map_self_illumination;\n" + " QGLMap map_specularity;\n" + " QGLMap map_specular;\n" + "};\n" + "uniform QGLLight qgl_AmbientLight;\n" + "uniform QGLLight qgl_Light[qgl_MaxLights];\n" + "uniform QGLMaterial qgl_Material;\n"; + + +QString loadShaderFile(QOpenGLShaderProgram * prog, QOpenGLShader::ShaderType type, const QString & file) { + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) return ""; + QString all = QString::fromUtf8(f.readAll()); + int i = all.indexOf("#version"); + QString version = all.mid(i + 8, all.indexOf("\n", i) - i - 8).trimmed(); + if (version.toInt() >= 150) { + int ip = all.indexOf("\n", i); + if (ip < 0) return all; + if (type == QOpenGLShader::Vertex) { + all.insert(ip + 1, qgl_vertex_head); + } + if (type == QOpenGLShader::Fragment) { + all.insert(ip + 1, qgl_fragment_head); + } + all.insert(ip + 1, qgl_structs); + all.insert(ip + 1, qgl_uniform); + } + prog->addShaderFromSourceCode(type, all); +// qDebug() << "********" << all; + return all; +} + + +bool loadShaders(QOpenGLShaderProgram * prog, const QString & name, const QString & dir) { + prog->removeAllShaders(); + QDir d(dir); + QFileInfoList sl; + //qDebug() << "[QGLView] Shader \"" + name + "\" load shaders from" << d.absolutePath(); + sl = d.entryInfoList(QStringList(name + ".geom"), QDir::Files | QDir::NoDotAndDotDot); + foreach (const QFileInfo & i, sl) { + //qDebug() << "[QGLView] Shader \"" + name + "\" add geometry shader:" << i.fileName(); + loadShaderFile(prog, QOpenGLShader::Geometry, i.absoluteFilePath()); + } + sl = d.entryInfoList(QStringList(name + ".vert"), QDir::Files | QDir::NoDotAndDotDot); + foreach (const QFileInfo & i, sl) { + //qDebug() << "[QGLView] Shader \"" + name + "\" add vertex shader:" << i.fileName(); + loadShaderFile(prog, QOpenGLShader::Vertex, i.absoluteFilePath()); + } + sl = d.entryInfoList(QStringList(name + ".frag"), QDir::Files | QDir::NoDotAndDotDot); + foreach (const QFileInfo & i, sl) { + //qDebug() << "[QGLView] Shader \"" + name + "\" add fragment shader:" << i.fileName(); + loadShaderFile(prog, QOpenGLShader::Fragment, i.absoluteFilePath()); + } + if (!prog->link()) { + qDebug() << "[QGLView] Shader \"" + name + "\" link error: " + prog->log(); + return false; + } + return true; +} + + +void setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj, QMatrix4x4 prevview) { + if (!prog) return; + if (!prog->isLinked()) return; + QMatrix4x4 mvpm = proj * view; + QMatrix4x4 pmvpm = prevproj * prevview; + QMatrix3x3 nm = view.normalMatrix(); + //nm.in; + prog->setUniformValue("qgl_ModelViewMatrix", view); + prog->setUniformValue("qgl_ProjectionMatrix", proj); + prog->setUniformValue("prev_ModelViewProjectioMatrix", pmvpm); + prog->setUniformValue("prev_ModelViewMatrix", prevview); + prog->setUniformValue("qgl_ModelViewProjectionMatrix", mvpm); + prog->setUniformValue("qgl_NormalMatrix", nm); + //prog->setUniformValue("qgl_BumpMatrix", nm.); + prog->setUniformValue("qgl_ModelViewMatrixTranspose", view.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", mvpm.transposed()); +} + + +void setUniformMap(QOpenGLShaderProgram * prog, QString map_name, const Map & map, int channel, int def_channel) { + if (!prog) return; + if (!prog->isLinked()) return; + prog->setUniformValue(("qgl_Material." + map_name + ".offset").toLatin1().constData(), map.color_offset); + prog->setUniformValue(("qgl_Material." + map_name + ".amount").toLatin1().constData(), map.color_amount); + prog->setUniformValue(("qgl_Material." + map_name + ".map").toLatin1().constData(), map.bitmap_id > 0 ? channel : def_channel); +} + + +void setUniformMaterial(QOpenGLShaderProgram * prog, const Material & mat) { + if (!prog) return; + if (!prog->isLinked()) return; + QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions(); + GLfloat mat_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + mat_diffuse[0] = mat.color_diffuse.redF(); + mat_diffuse[1] = mat.color_diffuse.greenF(); + mat_diffuse[2] = mat.color_diffuse.blueF(); + mat_diffuse[3] = mat.color_diffuse.alphaF() * (1.f - mat.transparency); + glFuncs->glVertexAttrib4f(prog->attributeLocation("qgl_Color"), mat_diffuse[0], mat_diffuse[1], mat_diffuse[2], mat_diffuse[3]); + prog->setUniformValue("qgl_Material.transparency", mat.transparency); + prog->setUniformValue("qgl_Material.reflectivity", mat.reflectivity); + prog->setUniformValue("qgl_Material.iof", mat.iof); + prog->setUniformValue("qgl_Material.dispersion", mat.dispersion); + prog->setUniformValue("qgl_Material.color_diffuse", mat.color_diffuse); + prog->setUniformValue("qgl_Material.color_self_illumination", mat.color_self_illumination); + prog->setUniformValue("qgl_Material.color_specular", mat.color_specular); + setUniformMap(prog, "map_diffuse", mat.map_diffuse, 0, 6); + setUniformMap(prog, "map_normal", mat.map_normal, 1, 7); + setUniformMap(prog, "map_relief", mat.map_relief, 2, 6); + setUniformMap(prog, "map_self_illumination", mat.map_self_illumination, 3, 6); + setUniformMap(prog, "map_specularity", mat.map_specularity, 4, 6); + setUniformMap(prog, "map_specular", mat.map_specular, 5, 6); + +} + + +void setUniformLights(QOpenGLShaderProgram * prog, const QVector & lights, const QMatrix4x4 & mat, int shadow_start) { + for (int i = 0; i < lights.size(); ++i) + setUniformLight(prog, lights[i], QString("qgl_Light[%1]").arg(i), mat, shadow_start + i); +} +/* +" vec3 position;\n" +" vec3 direction;\n" +" vec4 color;\n" +" float intensity;\n" +" float startAngle;\n" +" float endAngle;\n" +" float constantAttenuation;\n" +" float linearAttenuation;\n" +" float quadraticAttenuation;\n" +" sampler2DShadow shadow;\n" +" mat4 shadowMatrix;\n" +*/ +void setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat, int shadow) { + if (!prog) return; + if (!prog->isLinked()) return; + QMatrix4x4 m = mat * light->worldTransform(); + QVector4D pos(0, 0, 0, 1.), dir(light->direction);//, dir0(light->dir0), dir1(light->dir1); + pos = m * pos; + dir = (mat * dir).normalized(); + float ang_start = light->angle_start / 2.f, ang_end = light->angle_end / 2.f; + if (light->light_type == Light::Omni) + ang_start = ang_end = 180.; + //qDebug() << "light" << light->name() << ulightn << pos; + prog->setUniformValue((ulightn + ".position").toLatin1().constData(), pos); + prog->setUniformValue((ulightn + ".direction").toLatin1().constData(), dir); + prog->setUniformValue((ulightn + ".intensity").toLatin1().constData(), GLfloat(light->intensity)); + prog->setUniformValue((ulightn + ".startAngle").toLatin1().constData(), GLfloat(ang_start)); + prog->setUniformValue((ulightn + ".startAngleCos").toLatin1().constData(), GLfloat(cosf(ang_start * deg2rad))); + prog->setUniformValue((ulightn + ".endAngle").toLatin1().constData(), GLfloat(ang_end)); + prog->setUniformValue((ulightn + ".endAngleCos").toLatin1().constData(), GLfloat(cosf(ang_end * deg2rad))); + prog->setUniformValue((ulightn + ".color").toLatin1().constData(), light->color()); + prog->setUniformValue((ulightn + ".constantAttenuation").toLatin1().constData(), GLfloat(light->decay_const)); + prog->setUniformValue((ulightn + ".linearAttenuation").toLatin1().constData(), GLfloat(light->decay_linear)); + prog->setUniformValue((ulightn + ".quadraticAttenuation").toLatin1().constData(), GLfloat(light->decay_quadratic)); + prog->setUniformValue((ulightn + ".shadow").toLatin1().constData(), shadow); + prog->setUniformValue((ulightn + ".shadowColor").toLatin1().constData(), shadow); + prog->setUniformValue((ulightn + ".shadowMatrix").toLatin1().constData(), light->shadow_matrix); + //qDebug() << light->shadow_matrix; + //prog->setUniformValue((ulightn + ".shadowDir0").toLatin1().constData(), (mat * dir0)); + //prog->setUniformValue((ulightn + ".shadowDir1").toLatin1().constData(), (mat * dir1)); + //qDebug() << light->direction << light->dir0 << light->dir1; +} diff --git a/test/qglview/glshaders.h b/test/qglview/glshaders.h new file mode 100644 index 0000000..62f8939 --- /dev/null +++ b/test/qglview/glshaders.h @@ -0,0 +1,35 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLSHADERS_H +#define GLSHADERS_H + +#include "gltypes.h" + +class Map; +class Material; +class Light; + +QString loadShaderFile(QOpenGLShaderProgram * prog, QOpenGLShader::ShaderType type, const QString & file); +bool loadShaders(QOpenGLShaderProgram * prog, const QString & name, const QString & dir = QString()); +void setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj = QMatrix4x4(), QMatrix4x4 prevview = QMatrix4x4()); +void setUniformMap(QOpenGLShaderProgram * prog, const Map & map, int channel, int def_channel); +void setUniformMaterial(QOpenGLShaderProgram * prog, const Material & mat); +void setUniformLights(QOpenGLShaderProgram * prog, const QVector & lights, const QMatrix4x4 & mat, int shadow_start); +void setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat = QMatrix4x4(), int shadow = 0); +#endif // GLSHADERS_H diff --git a/test/qglview/gltexture_manager.cpp b/test/qglview/gltexture_manager.cpp new file mode 100644 index 0000000..e87ef8e --- /dev/null +++ b/test/qglview/gltexture_manager.cpp @@ -0,0 +1,67 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltexture_manager.h" + + +bool GLTextureManager::loadTextures() { + //glGenTextures(); + QFileInfoList fil; + Animation anim; + for (int i = 0; i < anim_pathes.size(); ++i) { + anim.path = anim_pathes[i].second; + anim.bitmaps.clear(); + fil = QDir(anim_pathes[i].first).entryInfoList(QDir::Files, QDir::Name); + foreach (const QFileInfo & fi, fil) { + if (fi.baseName().indexOf(anim_pathes[i].second) < 0) continue; + anim.bitmaps << loadTexture(fi.filePath(), false); + } + qDebug() << "[TextureManager] Loaded" << anim.bitmaps.size() << "frames"; + anim_ids << QPair(anim_pathes[i].second, anim); + } + anim_pathes.clear(); + foreach (const QString & i, tex_pathes) + loadTexture(i, true); + tex_pathes.clear(); + return true; +} + + +void GLTextureManager::deleteTextures() { + for (int i = 0; i < 2; ++i) { + QList texs = tex_ids[i].values(); + qDebug() << "[TextureManager] Delete" << texs.size() << "textures"; + if (!texs.isEmpty()) glDeleteTextures(texs.size(), &texs[0]); + tex_ids[i].clear(); + } + qDebug() << "[TextureManager] Delete" << anim_ids.size() << "animations"; + for (int i = 0; i < anim_ids.size(); ++i) + glDeleteTextures(anim_ids[i].second.bitmaps.size(), anim_ids[i].second.bitmaps.data()); + anim_ids.clear(); +} + + +void GLTextureManager::deleteTexture(const QString & name) { + for (int i = 0; i < 2; ++i) { + if (tex_ids[i].contains(name)) { + GLuint id = tex_ids[i][name]; + glDeleteTextures(1, &id); + tex_ids[i].remove(name); + } + } +} diff --git a/test/qglview/gltexture_manager.h b/test/qglview/gltexture_manager.h new file mode 100644 index 0000000..27fb36e --- /dev/null +++ b/test/qglview/gltexture_manager.h @@ -0,0 +1,53 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLTEXTUREMANAGER_H +#define GLTEXTUREMANAGER_H + +#include "glmaterial.h" +#include +#include + +class GLTextureManager: public GLTextureManagerBase +{ +public: + GLTextureManager() {;} + ~GLTextureManager() {deleteTextures();} + + struct Animation { + QString path; + QVector bitmaps; + GLuint bitmapID(const int frame) {if (frame < 0 || frame >= bitmaps.size()) return 0; return bitmaps[frame];} + }; + + void addTexture(const QString & path) {tex_pathes << path;} + void addAnimation(const QString & dir, const QString & name) {anim_pathes << QPair(dir, name);} + bool loadTextures(); + void deleteTextures(); + void deleteTexture(const QString & name); + Animation * findAnimation(const QString & name) {for (int i = 0; i < anim_ids.size(); ++i) if (anim_ids[i].first == name) return &(anim_ids[i].second); return 0;} + + QVector > anim_ids; + +private: + QStringList tex_pathes; + QList > anim_pathes; + +}; + +#endif // GLTEXTUREMANAGER_H diff --git a/test/qglview/gltypes.cpp b/test/qglview/gltypes.cpp new file mode 100644 index 0000000..98b2dcc --- /dev/null +++ b/test/qglview/gltypes.cpp @@ -0,0 +1,379 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glcamera.h" +#include "qglview.h" + +#include + +//__GLWidget__ * currentQGLView; +//QMutex globMutex; + + +QString readCharsUntilNull(QDataStream & s) { + QString str; + char ch; + s.readRawData(&ch, 1); + while (ch != '\0') { + str += ch; + s.readRawData(&ch, 1); + } + return str; +} + + +QString findFile(const QString & file, const QStringList & pathes) { + QFileInfo fi(QString(file).replace("\\", "/")); + //qDebug() << "search" << file << "in" << pathes; + if (fi.exists()) return fi.absoluteFilePath(); + QString fn = fi.fileName(); + if (fn.contains("/")) fn = fn.mid(fn.lastIndexOf("/")); + foreach (QString p, pathes) { + QFileInfoList fil = QDir(p).entryInfoList(QStringList(fn), QDir::Files | QDir::NoDotAndDotDot); + //qDebug() << "findFile" << fn << "in" << p << "->" << fil.size(); + if (!fil.isEmpty()) + return fil[0].absoluteFilePath(); + } + return QString(); +} + + +void glDrawQuad(QOpenGLShaderProgram * prog, QVector4D * corner_dirs, GLfloat x, GLfloat y, GLfloat w, GLfloat h) { + glResetAllTransforms(); + glSetPolygonMode(GL_FILL); + int loc = prog ? prog->attributeLocation("qgl_Color") : 0, + locv = prog ? prog->attributeLocation("qgl_Vertex") : 0, + loct = prog ? prog->attributeLocation("qgl_Texture") : 0, + locc = prog ? prog->attributeLocation("view_corner") : 0; + glBegin(GL_QUADS); + QOpenGLFunctions *glFuncs = QOpenGLContext::currentContext()->functions(); + if (prog) { + glFuncs->glVertexAttrib3f(loc, 1.f, 1.f, 1.f); + } + glColor3f(1.f, 1.f, 1.f); + if (prog) { + if (corner_dirs) + prog->setAttributeValue(locc, corner_dirs[0]); + glFuncs->glVertexAttrib2f(loct, 0.f, 0.f); + glFuncs->glVertexAttrib2f(locv, x, y); + } + glTexCoord2f(0.f, 0.f); + glVertex2f(x, y); + if (prog) { + if (corner_dirs) + prog->setAttributeValue(locc, corner_dirs[1]); + glFuncs->glVertexAttrib2f(loct, 1.f, 0.f); + glFuncs->glVertexAttrib2f(locv, x + w, y); + } + glTexCoord2f(1.f, 0.f); + glVertex2f(x + w, y); + if (prog) { + if (corner_dirs) + prog->setAttributeValue(locc, corner_dirs[2]); + glFuncs->glVertexAttrib2f(loct, 1.f, 1.f); + glFuncs->glVertexAttrib2f(locv, x + w, y + h); + } + glTexCoord2f(1.f, 1.f); + glVertex2f(x + w, y + h); + if (prog) { + if (corner_dirs) + prog->setAttributeValue(locc, corner_dirs[3]); + glFuncs->glVertexAttrib2f(loct, 0.f, 1.f); + glFuncs->glVertexAttrib2f(locv, x, y + h); + } + glTexCoord2f(0.f, 1.f); + glVertex2f(x, y + h); + glEnd(); +} + + +QMatrix4x4 getGLMatrix(GLenum matrix) { + GLfloat gm[16]; + glGetFloatv(matrix, gm); + float qm[16]; + for (int i = 0; i < 16; ++i) + qm[i] = gm[i]; + return QMatrix4x4(qm).transposed(); +} + + +void setGLMatrix(QMatrix4x4 matrix) { + GLfloat gm[16]; + float qm[16]; + matrix.transposed().copyDataTo(qm); + for (int i = 0; i < 16; ++i) + gm[i] = qm[i]; + glLoadMatrixf(gm); +} + + +void qglMultMatrix(const QMatrix4x4 & m) { + GLfloat gm[16]; + float qm[16]; + m.transposed().copyDataTo(qm); + for (int i = 0; i < 16; ++i) + gm[i] = qm[i]; + glMultMatrixf(gm); +} + + +void createGLTexture(GLuint & tex, int width, int height, const GLenum & format, const GLenum & target) { + glClearError(); + if (tex == 0) { + glGenTextures(1, &tex); + glBindTexture(target, tex); + } + //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24 || format == GL_DEPTH_COMPONENT32) + glTexImage2D(target, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr); + else { + int t = GL_UNSIGNED_BYTE; + int f = GL_RGBA; + if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGBA32F || format == GL_RGBA16F) + t = GL_FLOAT; + if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGB8 || format == GL_RGB) + f = GL_RGB; + glTexImage2D(target, 0, format, width, height, 0, f, t, nullptr); + //glGenerateMipmap(target); + //qDebug() << "glTexImage2D" << width << height << QString::number(t, 16); + } + //qDebug() << QString::number(glGetError(), 16); +} + + +void createGLTexture(GLuint & tex, const QImage & image, const GLenum & format, const GLenum & target) { + if (tex == 0) { + glGenTextures(1, &tex); + } + glBindTexture(target, tex); + QImage im = image.mirrored(false, true);///__GLWidget__::convertToGLFormat(image); + //const QImage & cim(im); + //glClearError(); + //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) { + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + } + glTexImage2D(target, 0, format, im.width(), im.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, im.bits()); + //qDebug() << tex << im.width() << im.height() << im.bits() << glGetError(); +} + + +QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_) { + QMatrix4x4 ret; + float t = 1.f / (tanf(angle * deg2rad / 2.f)), e = 2.4e-7f; + ret(0, 0) = t / aspect; + ret(1, 1) = t; + ret(2, 2) = e - 1.f;//far_ / (far_ - near_) - 1.; + ret(2, 3) = (e - 2.f) * near_;//2. * far_ * near_ / (far_ - near_); + ret(3, 2) = -1.f; + ret(3, 3) = 0.f; + return ret; +} + + +QImage rotateQImageLeft(const QImage & im) { + QImage ri(im.height(), im.width(), im.format()); + QPainter p(&ri); + p.rotate(90); + p.drawImage(0, -im.height(), im); + p.end(); + return ri; +} + + +QImage rotateQImageRight(const QImage & im) { + QImage ri(im.height(), im.width(), im.format()); + QPainter p(&ri); + p.rotate(-90); + p.drawImage(-im.width(), 0, im); + p.end(); + return ri; +} + + + + +QColor colorFromString(const QString & str) { + QString s = str.trimmed(); + int i = s.indexOf("\t"); + float r, g, b; + r = s.left(i).toFloat(); s = s.right(s.length() - i - 1); i = s.indexOf("\t"); + g = s.left(i).toFloat(); s = s.right(s.length() - i - 1); + b = s.toFloat(); + return QColor(r * 255.f, g * 255.f, b * 255.f); +} + + +QVector3D orthToVector(const QVector3D & v, const float & scale) { + if (v.isNull()) return QVector3D(); + QVector3D rv, fn, sn; + if (v.x() != 0.f) rv.setZ(1.); + else if (v.y() != 0.f) rv.setX(1.); + else rv.setY(1.); + fn = QVector3D::crossProduct(v, rv).normalized(); + sn = QVector3D::crossProduct(v, fn).normalized(); + return fn * urand(scale) + sn * urand(scale); +} + + +QVector3D rotateVector(const QVector3D & v, const QVector3D & a) { + QMatrix4x4 m; + m.rotate(a.z(), 0., 0., 1.); + m.rotate(a.y(), 0., 1., 0.); + m.rotate(a.x(), 1., 0., 0.); + return m * v; +} + + +void setVectorLength(QVector3D & v, const float & l) { + float vl = v.length(); + if (vl == 0.f) return; + float c = l / vl; + v *= c; +} + + +void lengthenVector(QVector3D & v, const float & l) { + float vl = v.length(); + if (l == 0.f || vl == 0.f) return; + float c = 1.f + l / vl; + v *= c; +} + + +Vector3d::Vector3d(const QString & str) { + QString s = str.trimmed(); + int i = s.indexOf("\t"); + x = s.left(i).toFloat(); s = s.right(s.length() - i - 1); i = s.indexOf("\t"); + y = s.left(i).toFloat(); s = s.right(s.length() - i - 1); + z = s.toFloat(); +} + + +Vector3i::Vector3i(const QString & str) { + QString s = str.trimmed(); + int i = s.indexOf("\t"); + p0 = s.left(i).toInt(); s = s.right(s.length() - i - 1); i = s.indexOf("\t"); + p1 = s.left(i).toInt(); s = s.right(s.length() - i - 1); + p2 = s.toInt(); +} + + +void glEnableDepth() { + glEnable(GL_DEPTH_TEST); + //glDepthFunc(GL_GREATER); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); +} + + +void glDisableDepth() { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); +} + + +void glClearFramebuffer(const QColor & color, bool depth) { + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + //glClearDepth(0.); + if (depth) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear(GL_COLOR_BUFFER_BIT); +} + + + + +QGLViewBase::QGLViewBase() { + camera_ = new Camera(); + textures_manager = new GLTextureManager(); +} + + +QGLViewBase::~QGLViewBase() { + delete textures_manager; +} + + +Camera * QGLViewBase::camera() { + return camera_; +} + + +const Camera * QGLViewBase::camera() const { + return camera_; +} + + +void QGLViewBase::setCamera(Camera * camera) { + camera_ = camera; +} + + +GLTextureManagerBase * QGLViewBase::textureManager() { + return textures_manager; +} + + +Box3D::Box3D(const QVector & points) { + x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f; + if (points.isEmpty()) return; + float ix, iy, iz, ax, ay, az; + ix = ax = points[0].x(); + iy = ay = points[0].y(); + iz = az = points[0].z(); + for (int i = 1; i < points.size(); ++i) { + ix = qMin(ix, points[i].x()); ax = qMax(ax, points[i].x()); + iy = qMin(iy, points[i].y()); ay = qMax(ay, points[i].y()); + iz = qMin(iz, points[i].z()); az = qMax(az, points[i].z()); + } + x = ix; + y = iy; + z = iz; + length = ax - ix; + width = ay - iy; + height = az - iz; +} + + +QVector Box3D::corners() const { + QVector ret; + ret << QVector3D(x, y, z) << QVector3D(x, y + width, z) << QVector3D(x, y, z + height) << QVector3D(x, y + width, z + height) + << QVector3D(x + length, y, z) << QVector3D(x + length, y + width, z) + << QVector3D(x + length, y, z + height) << QVector3D(x + length, y + width, z + height); + return ret; +} + + +Box3D & Box3D::operator |=(const Box3D & o) { + if (isEmpty()) *this = o; + else { + GLfloat mx = x + length, my = y + width, mz = z + height; + GLfloat omx = o.x + o.length, omy = o.y + o.width, omz = o.z + o.height; + x = qMin(x, o.x); y = qMin(y, o.y); z = qMin(z, o.z); + mx = qMax(mx, omx); my = qMax(my, omy); mz = qMax(mz, omz); + length = mx - x; width = my - y; height = mz - z; + } + return *this; +} diff --git a/test/qglview/gltypes.h b/test/qglview/gltypes.h new file mode 100644 index 0000000..27658ec --- /dev/null +++ b/test/qglview/gltypes.h @@ -0,0 +1,348 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLTYPES_H +#define GLTYPES_H + +#if WIN32 || WIN64 || _WIN32 || _WIN64 || __WIN32__ || __WIN64__ +# define WINDOWS +#endif +#if __QNX__ || __QNXNTO__ +# define QNX +#endif +#ifdef __APPLE__ +# define MAC +#endif +#ifndef WINDOWS +# ifndef QNX +# ifndef MAC +# define LINUX +# endif +# endif +#endif +#if __GNUC__ +# define CC_GCC +#elif _MSC_VER +# define CC_VC +#endif + + +#include +#ifndef WINDOWS +# ifdef MAC +# include +# include +# include +# else +# include +# include +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QNX +# include +# include +#else +# include +# include +#endif +#include + + +#ifdef WINDOWS +# define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +# define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif +#ifndef M_2PI +# define M_2PI 6.28318530717958647692 +#endif +#ifndef M_PI_3 +# define M_PI_3 1.04719755119659774615 +#endif + +#ifndef GL_RGBA16F +# define GL_RGBA16F GL_RGBA16F_ARB +#endif + +using std::complex; + +#ifndef PIP_VERSION +typedef long long llong; +typedef unsigned char uchar; +typedef unsigned short int ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long ullong; +typedef long double ldouble; + +const float deg2rad = atanf(1.f) / 45.f; +const float rad2deg = 45.f / atanf(1.f); + +# ifdef WINDOWS +inline int random() {return rand();} +# endif +#else +#define random randomi +#endif + +#ifdef CC_VC +inline float round(const float & v) {return floor(v + 0.5);} +#endif +inline float randomu() {return float(random()) / RAND_MAX;} + +inline const QSizeF operator *(const QSizeF & f, const QSizeF & s) {return QSizeF(f.width() * s.width(), f.height() * s.height());} +#ifndef PIP_VERSION +template inline void piSwap(Type & f, Type & s) {Type t = f; f = s; s = t;} +template inline Type piMin(const Type & f, const Type & s) {return (f > s) ? s : f;} +template inline Type piMin(const Type & f, const Type & s, const Type & t) {return (f < s && f < t) ? f : ((s < t) ? s : t);} +template inline Type piMax(const Type & f, const Type & s) {return (f < s) ? s : f;} +template inline Type piMax(const Type & f, const Type & s, const Type & t) {return (f > s && f > t) ? f : ((s > t) ? s : t);} +template inline Type piClamp(const Type & v, const Type & min, const Type & max) {return (v > max ? max : (v < min ? min : v));} +inline ushort letobe_s(ushort v) {return (v << 8) | (v >> 8);} +#endif +// return [-1, 1] +inline float urand(const float & scale = 1.) {return ((float)rand() / RAND_MAX - .5f) * (scale + scale);} +// return [0, 1] +inline float uprand(const float & scale = 1.) {return ((float)rand() / RAND_MAX) * scale;} +QString readCharsUntilNull(QDataStream & s); +QString findFile(const QString & file, const QStringList & pathes); +inline QColor operator *(const QColor & c, float v) {return QColor(piClamp(c.red() * v, 0, 255), piClamp(c.green() * v, 0, 255), piClamp(c.blue() * v, 0, 255), piClamp(c.alpha() * v, 0, 255));} +inline QColor operator /(const QColor & c, float v) {return QColor(piClamp(c.red() / v, 0, 255), piClamp(c.green() / v, 0, 255), piClamp(c.blue() / v, 0, 255), piClamp(c.alpha() / v, 0, 255));} + +//extern __GLWidget__ * currentQGLView; + +inline void qglColor(const QColor & c) {glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());} +void qglMultMatrix(const QMatrix4x4 & m); +void glEnableDepth(); +void glDisableDepth(); +inline void glResetAllTransforms() {glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity();} +inline void glClearError() {int c = 100; while (glGetError() != GL_NO_ERROR && --c > 0) glGetError();} +inline void glClearAccumulation(const QColor & color = Qt::black) {glClearAccum(color.redF(), color.greenF(), color.blueF(), color.alphaF()); glClear(GL_ACCUM_BUFFER_BIT);} +void glClearFramebuffer(const QColor & color = Qt::black, bool depth = true); +inline void glSetCapEnabled(GLenum cap, bool on = true) {if (on) glEnable(cap); else glDisable(cap);} +inline void glSetLightEnabled(bool on) {if (on) glEnable(GL_LIGHTING); else glDisable(GL_LIGHTING);} +inline void glSetFogEnabled(bool on) {if (on) glEnable(GL_FOG); else glDisable(GL_FOG);} +inline void glSetPolygonMode(GLenum mode) {glPolygonMode(GL_FRONT_AND_BACK, mode);} +void glDrawQuad(QOpenGLShaderProgram * prog = nullptr, QVector4D * corner_dirs = nullptr, GLfloat x = -1.f, GLfloat y = -1.f, GLfloat w = 2.f, GLfloat h = 2.f); +QMatrix4x4 getGLMatrix(GLenum matrix); +void setGLMatrix(QMatrix4x4 matrix); +inline void deleteGLTexture(GLuint & tex) {if (tex != 0) glDeleteTextures(1, &tex); tex = 0;} +//# define QGLCI if (!QOpenGLContext::currentContext()) return; QOpenGLFunctions gf(QOpenGLContext::currentContext()); +//# define QGLC gf. +//inline void glActiveTextureChannel(int channel) {QGLCI gf.glActiveTexture(GL_TEXTURE0 + channel);} +//inline void glDisableTextures(int channels = 8) {QGLCI for (int i = channels - 1; i >= 0; --i) {glActiveTextureChannel(i); glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_CUBE_MAP);}} +//inline void glReleaseTextures(int channels = 8) {QGLCI for (int i = channels - 1; i >= 0; --i) {glActiveTextureChannel(i); glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, 0);}} +//inline void glReleaseFramebuffer() {QGLCI gf.glBindFramebuffer(GL_FRAMEBUFFER, 0);} +//inline void glReleaseShaders() {QGLCI gf.glUseProgram(0);} +//inline void deleteGLFramebuffer(GLuint & fbo) {QGLCI if (fbo != 0) gf.glDeleteFramebuffers(1, &fbo); fbo = 0;} +//inline void deleteGLRenderbuffer(GLuint & drbo) {QGLCI if (drbo != 0) gf.glDeleteRenderbuffers(1, &drbo); drbo = 0;} +//inline void deleteGLBuffer(GLuint & bo) {QGLCI if (bo != 0) gf.glDeleteBuffers(1, &bo); bo = 0;} +//inline void deleteGLVertexArray(GLuint & va) {QGLCI if (va != 0) gf.glDeleteVertexArrays(1, &va); va = 0;} +void createGLTexture(GLuint & tex, int width, int height, const GLenum & format = GL_RGBA8, const GLenum & target = GL_TEXTURE_2D); +void createGLTexture(GLuint & tex, const QImage & image, const GLenum & format = GL_RGBA8, const GLenum & target = GL_TEXTURE_2D); +inline void qglTranslate(const QVector3D & v) {glTranslatef(v.x(), v.y(), v.z());} +inline void qglScale(const QVector3D & v) {glScalef(v.x(), v.y(), v.z());} +QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_); +QImage rotateQImageLeft(const QImage & im); +QImage rotateQImageRight(const QImage & im); +inline QImage rotateQImage180(const QImage & im) {return im.mirrored(true, true);} +//const double deg2rad = atan(1.) / 45.; +//const double rad2deg = 45. / atan(1.); + +struct Box3D { + GLfloat x; + GLfloat y; + GLfloat z; + GLfloat width; + GLfloat length; + GLfloat height; + GLfloat angle_z; + GLfloat angle_xy; + GLfloat angle_roll; + Box3D() {x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f;} + Box3D(const QVector3D & center, GLfloat hwid, GLfloat hlen, GLfloat hhei) {x = center.x() - hwid; y = center.y() - hlen; z = center.z() - hhei; width = 2 * hwid; length = 2 * hlen; height = 2 * hhei; angle_z = angle_xy = angle_roll = 0.f;} + Box3D(const QVector & points); + bool isEmpty() const {return (qAbs(width) < 1E-6f) || (qAbs(length) < 1E-6f) || (qAbs(height) < 1E-6f);} + QVector3D randomPoint() const {return QVector3D(uprand(length) + x, uprand(width) + y, uprand(height) + z);} + QVector3D pos() const {return QVector3D(x, y, z);} + QVector3D size() const {return QVector3D(length, width, height);} + QVector3D center() const {return QVector3D(length / 2.f + x, width / 2.f + y, height / 2.f + z);} + QVector3D angles() const {return QVector3D(angle_xy, angle_roll, angle_z);} + QVector corners() const; + void setPos(const QVector3D & p) {x = p.x(); y = p.y(); z = p.z();} + void setAngles(const QVector3D & a) {angle_xy = a.x(); angle_roll = a.y(); angle_z = a.z();} + void setSize(const QVector3D & s) {length = s.x(); width = s.y(); height = s.z();} + Box3D & moveTo(const QVector3D & v) {x = v.x(); y = v.y(); z = v.z(); return *this;} + Box3D & move(const QVector3D & v) {x += v.x(); y += v.y(); z += v.z(); return *this;} + Box3D movedTo(const QVector3D & v) const {Box3D t(*this); t.x = v.x(); t.y = v.y(); t.z = v.z(); return t;} + Box3D moved(const QVector3D & v) const {Box3D t(*this); t.x += v.x(); t.y += v.y(); t.z += v.z(); return t;} + Box3D & operator |=(const Box3D & o); +}; + +inline QDebug operator <<(QDebug d, const Box3D & v) {d << "Box3D {start (" << v.x << "," << v.y << "," << v.z << "), size (" << v.length << "," << v.width << "," << v.height << ")}"; return d;} + +struct Vector3d; + +GLfloat dot(const Vector3d & v0, const Vector3d & v1); + +struct Vector3d { + GLfloat x; + GLfloat y; + GLfloat z; + Vector3d(GLfloat x_ = 0., GLfloat y_ = 0., GLfloat z_ = 0.) {x = x_; y = y_; z = z_;} + Vector3d(const QVector3D & v) {x = v.x(); y = v.y(); z = v.z();} + Vector3d(const QString & str); + inline void clear() {x = y = z = 0.;} + inline GLfloat length() const {return sqrtf(x*x + y*y + z*z);} + inline GLfloat lengthSquared() const {return x*x + y*y + z*z;} + Vector3d & normalize() { + GLfloat l = length(); + if (l == 0.f) return *this; + x /= l; y /= l; z /= l; + return *this; + } + Vector3d normalized() {return Vector3d(*this).normalize();} + Vector3d projectTo(Vector3d dir) {dir.normalize(); return dir * dot(dir, *this);} + Vector3d operator *(const GLfloat v) {return Vector3d(x*v, y*v, z*v);} + Vector3d operator /(const GLfloat v) {return Vector3d(x/v, y/v, z/v);} + Vector3d operator +(const GLfloat v) {return Vector3d(x+v, y+v, z+v);} + Vector3d operator -(const GLfloat v) {return Vector3d(x-v, y-v, z-v);} + Vector3d operator +(const Vector3d & v) {return Vector3d(x + v.x, y + v.y, z + v.z);} + Vector3d operator -(const Vector3d & v) {return Vector3d(x - v.x, y - v.y, z - v.z);} + Vector3d operator -() {return Vector3d(-x, -y, -z);} + Vector3d & operator *=(const GLfloat & v) {x *= v; y *= v; z *= v; return *this;} + Vector3d & operator /=(const GLfloat & v) {x /= v; y /= v; z /= v; return *this;} + Vector3d & operator +=(const GLfloat & v) {x += v; y += v; z += v; return *this;} + Vector3d & operator -=(const GLfloat & v) {x -= v; y -= v; z -= v; return *this;} + Vector3d & operator +=(const Vector3d & v) {x += v.x; y += v.y; z += v.z; return *this;} + Vector3d & operator -=(const Vector3d & v) {x -= v.x; y -= v.y; z -= v.z; return *this;} + bool operator ==(const Vector3d & v) {return x == v.x && y == v.y && z == v.z;} + QVector3D toQVector3D() const {return QVector3D(x, y, z);} +}; + +inline Vector3d operator *(const Vector3d & v0, const Vector3d & v1) { + return Vector3d(v0.y * v1.z - v1.y * v0.z, v1.x * v0.z - v0.x * v1.z, v0.x * v1.y - v1.x * v0.y); +} +inline GLfloat dot(const Vector3d & v0, const Vector3d & v1) {return v0.x*v1.x + v0.y*v1.y + v0.z*v1.z;} + +struct Vector2d { + GLfloat x; + GLfloat y; + Vector2d(GLfloat x_ = 0., GLfloat y_ = 0.) {x = x_; y = y_;} + Vector2d(const Vector3d & v3) {x = v3.x; y = v3.y;} + Vector2d operator *(const GLfloat v) {return Vector2d(x*v, y*v);} + Vector2d operator /(const GLfloat v) {return Vector2d(x/v, y/v);} + Vector2d operator +(const GLfloat v) {return Vector2d(x+v, y+v);} + Vector2d operator -(const GLfloat v) {return Vector2d(x-v, y-v);} + Vector2d operator +(const Vector3d & v) {return Vector3d(x + v.x, y + v.y);} + Vector2d operator -(const Vector3d & v) {return Vector3d(x - v.x, y - v.y);} + Vector2d operator +(const Vector2d & v) {return Vector2d(x + v.x, y + v.y);} + Vector2d operator -(const Vector2d & v) {return Vector2d(x - v.x, y - v.y);} + Vector2d & operator *=(const GLfloat & v) {x *= v; y *= v; return *this;} + Vector2d & operator /=(const GLfloat & v) {x /= v; y /= v; return *this;} + Vector2d & operator +=(const GLfloat & v) {x += v; y += v; return *this;} + Vector2d & operator -=(const GLfloat & v) {x -= v; y -= v; return *this;} + Vector2d & operator +=(const Vector3d & v) {x += v.x; y += v.y;; return *this;} + Vector2d & operator -=(const Vector3d & v) {x -= v.x; y -= v.y;; return *this;} +}; + +struct Vector3i { + Vector3i(int p0_ = 0, int p1_ = 0, int p2_ = 0) {p0 = p0_; p1 = p1_; p2 = p2_;} + Vector3i(const QString & str); + Vector3i movedX(const int & o) {return Vector3i(p0 + o, p1, p2);} + Vector3i movedY(const int & o) {return Vector3i(p0, p1 + o, p2);} + Vector3i movedZ(const int & o) {return Vector3i(p0, p1, p2 + o);} + Vector3i moved(const int & x, const int & y, const int & z) {return Vector3i(p0 + x, p1 + y, p2 + z);} + int p0; + int p1; + int p2; + bool operator ==(const Vector3i & o) const {return p0 == o.p0 && p1 == o.p1 && p2 == o.p2;} + bool operator !=(const Vector3i & o) const {return p0 != o.p0 || p1 != o.p1 || p2 != o.p2;} + QVector3D toQVector3D() const {return QVector3D(p0, p1, p2);} +}; + +inline Vector3i operator +(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 + s.p0, f.p1 + s.p1, f.p2 + s.p2);} +inline Vector3i operator -(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 - s.p0, f.p1 - s.p1, f.p2 - s.p2);} +inline Vector3i operator /(const Vector3i & f, const int & s) {return Vector3i(f.p0 / s, f.p1 / s, f.p2 / s);} +inline uint qHash(const Vector3i & v) {return v.p0 + v.p1 * 1024 + v.p2 * 1024 * 1024;} +inline QDebug operator <<(QDebug d, const Vector3d& v) {d.nospace() << "{" << v.x << ", " << v.y << ", " << v.z << "}"; return d.space();} +inline QDebug operator <<(QDebug d, const Vector3i & v) {d.nospace() << "{" << v.p0 << ", " << v.p1 << ", " << v.p2 << "}"; return d.space();} + +inline QDataStream & operator <<(QDataStream & s, const Vector3d & v) {s << v.x << v.y << v.z; return s;} +inline QDataStream & operator >>(QDataStream & s, Vector3d & v) {s >> v.x >> v.y >> v.z; return s;} +inline QDataStream & operator <<(QDataStream & s, const Vector3i & v) {s << v.p0 << v.p1 << v.p2; return s;} +inline QDataStream & operator >>(QDataStream & s, Vector3i & v) {s >> v.p0 >> v.p1 >> v.p2; return s;} + +QColor colorFromString(const QString & str); +inline float cosABV(const QVector3D & v0, const QVector3D & v1) { + float l = v0.length() * v1.length(); + if (l == 0.f) return 0.; + return (QVector3D::dotProduct(v0, v1)) / l; +} +inline QVector3D projection(const QVector3D & v, const QVector3D & to) {return to.normalized() * v.length() * cosABV(v, to);} +QVector3D orthToVector(const QVector3D & v, const float & scale = 1.); +QVector3D rotateVector(const QVector3D & v, const QVector3D & a); +void setVectorLength(QVector3D & v, const float & l); +void lengthenVector(QVector3D & v, const float & l); +inline float squareLength(const QVector3D & from, const QVector3D & to) {return (to.x() - from.x())*(to.x() - from.x()) + (to.y() - from.y())*(to.y() - from.y()) + (to.z() - from.z())*(to.z() - from.z());} +inline QVector3D directionFromAngles(const QVector3D & a) {return rotateVector(QVector3D(1., 0., 0.), a);} +inline float frac(const float & x, const float & b) {return x - int(x / b) * b;} + +class GLObjectBase; +class QGLView; +class Light; +class Camera; +class GLTextureManagerBase; + +class QGLViewBase +{ + friend class GLObjectBase; +public: + QGLViewBase(); + virtual ~QGLViewBase(); + Camera * camera(); + const Camera * camera() const; + void setCamera(Camera * camera); + GLTextureManagerBase * textureManager(); +protected: + virtual void collectLights() = 0; + Camera * camera_; + GLTextureManagerBase * textures_manager; +}; + +#endif // GLTYPES_H diff --git a/test/qglview/glvbo.cpp b/test/qglview/glvbo.cpp new file mode 100644 index 0000000..83e031b --- /dev/null +++ b/test/qglview/glvbo.cpp @@ -0,0 +1,317 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glvbo.h" + + +GLVBO::GLVBO(GLenum usage_) { + buffer_ = 0; + va_ = 0; + usage = usage_; + changed = true; +} + + +GLVBO::~GLVBO() { + //destroy(); +} + + +void GLVBO::init() { + initializeOpenGLFunctions(); + if (!isInit()) { + //glGenVertexArrays(1, &va_); + glGenBuffers(1, &buffer_); + } + changed = true; +} + + +void GLVBO::destroy() { + if (buffer_ != 0) glDeleteFramebuffers(1, &buffer_); + buffer_ = 0; +// if (va_ != 0) glDeleteVertexArrays(1, &va_); +// va_ = 0; +} + + +void GLVBO::calculateBinormals() { + tangents_.clear(); + bitangents_.clear(); + if (vertices_.isEmpty() || texcoords_.isEmpty()) return; + int vcnt = vertices_.size() / 3; + if (texcoords_.size() != vcnt * 2) return; + int tcnt = vcnt / 3; + //qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "..."; + for (int t = 0; t < tcnt; ++t) { + int vi = t*9, ti = t*6; + Vector3d v0(vertices_[vi + 0], vertices_[vi + 1], vertices_[vi + 2]); + Vector3d v1(vertices_[vi + 3], vertices_[vi + 4], vertices_[vi + 5]); + Vector3d v2(vertices_[vi + 6], vertices_[vi + 7], vertices_[vi + 8]); + Vector2d t0(texcoords_[ti + 0], texcoords_[ti + 1]); + Vector2d t1(texcoords_[ti + 2], texcoords_[ti + 3]); + Vector2d t2(texcoords_[ti + 4], texcoords_[ti + 5]); + Vector3d dv1 = v1 - v0, dv2 = v2 - v0; + Vector2d dt1 = t1 - t0, dt2 = t2 - t0; + Vector3d tan; + Vector3d bitan; + tan = (dv1 * dt2.y - dv2 * dt1.y).normalize(); + bitan = (dv2 * dt1.x - dv1 * dt2.x).normalize(); + for (int i = 0; i < 3; ++i) { + tangents_ << tan.x << tan.y << tan.z; + bitangents_ << bitan.x << bitan.y << bitan.z; + } + //qDebug() << " t" << t << vi << ti << dv1.toQVector3D() << "..."; + } + //qDebug() << "calculateBinormals" << vcnt << tcnt << tangents_.size(); +} + + +bool GLVBO::rebuffer(bool clear_) { + initializeOpenGLFunctions(); + QVector data; + //data.clear(); + calculateBinormals(); + data << vertices_; + if (!normals_.isEmpty()) { + data << normals_; + has_normals = true; + } else has_normals = false; + if (!texcoords_.isEmpty()) { + data << texcoords_; + has_texcoords = true; + } else has_texcoords = false; + if (!colors_.isEmpty()) { + data << colors_; + has_colors = true; + } else has_colors = false; + if (!tangents_.isEmpty()) { + data << tangents_ << bitangents_; + has_binormals = true; + } else has_binormals = false; + //glBindVertexArray(va_); + //qDebug() << "load buffer" << data.size() << buffer_; + glBindBuffer(GL_ARRAY_BUFFER, buffer_); + glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.constData(), usage); + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindVertexArray(0); + vert_count = vertices_.size() / 3; + changed = false; + //qDebug() << "rebuff" << buffer_ << vert_count; + if (clear_) clear(); + return !isEmpty(); +} + + +void GLVBO::draw(GLenum type, QOpenGLShaderProgram * prog, bool simplest) { + if (buffer_ == 0 || vert_count == 0) return; + if (changed) rebuffer(); + //qDebug() << "draw" << vert_count; + void * offset = (void*)(vert_count * 3 * sizeof(GLfloat)); + //glBindVertexArray(va_); + void * offsets[5] = {0, 0, 0, 0, 0}; + if (!simplest) { + if (has_normals) { + offsets[0] = offset; + offset = (void*)(llong(offset) + vert_count * 3 * sizeof(GLfloat)); + } + if (has_texcoords) { + offsets[1] = offset; + offset = (void*)(llong(offset) + vert_count * 2 * sizeof(GLfloat)); + } + if (has_colors) { + offsets[2] = offset; + offset = (void*)(llong(offset) + vert_count * 4 * sizeof(GLfloat)); + } + if (has_binormals) { + offsets[3] = offset; + offset = (void*)(llong(offset) + vert_count * 3 * sizeof(GLfloat)); + offsets[4] = offset; + } + } + + glBindBuffer(GL_ARRAY_BUFFER, buffer_); + if (prog) { + locs.clear(); + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + int loc = prog->attributeLocation("_qgl_Vertex"); + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, 0); + locs << loc; + if (!simplest) { + loc = prog->attributeLocation("qgl_Normal"); + if (has_normals) { + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, offsets[0]); + locs << loc; + } else + glDisableVertexAttribArray(loc); + loc = prog->attributeLocation("qgl_Texture"); + if (has_texcoords) { + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, 2, GL_FLOAT, 0, 0, offsets[1]); + locs << loc; + } else + glDisableVertexAttribArray(loc); + loc = prog->attributeLocation("qgl_Color"); + if (has_colors) { + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, 4, GL_FLOAT, 0, 0, offsets[2]); + locs << loc; + } else + glDisableVertexAttribArray(loc); + loc = prog->attributeLocation("qgl_Tangent"); + int loc2 = prog->attributeLocation("qgl_Bitangent"); + if (has_binormals) { + glEnableVertexAttribArray(loc); + glEnableVertexAttribArray(loc2); + glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, offsets[3]); + glVertexAttribPointer(loc2, 3, GL_FLOAT, 0, 0, offsets[4]); + locs << loc << loc2; + } else { + glDisableVertexAttribArray(loc); + glDisableVertexAttribArray(loc2); + } + } + } else { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, 0); + if (!simplest) { + if (has_normals) { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, 0, offsets[0]); + } else glDisableClientState(GL_NORMAL_ARRAY); + if (has_texcoords) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, offsets[1]); + } else glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if (has_colors) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(4, GL_FLOAT, 0, offsets[2]); + } else glDisableClientState(GL_COLOR_ARRAY); + } + } + //qDebug() << "draw" << vert_count << buffer_ << offsets[0] << offsets[1] << offsets[3]; + glDrawArrays(type, 0, vert_count); + //qDebug() << "draw" << vert_count << buffer_ << "done"; + glBindBuffer(GL_ARRAY_BUFFER, 0); + foreach (int l, locs) + glDisableVertexAttribArray(l); +} + + +void GLVBO::clear() { + vertices_.clear(); + normals_.clear(); + texcoords_.clear(); + colors_.clear(); +} + + +void GLVBO::translatePoints(const QVector3D & dp) { + if (vertices_.isEmpty()) return; + int vcnt = vertices_.size() / 3; + for (int i = 0; i < vcnt; ++i) { + int vi = i * 3; + vertices_[vi + 0] += dp.x(); + vertices_[vi + 1] += dp.y(); + vertices_[vi + 2] += dp.z(); + } + changed = true; +} + + +void GLVBO::scalePoints(const QVector3D & dp) { + if (vertices_.isEmpty()) return; + int vcnt = vertices_.size() / 3; + for (int i = 0; i < vcnt; ++i) { + int vi = i * 3; + vertices_[vi + 0] *= dp.x(); + vertices_[vi + 1] *= dp.y(); + vertices_[vi + 2] *= dp.z(); + } + changed = true; +} + + +bool GLVBO::saveToFile(const QString & filename) { + if (filename.isEmpty()) return false; + QFile f(filename); + QByteArray ba; + if (f.open(QFile::WriteOnly)) { + QDataStream out(&ba, QFile::WriteOnly); + out << vertices_ << normals_ << texcoords_ << colors_; + ba = qCompress(ba); + f.resize(0); + f.write(ba); + f.close(); + return true; + } + return false; +} + + +bool GLVBO::loadFromFile(const QString & filename) { + if (filename.isEmpty()) return false; + QFile f(filename); + QByteArray ba; + if (f.open(QFile::ReadOnly)) { + ba = f.readAll(); + if (ba.isEmpty()) return false; + ba = qUncompress(ba); + QDataStream in(ba); + in >> vertices_ >> normals_ >> texcoords_ >> colors_; + changed = true; + f.close(); + return !isEmpty(); + } + return false; +} + + +Box3D GLVBO::boundingBox() const { + if (vertices_.size() < 3) return Box3D(); + int vcnt = vertices_.size() / 3; + //qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "..."; + GLfloat mix, miy, miz, max, may, maz; + Vector3d v0(vertices_[0], vertices_[1], vertices_[2]); + mix = max = v0.x; + miy = may = v0.y; + miz = maz = v0.z; + Box3D bound; + for (int t = 1; t < vcnt; ++t) { + int vi = t*3; + Vector3d v(vertices_[vi + 0], vertices_[vi + 1], vertices_[vi + 2]); + if (mix > v.x) mix = v.x; + if (max < v.x) max = v.x; + if (miy > v.y) miy = v.y; + if (may < v.y) may = v.y; + if (miz > v.z) miz = v.z; + if (maz < v.z) maz = v.z; + } + bound.x = mix; + bound.y = miy; + bound.z = miz; + bound.length = max - mix; + bound.width = may - miy; + bound.height = maz - miz; + return bound; +} diff --git a/test/qglview/glvbo.h b/test/qglview/glvbo.h new file mode 100644 index 0000000..2f5ed06 --- /dev/null +++ b/test/qglview/glvbo.h @@ -0,0 +1,98 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLVBO_H +#define GLVBO_H + +#include "gltypes.h" +#include "chunkstream.h" + +class GLVBO : protected QOpenGLFunctions +{ + friend class GLObjectBase; + friend QDataStream & operator <<(QDataStream & s, const GLVBO & m); + friend QDataStream & operator >>(QDataStream & s, GLVBO & m); +public: + GLVBO(GLenum usage = GL_DYNAMIC_DRAW); + ~GLVBO(); + + //GLVBO & operator =(const GLVBO & o) {return *this;} + + void init(); + void destroy(); + bool rebuffer(bool clear_ = false); + void draw(GLenum type, QOpenGLShaderProgram * prog, bool simplest = false); + void clear(); + + GLuint buffer() const {return buffer_;} + int verticesCount() const {return vertices_.size() / 3;} + bool isInit() const {return buffer_ != 0;} + bool isEmpty() const {return vertices_.size() < 3;} + + QVector & vertices() {changed = true; return vertices_;} + QVector & normals() {changed = true; return normals_;} + QVector & texcoords() {changed = true; return texcoords_;} + QVector & colors() {changed = true; return colors_;} + + void translatePoints(const QVector3D & dp); + void scalePoints(const QVector3D & dp); + + bool saveToFile(const QString & filename); + bool loadFromFile(const QString & filename); + + Box3D boundingBox() const; + +private: + void calculateBinormals(); + + QVector vertices_, normals_, texcoords_, colors_, tangents_, bitangents_; + QVector locs; + GLenum usage; + GLuint buffer_, va_; + int vert_count; + bool changed, has_normals, has_texcoords, has_colors, has_binormals; + +}; + + +inline QDataStream & operator <<(QDataStream & s, const GLVBO & m) { + ChunkStream cs; + //qDebug() << "place VBO" << m.vertices_.size() << m.normals_.size() << m.texcoords_.size() << m.colors_.size() << "..."; + cs << cs.chunk(1, m.vertices_) << cs.chunk(2, m.normals_) << cs.chunk(3, m.texcoords_) << cs.chunk(4, m.colors_) << cs.chunk(5, m.usage); + //qDebug() << "place VBO done" << cs.data().size() << "..."; + s << qCompress(cs.data()); return s; +} +inline QDataStream & operator >>(QDataStream & s, GLVBO & m) { + QByteArray ba; + s >> ba; + ba = qUncompress(ba); + ChunkStream cs(ba); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: m.vertices_ = cs.getData >(); break; + case 2: m.normals_ = cs.getData >(); break; + case 3: m.texcoords_ = cs.getData >(); break; + case 4: m.colors_ = cs.getData >(); break; + case 5: m.usage = cs.getData(); break; + } + } + m.changed = true; + return s; +} + +#endif // GLVBO_H diff --git a/test/qglview/glwidget.cpp b/test/qglview/glwidget.cpp new file mode 100644 index 0000000..94972ca --- /dev/null +++ b/test/qglview/glwidget.cpp @@ -0,0 +1,268 @@ +#include "glwidget.h" +#include "renderer_simple.h" +#include "renderer_deferred_shading.h" +#include + + +GLWidget::GLWidget(QWidget *parent) : QWidget(parent) { + view_ = new QGLView(); + view_->setFlag(Qt::FramelessWindowHint); + container = QWidget::createWindowContainer(view_, this); + lay = new QVBoxLayout(this); + lay->addWidget(container); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + setMouseTracking(true); + setWindowIcon(QIcon("://icons/qglview.png")); + connect(view_, &QGLView::doubleClick, this, &GLWidget::viewDoubleClicked); +} + + +QColor GLWidget::backColor() const { + return view_->backColor(); +} + + +qreal GLWidget::lineWidth() const { + return view_->lineWidth(); +} + + +qreal GLWidget::FOV() const { + return view_->FOV(); +} + + +qreal GLWidget::depthStart() const { + return view_->depthStart(); +} + + +qreal GLWidget::depthEnd() const { + return view_->depthEnd(); +} + + +QColor GLWidget::ambientColor() const { + return view_->ambientColor(); +} + + +bool GLWidget::isLightEnabled() const { + return view_->isLightEnabled(); +} + + +bool GLWidget::isGrabMouseEnabled() const { + return view_->isGrabMouseEnabled(); +} + + +bool GLWidget::isMouseRotateEnabled() const { + return view_->isMouseRotateEnabled(); +} + + +bool GLWidget::isMouseSelectionEnabled() const { + return view_->isMouseSelectionEnabled(); +} + + +bool GLWidget::isCameraOrbit() const +{ + return view_->isCameraOrbit(); +} + + +bool GLWidget::isHoverHaloEnabled() const { + return view_->isHoverHaloEnabled(); +} + + +QColor GLWidget::hoverHaloColor() const { + return view_->hoverHaloColor(); +} + + +qreal GLWidget::hoverHaloFillAlpha() const { + return view_->hoverHaloFillAlpha(); +} + + +bool GLWidget::isSelectionHaloEnabled() const { + return view_->isSelectionHaloEnabled(); +} + + +QColor GLWidget::selectionHaloColor() const { + return view_->selectionHaloColor(); +} + + +qreal GLWidget::selectionHaloFillAlpha() const { + return view_->selectionHaloFillAlpha(); +} + + +void GLWidget::addObject(GLObjectBase * o) { + view_->addObject(o); +} + + +QByteArray GLWidget::saveCamera() { + ChunkStream cs; + const Camera * c = view()->camera(); + cs.add(1, c->posX()); + cs.add(2, c->posY()); + cs.add(3, c->posZ()); + cs.add(4, c->aim().x()); + cs.add(5, c->aim().y()); + cs.add(6, c->aim().z()); + cs.add(7, c->angleZ()); + cs.add(8, c->angleXY()); + cs.add(9, c->angleRoll()); + cs.add(10, c->FOV()); + return cs.data(); +} + + +void GLWidget::restoreCamera(const QByteArray &ba) { + if (ba.isEmpty()) return; + ChunkStream cs(ba); + QVector3D pos, aim, ang; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: pos.setX(cs.getData()); break; + case 2: pos.setY(cs.getData()); break; + case 3: pos.setZ(cs.getData()); break; + case 4: aim.setX(cs.getData()); break; + case 5: aim.setY(cs.getData()); break; + case 6: aim.setZ(cs.getData()); break; + case 7: ang.setZ(cs.getData()); break; + case 8: ang.setY(cs.getData()); break; + case 9: ang.setX(cs.getData()); break; + case 10: setFOV(cs.getData()); break; + default: break; + } + } + view()->camera()->setPos(pos); + view()->camera()->setAim(aim); + view()->camera()->setAngles(ang); +} + + +void GLWidget::stop() { + view_->stop(); +} + + +void GLWidget::start(float freq, GLRendererBase * r) { + if (r == nullptr) r = new RendererSimple(view_); + GLRendererBase * pr = nullptr; + view_->setRenderer(r, &pr); + if (pr != nullptr && pr != r) delete pr; + view_->start(freq); +} + + +void GLWidget::setBackColor(const QColor & c) { + view_->setBackColor(c); +} + + +void GLWidget::setLineWidth(const qreal & arg) { + view_->setLineWidth(arg); +} + + +void GLWidget::setFOV(const qreal & arg) { + view_->setFOV(arg); +} + + +void GLWidget::setDepthStart(const qreal & arg) { + view_->setDepthStart(arg); +} + + +void GLWidget::setDepthEnd(const qreal & arg) { + view_->setDepthEnd(arg); +} + + +void GLWidget::setAmbientColor(const QColor & arg) { + view_->setAmbientColor(arg); +} + + +void GLWidget::setLightEnabled(const bool & arg) { + view_->setLightEnabled(arg); +} + + +void GLWidget::setGrabMouseEnabled(const bool & arg) { + view_->setGrabMouseEnabled(arg); +} + + +void GLWidget::setMouseRotateEnabled(const bool & arg) { + view_->setMouseRotateEnabled(arg); +} + + +void GLWidget::setMouseSelectionEnabled(const bool & arg) { + view_->setMouseSelectionEnabled(arg); +} + + +void GLWidget::setCameraOrbit(const bool & arg) { + view_->setCameraOrbit(arg); +} + + +void GLWidget::setHoverHaloEnabled(const bool & arg) { + view_->setHoverHaloEnabled(arg); +} + + +void GLWidget::setHoverHaloColor(const QColor & arg) { + view_->setHoverHaloColor(arg); +} + + +void GLWidget::setHoverHaloFillAlpha(const qreal & arg) { + view_->setHoverHaloFillAlpha(arg); +} + + +void GLWidget::setSelectionHaloEnabled(const bool & arg) { + view_->setSelectionHaloEnabled(arg); +} + + +void GLWidget::setSelectionHaloColor(const QColor & arg) { + view_->setSelectionHaloColor(arg); +} + + +void GLWidget::setSelectionHaloFillAlpha(const qreal & arg) { + view_->setSelectionHaloFillAlpha(arg); +} + + +void GLWidget::viewDoubleClicked() { +// qDebug() << "click widget!!"; + if (view_->windowState() == Qt::WindowFullScreen) { +// view_->hide(); + container = QWidget::createWindowContainer(view_, this); + lay->addWidget(container); + container->show(); +// show(); + } else { +// hide(); + view_->setParent(nullptr); + view_->showFullScreen(); + lay->removeWidget(container); + } +// qDebug() << "click widge done!"; +} diff --git a/test/qglview/glwidget.h b/test/qglview/glwidget.h new file mode 100644 index 0000000..87eb6f1 --- /dev/null +++ b/test/qglview/glwidget.h @@ -0,0 +1,88 @@ +#ifndef GLWIDGET_H +#define GLWIDGET_H + +#include + + +class QGLView; +class GLRendererBase; +class GLObjectBase; + +class GLWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY (qreal lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY (qreal FOV READ FOV WRITE setFOV) + Q_PROPERTY (qreal depthStart READ depthStart WRITE setDepthStart) + Q_PROPERTY (qreal depthEnd READ depthEnd WRITE setDepthEnd) + Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor) + Q_PROPERTY (bool grabMouse READ isGrabMouseEnabled WRITE setGrabMouseEnabled) + Q_PROPERTY (bool mouseRotate READ isMouseRotateEnabled WRITE setMouseRotateEnabled) + Q_PROPERTY (bool mouseSelection READ isMouseSelectionEnabled WRITE setMouseSelectionEnabled) + Q_PROPERTY (bool cameraOrbit READ isCameraOrbit WRITE setCameraOrbit) + Q_PROPERTY (bool hoverHalo READ isHoverHaloEnabled WRITE setHoverHaloEnabled) + Q_PROPERTY (QColor hoverHaloColor READ hoverHaloColor WRITE setHoverHaloColor) + Q_PROPERTY (qreal hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha) + Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled) + Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor) + Q_PROPERTY (qreal selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha) +public: + explicit GLWidget(QWidget *parent = nullptr); + QGLView * view() {return view_;} + + QColor backColor() const; + qreal lineWidth() const; + qreal FOV() const; + qreal depthStart() const; + qreal depthEnd() const; + QColor ambientColor() const; + bool isLightEnabled() const; + bool isGrabMouseEnabled() const; + bool isMouseRotateEnabled() const; + bool isMouseSelectionEnabled() const; + bool isCameraOrbit() const; + bool isHoverHaloEnabled() const; + QColor hoverHaloColor() const; + qreal hoverHaloFillAlpha() const; + bool isSelectionHaloEnabled() const; + QColor selectionHaloColor() const; + qreal selectionHaloFillAlpha() const; + + void addObject(GLObjectBase * o); + QByteArray saveCamera(); + void restoreCamera(const QByteArray & ba); + +public slots: + void stop(); + void start(float freq = 60.0, GLRendererBase * r = nullptr); + void setBackColor(const QColor & c); + void setLineWidth(const qreal & arg); + void setFOV(const qreal & arg); + void setDepthStart(const qreal & arg); + void setDepthEnd(const qreal & arg); + void setAmbientColor(const QColor & arg); + void setLightEnabled(const bool & arg); + void setGrabMouseEnabled(const bool & arg); + void setMouseRotateEnabled(const bool & arg); + void setMouseSelectionEnabled(const bool & arg); + void setCameraOrbit(const bool & arg); + void setHoverHaloEnabled(const bool & arg); + void setHoverHaloColor(const QColor & arg); + void setHoverHaloFillAlpha(const qreal & arg); + void setSelectionHaloEnabled(const bool & arg); + void setSelectionHaloColor(const QColor & arg); + void setSelectionHaloFillAlpha(const qreal & arg); + +private slots: + void viewDoubleClicked(); + +private: + QWidget * container; + QGLView * view_; + QLayout * lay; + +signals: +}; + +#endif // GLWIDGET_H diff --git a/test/qglview/icons/alpha.png b/test/qglview/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/test/qglview/icons/alpha.png differ diff --git a/test/qglview/icons/application-exit.png b/test/qglview/icons/application-exit.png new file mode 100644 index 0000000..ed5f8b2 Binary files /dev/null and b/test/qglview/icons/application-exit.png differ diff --git a/test/qglview/icons/configure.png b/test/qglview/icons/configure.png new file mode 100644 index 0000000..5ce478b Binary files /dev/null and b/test/qglview/icons/configure.png differ diff --git a/test/qglview/icons/dialog-close.png b/test/qglview/icons/dialog-close.png new file mode 100644 index 0000000..2c2f99e Binary files /dev/null and b/test/qglview/icons/dialog-close.png differ diff --git a/test/qglview/icons/document-import.png b/test/qglview/icons/document-import.png new file mode 100644 index 0000000..293b5a0 Binary files /dev/null and b/test/qglview/icons/document-import.png differ diff --git a/test/qglview/icons/document-new.png b/test/qglview/icons/document-new.png new file mode 100644 index 0000000..61db97a Binary files /dev/null and b/test/qglview/icons/document-new.png differ diff --git a/test/qglview/icons/document-open.png b/test/qglview/icons/document-open.png new file mode 100644 index 0000000..3432ed2 Binary files /dev/null and b/test/qglview/icons/document-open.png differ diff --git a/test/qglview/icons/document-save-.png b/test/qglview/icons/document-save-.png new file mode 100644 index 0000000..cc380a0 Binary files /dev/null and b/test/qglview/icons/document-save-.png differ diff --git a/test/qglview/icons/document-save-all.png b/test/qglview/icons/document-save-all.png new file mode 100644 index 0000000..3e40997 Binary files /dev/null and b/test/qglview/icons/document-save-all.png differ diff --git a/test/qglview/icons/document-save.png b/test/qglview/icons/document-save.png new file mode 100644 index 0000000..8072aea Binary files /dev/null and b/test/qglview/icons/document-save.png differ diff --git a/test/qglview/icons/edit-clear-locationbar-rtl.png b/test/qglview/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..6c4b83b Binary files /dev/null and b/test/qglview/icons/edit-clear-locationbar-rtl.png differ diff --git a/test/qglview/icons/edit-clear.png b/test/qglview/icons/edit-clear.png new file mode 100644 index 0000000..19a1665 Binary files /dev/null and b/test/qglview/icons/edit-clear.png differ diff --git a/test/qglview/icons/edit-copy.png b/test/qglview/icons/edit-copy.png new file mode 100644 index 0000000..5cdeb5f Binary files /dev/null and b/test/qglview/icons/edit-copy.png differ diff --git a/test/qglview/icons/edit-delete.png b/test/qglview/icons/edit-delete.png new file mode 100644 index 0000000..87cd0b0 Binary files /dev/null and b/test/qglview/icons/edit-delete.png differ diff --git a/test/qglview/icons/edit-find.png b/test/qglview/icons/edit-find.png new file mode 100644 index 0000000..9a462c0 Binary files /dev/null and b/test/qglview/icons/edit-find.png differ diff --git a/test/qglview/icons/edit-guides.png b/test/qglview/icons/edit-guides.png new file mode 100644 index 0000000..d264839 Binary files /dev/null and b/test/qglview/icons/edit-guides.png differ diff --git a/test/qglview/icons/edit-paste.png b/test/qglview/icons/edit-paste.png new file mode 100644 index 0000000..a4e0a02 Binary files /dev/null and b/test/qglview/icons/edit-paste.png differ diff --git a/test/qglview/icons/item-add.png b/test/qglview/icons/item-add.png new file mode 100644 index 0000000..8a422c7 Binary files /dev/null and b/test/qglview/icons/item-add.png differ diff --git a/test/qglview/icons/item.png b/test/qglview/icons/item.png new file mode 100644 index 0000000..50bfddb Binary files /dev/null and b/test/qglview/icons/item.png differ diff --git a/test/qglview/icons/list-add.png b/test/qglview/icons/list-add.png new file mode 100644 index 0000000..1e03be9 Binary files /dev/null and b/test/qglview/icons/list-add.png differ diff --git a/test/qglview/icons/node-add.png b/test/qglview/icons/node-add.png new file mode 100644 index 0000000..80b5c61 Binary files /dev/null and b/test/qglview/icons/node-add.png differ diff --git a/test/qglview/icons/node.png b/test/qglview/icons/node.png new file mode 100644 index 0000000..fc8194e Binary files /dev/null and b/test/qglview/icons/node.png differ diff --git a/test/qglview/icons/qglview.png b/test/qglview/icons/qglview.png new file mode 100644 index 0000000..21bc50b Binary files /dev/null and b/test/qglview/icons/qglview.png differ diff --git a/test/qglview/icons/qglview.xcf b/test/qglview/icons/qglview.xcf new file mode 100644 index 0000000..50d33a6 Binary files /dev/null and b/test/qglview/icons/qglview.xcf differ diff --git a/test/qglview/icons/type-camera.png b/test/qglview/icons/type-camera.png new file mode 100644 index 0000000..c28aa2d Binary files /dev/null and b/test/qglview/icons/type-camera.png differ diff --git a/test/qglview/icons/type-geo.png b/test/qglview/icons/type-geo.png new file mode 100644 index 0000000..9204153 Binary files /dev/null and b/test/qglview/icons/type-geo.png differ diff --git a/test/qglview/icons/type-light.png b/test/qglview/icons/type-light.png new file mode 100644 index 0000000..687c8fe Binary files /dev/null and b/test/qglview/icons/type-light.png differ diff --git a/test/qglview/icons/view-grid.png b/test/qglview/icons/view-grid.png new file mode 100644 index 0000000..0f1d70c Binary files /dev/null and b/test/qglview/icons/view-grid.png differ diff --git a/test/qglview/icons/zoom-fit-best.png b/test/qglview/icons/zoom-fit-best.png new file mode 100644 index 0000000..07cfc98 Binary files /dev/null and b/test/qglview/icons/zoom-fit-best.png differ diff --git a/test/qglview/loader_3ds.cpp b/test/qglview/loader_3ds.cpp new file mode 100644 index 0000000..39811fb --- /dev/null +++ b/test/qglview/loader_3ds.cpp @@ -0,0 +1,348 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_3ds.h" + + +void Loader3DS::init3DSMesh(GLObjectBase * o, const QVector & smooth) { + QVector & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords()); + QVector & points(o->points), & puvws(o->puvws), fnormals; + QVector & faces(o->faces); + Vector3d pos = Vector3d(o->pos()); + bool has_uv = !puvws.isEmpty(); + Vector3i cf; + Vector3d v0, v1, v2, cn0, cn1, cn2; + fnormals.resize(faces.size()); + for (int i = 0; i < points.size(); ++i) + points[i] -= pos; + for (int i = 0; i < fnormals.size(); ++i) { + cf = faces[i]; + v0 = points[cf.p0]; + v1 = points[cf.p1]; + v2 = points[cf.p2]; + fnormals[i] = ((v1 - v0) * (v2 - v0)).normalized(); + } + int fcnt = faces.size() * 3; + vertices.resize(fcnt * 3); + normals.resize(vertices.size()); + if (has_uv) uvs.resize(fcnt * 2); + int ind = 0, induv = 0, ncnt0, ncnt1, ncnt2, csg; + //qDebug() << faces.size(); + if (smooth.isEmpty()) { + for (int i = 0; i < faces.size(); ++i) { + cf = faces[i]; + cn0 = fnormals[i]; + v0 = points[cf.p0]; + v1 = points[cf.p1]; + v2 = points[cf.p2]; + vertices[ind] = v0.x; normals[ind] = cn0.x; ++ind; + vertices[ind] = v0.y; normals[ind] = cn0.y; ++ind; + vertices[ind] = v0.z; normals[ind] = cn0.z; ++ind; + vertices[ind] = v1.x; normals[ind] = cn0.x; ++ind; + vertices[ind] = v1.y; normals[ind] = cn0.y; ++ind; + vertices[ind] = v1.z; normals[ind] = cn0.z; ++ind; + vertices[ind] = v2.x; normals[ind] = cn0.x; ++ind; + vertices[ind] = v2.y; normals[ind] = cn0.y; ++ind; + vertices[ind] = v2.z; normals[ind] = cn0.z; ++ind; + if (has_uv) { + uvs[induv] = puvws[cf.p0].x; ++induv; + uvs[induv] = puvws[cf.p0].y; ++induv; + uvs[induv] = puvws[cf.p1].x; ++induv; + uvs[induv] = puvws[cf.p1].y; ++induv; + uvs[induv] = puvws[cf.p2].x; ++induv; + uvs[induv] = puvws[cf.p2].y; ++induv; + } + } + } else { + for (int i = 0; i < faces.size(); ++i) { + cf = faces[i]; + csg = smooth[i]; + v0 = points[cf.p0]; + v1 = points[cf.p1]; + v2 = points[cf.p2]; + + cn0 = cn1 = cn2 = fnormals[i]; + ncnt0 = ncnt1 = ncnt2 = 1; + for (int j = 0; j < faces.size(); ++j) { + if (csg != smooth[j] || j == i) continue; + if (faces[j].p0 == cf.p0 || faces[j].p1 == cf.p0 || faces[j].p2 == cf.p0 || + points[faces[j].p0] == v0 || points[faces[j].p1] == v0 || points[faces[j].p2] == v0) { + cn0 += fnormals[j]; + ++ncnt0; + } + if (faces[j].p0 == cf.p1 || faces[j].p1 == cf.p1 || faces[j].p2 == cf.p1 || + points[faces[j].p0] == v1 || points[faces[j].p1] == v1 || points[faces[j].p2] == v1) { + cn1 += fnormals[j]; + ++ncnt1; + } + if (faces[j].p0 == cf.p2 || faces[j].p1 == cf.p2 || faces[j].p2 == cf.p2 || + points[faces[j].p0] == v2 || points[faces[j].p1] == v2 || points[faces[j].p2] == v2) { + cn2 += fnormals[j]; + ++ncnt2; + } + } + cn0 /= ncnt0; + cn1 /= ncnt1; + cn2 /= ncnt2; + vertices[ind] = v0.x; normals[ind] = cn0.x; ++ind; + vertices[ind] = v0.y; normals[ind] = cn0.y; ++ind; + vertices[ind] = v0.z; normals[ind] = cn0.z; ++ind; + vertices[ind] = v1.x; normals[ind] = cn1.x; ++ind; + vertices[ind] = v1.y; normals[ind] = cn1.y; ++ind; + vertices[ind] = v1.z; normals[ind] = cn1.z; ++ind; + vertices[ind] = v2.x; normals[ind] = cn2.x; ++ind; + vertices[ind] = v2.y; normals[ind] = cn2.y; ++ind; + vertices[ind] = v2.z; normals[ind] = cn2.z; ++ind; + if (has_uv) { + uvs[induv] = puvws[cf.p0].x; ++induv; + uvs[induv] = puvws[cf.p0].y; ++induv; + uvs[induv] = puvws[cf.p1].x; ++induv; + uvs[induv] = puvws[cf.p1].y; ++induv; + uvs[induv] = puvws[cf.p2].x; ++induv; + uvs[induv] = puvws[cf.p2].y; ++induv; + } + } + } +} + + +Material Loader3DS::materialByName(const QVector & materials, const QString & name) { + foreach (const Material & m, materials) + if (m.name == name) + return m; + return Material(); +} + + +GLObjectBase * loadFrom3DSFile(const QString & filepath, float scale) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader 3DS] Error: can`t open \"" + filepath + "\""; + return nullptr; + } + f.open(QIODevice::ReadOnly); + QDataStream stream(&f); + QVector materials; + QVector smooth; + QVector face_mats; + GLObjectBase * root = new GLObjectBase(), * co = nullptr; + Material mat; + Loader3DS::Chunk cc; + Loader3DS::Face face; + Vector3d pos; + QString str; + ushort cnt; + QString name; + QByteArray ba; + int cur_map = 0; + float fl, fl1, matrix[3][3]; + uint col; + root->setName(QFileInfo(f).baseName()); + while (!stream.atEnd()) { + stream.readRawData((char * )&cc, sizeof(cc)); + switch (cc.id) { + case LOADER_3DS_CHUNK_MAIN: /*qDebug() << "main" << cc.size;*/ break; + case LOADER_3DS_CHUNK_OBJECTS: /*qDebug() << " objects" << cc.size;*/ break; + case LOADER_3DS_CHUNK_OBJECT: + if (co != nullptr) { + Loader3DS::init3DSMesh(co, smooth); + root->addChild(co); + } + co = new GLObjectBase(); + co->setName(readCharsUntilNull(stream)); + smooth.clear(); + //qDebug() << " object" << co->name(); + break; + case LOADER_3DS_CHUNK_MESH: /*qDebug() << " mesh" << cc.size;*/ break; + case LOADER_3DS_CHUNK_VERTLIST: + stream.readRawData((char * )&cnt, sizeof(ushort)); + co->points.resize(cnt); + //qDebug() << " vertices" << cnt; + for (int i = 0; i < cnt; ++i) { + stream.readRawData((char * )&co->points[i].x, sizeof(float)); + stream.readRawData((char * )&co->points[i].y, sizeof(float)); + stream.readRawData((char * )&co->points[i].z, sizeof(float)); + co->points[i] *= scale; + } + break; + case LOADER_3DS_CHUNK_FACELIST: + stream.readRawData((char * )&cnt, sizeof(ushort)); + co->faces.resize(cnt); + //qDebug() << " faces" << cnt; + for (int i = 0; i < cnt; ++i) { + stream.readRawData((char * )&face, sizeof(Loader3DS::Face)); + co->faces[i].p0 = face.v0; + co->faces[i].p1 = face.v1; + co->faces[i].p2 = face.v2; + } + break; + case LOADER_3DS_CHUNK_FACEMAT: + name = readCharsUntilNull(stream); + stream.readRawData((char * )&cnt, sizeof(ushort)); + face_mats.resize(cnt); + for (int i = 0; i < cnt; ++i) + stream.readRawData((char * )&(face_mats[i]), sizeof(ushort)); + //qDebug() << " facemat name" << name << cnt; + co->material().name = name; + break; + case LOADER_3DS_CHUNK_MAPLIST: + stream.readRawData((char * )&cnt, sizeof(ushort)); + co->puvws.resize(cnt); + //qDebug() << " texcoords" << cnt; + for (int i = 0; i < cnt; ++i) { + stream.readRawData((char * )&co->puvws[i].x, sizeof(float)); + stream.readRawData((char * )&co->puvws[i].y, sizeof(float)); + } + break; + case LOADER_3DS_CHUNK_SMOOTH: + cnt = co->faces.size(); + smooth.resize(cnt); + //qDebug() << " smooth" << cnt; + for (int i = 0; i < cnt; ++i) + stream.readRawData((char * )&smooth[i], sizeof(uint)); + break; + case LOADER_3DS_CHUNK_TRMATRIX: + //qDebug() << co->name(); + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) + stream.readRawData((char * )&(matrix[i][j]), sizeof(float)); + //qDebug() << matrix[i][0] << matrix[i][1] << matrix[i][2]; + } + stream.readRawData((char * )&pos, sizeof(Vector3d)); + pos *= scale; + //qDebug() << "pos =" << pos; + co->setPos(pos.toQVector3D()); + break; + case LOADER_3DS_CHUNK_LIGHT: + //qDebug() << " light" << cc.size; + str = co->name(); + delete co; + co = new Light(); + co->setName(str); + stream.readRawData((char * )&pos, sizeof(Vector3d)); + pos *= scale; + co->setPos(pos.toQVector3D()); + break; + case LOADER_3DS_CHUNK_SPOTLIGHT: + stream.readRawData((char * )&pos, sizeof(Vector3d)); + pos *= scale; + globject_cast(co)->light_type = Light::Cone; + globject_cast(co)->direction = (pos.toQVector3D() - co->pos()).normalized(); + stream.readRawData((char * )&fl1, sizeof(float)); + stream.readRawData((char * )&fl, sizeof(float)); + globject_cast(co)->angle_start = fl1; + globject_cast(co)->angle_end = fl; + //qDebug() << "spotlight" << globject_cast(co)->direction << globject_cast(co)->angle_spread; + break; + case LOADER_3DS_CHUNK_LIGHT_OFF: + stream.skipRawData(cc.size - 6); + co->hide(); + break; + case LOADER_3DS_CHUNK_ATTENUATION_ON: + stream.skipRawData(cc.size - 6); + fl = globject_cast(co)->decay_end; + //fl1 = globject_cast(co)->decay_start; + globject_cast(co)->decay_quadratic = 4.f / fl; + //qDebug() << "decay" << globject_cast(co)->decay_quadratic; + break; + case LOADER_3DS_CHUNK_COLOR_F: + stream.readRawData((char * )&pos, sizeof(Vector3d)); + co->setColor(QColor::fromRgbF(pos.x, pos.y, pos.z)); + //qDebug() << " color_f" << co->color(); + break; + case LOADER_3DS_CHUNK_COLOR_B: + stream.readRawData((char * )&col, 3); + co->setColor(QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2])); + //qDebug() << " color_b" << co->color(); + break; + case LOADER_3DS_CHUNK_MULTIPLIER: + stream.readRawData((char * )&fl, sizeof(float)); + globject_cast(co)->intensity = fl; + //qDebug() << " multiplier" << fl; + break; + case LOADER_3DS_CHUNK_RANGE_START: + stream.readRawData((char * )&fl, sizeof(float)); + globject_cast(co)->decay_start = fl; + //qDebug() << " range start" << fl; + break; + case LOADER_3DS_CHUNK_RANGE_END: + stream.readRawData((char * )&fl, sizeof(float)); + globject_cast(co)->decay_end = fl; + //qDebug() << " range end" << fl; + break; + case LOADER_3DS_CHUNK_MATERIAL: + //stream.skipRawData(cc.size - 6); + if (!mat.name.isEmpty()) + materials << mat; + mat = Material(); + break; + case LOADER_3DS_CHUNK_MATERIAL_NAME: + mat.name = readCharsUntilNull(stream); + //qDebug() << "matname" << mat.name; + break; + case LOADER_3DS_CHUNK_AMBIENT_COLOR: + stream.skipRawData(cc.size - 9); + stream.readRawData((char * )&col, 3); + mat.color_self_illumination = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); + //qDebug() << "mat diffuse" << mat.color_diffuse; + break; + case LOADER_3DS_CHUNK_DIFFUSE_COLOR: + stream.skipRawData(cc.size - 9); + stream.readRawData((char * )&col, 3); + mat.color_diffuse = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); + //qDebug() << "mat diffuse" << mat.color_diffuse; + break; + case LOADER_3DS_CHUNK_SPECULAR_COLOR: + stream.skipRawData(cc.size - 9); + stream.readRawData((char * )&col, 3); + mat.color_specular = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); + //qDebug() << "mat diffuse" << mat.color_diffuse; + break; + case LOADER_3DS_CHUNK_TEXTURE_MAP: + cur_map = LOADER_3DS_CHUNK_TEXTURE_MAP; + break; + case LOADER_3DS_CHUNK_BUMP_MAP: + cur_map = LOADER_3DS_CHUNK_BUMP_MAP; + break; + case LOADER_3DS_CHUNK_REFLECTION_MAP: + cur_map = LOADER_3DS_CHUNK_REFLECTION_MAP; + break; + case LOADER_3DS_CHUNK_MAP_FILENAME: + name = readCharsUntilNull(stream); + //qDebug() << " mat map" << QString::number(cur_map, 16) << name; + switch (cur_map) { + case LOADER_3DS_CHUNK_TEXTURE_MAP: mat.map_diffuse.bitmap_path = name; break; + case LOADER_3DS_CHUNK_BUMP_MAP: mat.map_normal.bitmap_path = name; break; + } + break; + default: /*qDebug() << "???" << QString::number(cc.id, 16).rightJustified(4, '0') << cc.size;*/ stream.skipRawData(cc.size - 6); + } + } + if (!mat.name.isEmpty()) + materials << mat; + foreach (const Material & m, materials) + qDebug() << m.name; + if (co != nullptr) { + Loader3DS::init3DSMesh(co, smooth); + root->addChild(co); + } + for (int i = 0; i < root->childCount(); ++i) + root->child(i)->material() = Loader3DS::materialByName(materials, root->child(i)->material().name); + qDebug() << "[Loader 3DS] Loaded" << root->childCount() << "objects from" << filepath; + return root; +} diff --git a/test/qglview/loader_3ds.h b/test/qglview/loader_3ds.h new file mode 100644 index 0000000..2eeeb3d --- /dev/null +++ b/test/qglview/loader_3ds.h @@ -0,0 +1,77 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_3DS_H +#define LOADER_3DS_H + +#include "gltexture_manager.h" +#include "globject.h" +#include +#include + +#define LOADER_3DS_CHUNK_MAIN 0x4D4D // [-] сцена +#define LOADER_3DS_CHUNK_COLOR_F 0x0010 // [+] цвет во float +#define LOADER_3DS_CHUNK_COLOR_B 0x0011 // [+] цвет в byte +#define LOADER_3DS_CHUNK_OBJECTS 0x3D3D // [-] всяческие объекты +#define LOADER_3DS_CHUNK_OBJECT 0x4000 // [+] объект +#define LOADER_3DS_CHUNK_MESH 0x4100 // [-] mesh-объект +#define LOADER_3DS_CHUNK_VERTLIST 0x4110 // [+] список вершин +#define LOADER_3DS_CHUNK_FACELIST 0x4120 // [+] список граней +#define LOADER_3DS_CHUNK_FACEMAT 0x4130 // [+] материалы граней +#define LOADER_3DS_CHUNK_MAPLIST 0x4140 // [+] текстурные координаты +#define LOADER_3DS_CHUNK_SMOOTH 0x4150 // [+] группы сглаживания +#define LOADER_3DS_CHUNK_TRMATRIX 0x4160 // [+] матрица перевода +#define LOADER_3DS_CHUNK_LIGHT 0x4600 // [+] источник света +#define LOADER_3DS_CHUNK_SPOTLIGHT 0x4610 // [+] +#define LOADER_3DS_CHUNK_LIGHT_OFF 0x4620 // [+] +#define LOADER_3DS_CHUNK_ATTENUATION_ON 0x4625 // [+] +#define LOADER_3DS_CHUNK_RANGE_START 0x4659 // [+] +#define LOADER_3DS_CHUNK_RANGE_END 0x465A // [+] +#define LOADER_3DS_CHUNK_MULTIPLIER 0x465B // [+] +#define LOADER_3DS_CHUNK_CAMERA 0x4700 // [+] объект-камера +#define LOADER_3DS_CHUNK_MATERIAL 0xAFFF // [-] материал +#define LOADER_3DS_CHUNK_MATERIAL_NAME 0xA000 +#define LOADER_3DS_CHUNK_AMBIENT_COLOR 0xA010 +#define LOADER_3DS_CHUNK_DIFFUSE_COLOR 0xA020 +#define LOADER_3DS_CHUNK_SPECULAR_COLOR 0xA030 +#define LOADER_3DS_CHUNK_TEXTURE_MAP 0xA200 +#define LOADER_3DS_CHUNK_BUMP_MAP 0xA230 +#define LOADER_3DS_CHUNK_REFLECTION_MAP 0xA220 +#define LOADER_3DS_CHUNK_MAP_FILENAME 0xA300 +#define LOADER_3DS_CHUNK_MAP_PARAMETERS 0xA351 + +namespace Loader3DS { +#pragma pack(push, 1) + struct Chunk { + ushort id; + uint size; + }; + struct Face { + ushort v0; + ushort v1; + ushort v2; + ushort flags; + }; +#pragma pack(pop) + void init3DSMesh(GLObjectBase * o, const QVector & smooth); + Material materialByName(const QVector & materials, const QString & name); +} + +GLObjectBase * loadFrom3DSFile(const QString & filepath, float scale = 1.0); + +#endif // LOADER_3DS_H diff --git a/test/qglview/loader_ase.cpp b/test/qglview/loader_ase.cpp new file mode 100644 index 0000000..c5244f9 --- /dev/null +++ b/test/qglview/loader_ase.cpp @@ -0,0 +1,386 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_ase.h" + + +void LoaderASE::initASEMesh(GLObjectBase * o) { + QVector & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords()); + QVector & points(o->points), & puvws(o->puvws), & fnormals(o->normals); + QVector & faces(o->faces); + Vector3d pos = Vector3d(o->pos()); + bool has_uv = !puvws.isEmpty(), has_norms = !fnormals.isEmpty(); + Vector3i cf; + Vector3d v0, v1, v2, cn0, cn1, cn2; + int ni = 0; + for (int i = 0; i < points.size(); ++i) + points[i] -= pos; + if (!has_norms) { + fnormals.resize(faces.size() * 3); + for (int i = 0; i < faces.size(); ++i) { + cf = faces[i]; + v0 = points[cf.p0]; + v1 = points[cf.p1]; + v2 = points[cf.p2]; + cn0 = ((v1 - v0) * (v2 - v0)).normalized(); + fnormals[ni] = cn0; ++ni; + fnormals[ni] = cn0; ++ni; + fnormals[ni] = cn0; ++ni; + } + } + int fcnt = faces.size() * 3; + vertices.resize(fcnt * 3); + normals.resize(vertices.size()); + if (has_uv) uvs.resize(fcnt * 2); + int ind = 0, induv = 0; + qDebug() << "init ase" << faces.size() << "faces"; + ni = 0; + for (int i = 0; i < faces.size(); ++i) { + cf = faces[i]; + v0 = points[cf.p0]; + v1 = points[cf.p1]; + v2 = points[cf.p2]; + cn0 = fnormals[ni]; ++ni; + cn1 = fnormals[ni]; ++ni; + cn2 = fnormals[ni]; ++ni; + vertices[ind] = v0.x; normals[ind] = cn0.x; ++ind; + vertices[ind] = v0.y; normals[ind] = cn0.y; ++ind; + vertices[ind] = v0.z; normals[ind] = cn0.z; ++ind; + vertices[ind] = v1.x; normals[ind] = cn1.x; ++ind; + vertices[ind] = v1.y; normals[ind] = cn1.y; ++ind; + vertices[ind] = v1.z; normals[ind] = cn1.z; ++ind; + vertices[ind] = v2.x; normals[ind] = cn2.x; ++ind; + vertices[ind] = v2.y; normals[ind] = cn2.y; ++ind; + vertices[ind] = v2.z; normals[ind] = cn2.z; ++ind; + if (has_uv) { + uvs[induv] = puvws[cf.p0].x; ++induv; + uvs[induv] = puvws[cf.p0].y; ++induv; + uvs[induv] = puvws[cf.p1].x; ++induv; + uvs[induv] = puvws[cf.p1].y; ++induv; + uvs[induv] = puvws[cf.p2].x; ++induv; + uvs[induv] = puvws[cf.p2].y; ++induv; + } + } +} + + +GLObjectBase * loadFromASEFile(const QString & filepath, float scale) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader ASE] Error: can`t open" << filepath; + return nullptr; + } + f.open(QIODevice::ReadOnly); + //QVector materials; + GLObjectBase * root = new GLObjectBase(), * co = nullptr; + root->setName(QFileInfo(f).baseName()); + QTextStream stream(&f); + QVector materials; + QVector points, puvws; + QVector faces, uvws; + QVector normals; + Vector3d cv; + int mst = -1;//, mat_ind; + qint64 pst; + QString line, cname; + + + /// Parse materials + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MATERIAL_LIST {"); + } + line = stream.readLine().trimmed(); + mst = line.indexOf("MATERIAL_COUNT"); + materials.resize(line.right(line.length() - mst - 14).toInt()); + //qDebug() << materials.size() << "materials"; + for (int i = 0; i < materials.size(); ++i) { + materials[i].map_diffuse.bitmap_id = 0; + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MATERIAL " + QString::number(i) + " {"); + } + /// Parse material i + while (line != "}" && !stream.atEnd()) { + line = stream.readLine().trimmed(); + if (line.left(17) == "*MATERIAL_DIFFUSE") {materials[i].color_diffuse = colorFromString(line.right(line.length() - 18)); continue;} //qDebug() << "diffuse " << i << " = " << colorFromString(line.right(line.length() - 18)); + if (line.left(18) == "*MATERIAL_SPECULAR") {materials[i].color_specular = colorFromString(line.right(line.length() - 19)); continue;} //qDebug() << "specular " << i << " = " << colorFromString(line.right(line.length() - 19)); + if (line.left(23) == "*MATERIAL_SHINESTRENGTH") {materials[i].map_specular.color_amount = line.right(line.length() - 24).toFloat(); continue;} + if (line.left(15) == "*MATERIAL_SHINE") {materials[i].map_specularity.color_amount = 2.f / expf(line.right(line.length() - 16).toFloat()); continue;} + if (line.left(22) == "*MATERIAL_TRANSPARENCY") {materials[i].transparency = line.right(line.length() - 23).toFloat(); continue;} + if (line.left(12) == "*MAP_DIFFUSE") { + line = stream.readLine().trimmed(); + while (line.left(11) != "*MAP_AMOUNT" && !stream.atEnd()) line = stream.readLine().trimmed(); + materials[i].map_normal.color_amount = line.right(line.length() - 12).toFloat(); + while (line.left(7) != "*BITMAP" && !stream.atEnd()) line = stream.readLine().trimmed(); + materials[i].map_diffuse.bitmap_path = line.mid(9, line.length() - 10); + /*if (!materials[i].diffuse.bitmap_path.isEmpty()) { + materials[i].diffuse.bitmap_id = currentQGLView->bindTexture(QImage(materials[i].diffuse.bitmap_path)); + parent->textures << materials[i].diffuse.bitmap_id; + } + qDebug() << materials[i].diffuse.bitmap_path << ", bind to" << materials[i].diffuse.bitmap_id;*/ + while (line != "}" && !stream.atEnd()) line = stream.readLine().trimmed(); + line = ""; + continue; + } + if (line.left(9) == "*MAP_BUMP") { + line = stream.readLine().trimmed(); + while (line.left(11) != "*MAP_AMOUNT" && !stream.atEnd()) line = stream.readLine().trimmed(); + materials[i].map_normal.color_amount = line.right(line.length() - 12).toFloat(); + //qDebug() << "bump amount" << materials[i].bump.color_amount; + while (line.left(7) != "*BITMAP" && !stream.atEnd()) line = stream.readLine().trimmed(); + materials[i].map_normal.bitmap_path = line.mid(9, line.length() - 10); + /*if (!materials[i].bump.bitmap_path.isEmpty()) { + materials[i].bump.bitmap_id = currentQGLView->bindTexture(QImage(materials[i].bump.bitmap_path)); + parent->textures << materials[i].bump.bitmap_id; + } + qDebug() << materials[i].bump.bitmap_path << ", bind to" << materials[i].bump.bitmap_id;*/ + while (line != "}" && !stream.atEnd()) line = stream.readLine().trimmed(); + line = ""; + continue; + } + } + + } + //bs << materials; + /// Geometry objects + int cotype = 0; + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + if (line.indexOf("GEOMOBJECT {") >= 0 || line.indexOf("LIGHTOBJECT {") >= 0) { + if (line.indexOf("GEOMOBJECT {") >= 0) cotype = 0; + if (line.indexOf("LIGHTOBJECT {") >= 0) cotype = 1; + mst = -1; + if (co != nullptr) { + co->points = points; + co->faces = faces; + co->normals = normals; + co->uvws = uvws; + LoaderASE::initASEMesh(co); + root->addChild(co); + } + co = new GLObjectBase(); + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("NODE_NAME"); + } + cname = line.right(line.length() - mst - 10); + co->setName(cname.mid(1, cname.length() - 2)); + qDebug() << co->name(); + } + mst = -1; + switch (cotype) { + case 0: + //qDebug() << "object"; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH {"); + } + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NUMVERTEX "); + } + points.resize(line.right(line.length() - mst - 15).toInt()); + //qDebug() << points.size() << "vertices"; + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NUMFACES "); + } + faces.resize(line.right(line.length() - mst - 14).toInt()); + normals.resize(faces.size() * 3); + //qDebug() << faces.size() << "faces"; + //uvws.resize(faces.size()); + /// Points + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_VERTEX_LIST {"); + } + for (int i = 0; i < points.size(); ++i) { + line = stream.readLine().trimmed(); + mst = line.indexOf("MESH_VERTEX"); + points[i] = Vector3d(line.right(line.length() - mst - 17)) * scale; + //qDebug() << points[i]; + } + /// Faces + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_FACE_LIST {"); + } + for (int i = 0; i < faces.size(); ++i) { + line = stream.readLine().trimmed(); + mst = line.indexOf("MESH_FACE"); + line = line.right(line.length() - mst - 15); + mst = line.indexOf("A:"); line = line.right(line.length() - mst - 2); + mst = line.indexOf("B:"); + faces[i].p0 = line.left(mst).toInt(); line = line.right(line.length() - mst - 2); + mst = line.indexOf("C:"); + faces[i].p1 = line.left(mst).toInt(); line = line.right(line.length() - mst - 2); + mst = line.indexOf("AB"); + faces[i].p2 = line.left(mst).toInt(); + //qDebug() << faces[i]; + } + /// Texture coordinates + mst = -1; + pst = stream.pos(); + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NUMTVERTEX "); + } + if (mst >= 0) { + puvws.resize(line.right(line.length() - mst - 16).toInt()); + //qDebug() << puvws.size() << "tvertices"; + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_TVERTLIST {"); + } + for (int i = 0; i < puvws.size(); ++i) { + line = stream.readLine().trimmed(); + mst = line.indexOf("MESH_TVERT"); line = line.right(line.length() - mst - 10); + mst = line.indexOf("\t"); line = line.right(line.length() - mst - 1); + puvws[i] = Vector3d(line); + } + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NUMTVFACES "); + } + uvws.resize(line.right(line.length() - mst - 16).toInt()); + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_TFACELIST {"); + } + for (int i = 0; i < uvws.size(); ++i) { + line = stream.readLine().trimmed(); + mst = line.indexOf("MESH_TFACE"); line = line.right(line.length() - mst - 10); + mst = line.indexOf("\t"); line = line.right(line.length() - mst - 1); + uvws[i] = Vector3i(line); + } + } else { + uvws.clear(); + uvws.resize(faces.size()); + stream.seek(pst); + } + if (puvws.size() <= 0) puvws.resize(1); + /// Normals + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NORMALS {"); + } + for (int i = 0; i < faces.size(); ++i) { + line = stream.readLine(); + line = stream.readLine().trimmed(); mst = line.indexOf(" "); + line = line.right(line.length() - mst - 1); normals[i * 3] = Vector3d(line); + line = stream.readLine().trimmed(); mst = line.indexOf(" "); + line = line.right(line.length() - mst - 1); normals[i * 3 + 1] = Vector3d(line); + line = stream.readLine().trimmed(); mst = line.indexOf(" "); + line = line.right(line.length() - mst - 1); normals[i * 3 + 2] = Vector3d(line); + //qDebug() << normals[i][0] << normals[i][1] << normals[i][2]; + } + /// Material index + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MATERIAL_REF "); + } + //mat_ind = line.right(line.length() - mst - 13).toInt(); + //qDebug() << mat_ind.back(); + if (points.size() == 0 || faces.size() == 0) { + mst = -1; + continue; + } + + /// Compiling into GLList + /*glNewList(model, GL_COMPILE); + if (mat_ind < 0 || mat_ind >= materials.size()) { + mat_diffuse[0] = cfr; + mat_diffuse[1] = cfg; + mat_diffuse[2] = cfb; + glColor3f(cfr, cfg, cfb); + glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, mat_none); + glMaterialf(GL_FRONT, GL_SHININESS, 0.); + } else { + materials[mat_ind].apply(); + //parent->material_ = materials[mat_ind]; + } + glBegin(GL_TRIANGLES); + if (normals_) { + for (int i = 0; i < faces.size(); ++i) { + glNormal3d(normals[i][0].x, normals[i][0].y, normals[i][0].z); + glTexCoord3d(puvws[uvws[i].p0].x, puvws[uvws[i].p0].y, puvws[uvws[i].p0].z); + cv = points[faces[i].p0] * scale; glVertex3d(cv.x, cv.y, cv.z); + glNormal3d(normals[i][1].x, normals[i][1].y, normals[i][1].z); + glTexCoord3d(puvws[uvws[i].p1].x, puvws[uvws[i].p1].y, puvws[uvws[i].p1].z); + cv = points[faces[i].p1] * scale; glVertex3d(cv.x, cv.y, cv.z); + glNormal3d(normals[i][2].x, normals[i][2].y, normals[i][2].z); + glTexCoord3d(puvws[uvws[i].p2].x, puvws[uvws[i].p2].y, puvws[uvws[i].p2].z); + cv = points[faces[i].p2] * scale; glVertex3d(cv.x, cv.y, cv.z); + } + } else { + for (int i = 0; i < faces.size(); ++i) { + glTexCoord3d(puvws[uvws[i].p0].x, puvws[uvws[i].p0].y, puvws[uvws[i].p0].z); + cv = points[faces[i].p0] * scale; glVertex3d(cv.x, cv.y, cv.z); + glTexCoord3d(puvws[uvws[i].p1].x, puvws[uvws[i].p1].y, puvws[uvws[i].p1].z); + cv = points[faces[i].p1] * scale; glVertex3d(cv.x, cv.y, cv.z); + glTexCoord3d(puvws[uvws[i].p2].x, puvws[uvws[i].p2].y, puvws[uvws[i].p2].z); + cv = points[faces[i].p2] * scale; glVertex3d(cv.x, cv.y, cv.z); + } + } + glEnd();*/ + + ///// Save binary + //bs << mat_ind << points << faces << puvws << uvws << normals; + break; + case 1: + qDebug() << "light"; + mst = -1; + while (mst < 0 && !stream.atEnd()) { + line = stream.readLine(); + mst = line.indexOf("MESH_NORMALS {"); + } + break; + } + /// Continue + mst = -1; + } + f.close(); + if (co != nullptr) { + co->points = points; + co->faces = faces; + co->normals = normals; + co->uvws = uvws; + LoaderASE::initASEMesh(co); + root->addChild(co); + } + /*parent->points = points; + parent->puvws = puvws; + parent->faces = faces; + parent->uvws = uvws; + parent->normals = normals; + return model;*/ + return root; +} diff --git a/test/qglview/loader_ase.h b/test/qglview/loader_ase.h new file mode 100644 index 0000000..050e77e --- /dev/null +++ b/test/qglview/loader_ase.h @@ -0,0 +1,33 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_ASE_H +#define LOADER_ASE_H + +#include "globject.h" +#include +#include +#include + +namespace LoaderASE { + void initASEMesh(GLObjectBase * o); +} + +GLObjectBase * loadFromASEFile(const QString & filepath, float scale = 1.0); + +#endif // LOADER_ASE_H diff --git a/test/qglview/loader_dae.cpp b/test/qglview/loader_dae.cpp new file mode 100644 index 0000000..7c4a1f1 --- /dev/null +++ b/test/qglview/loader_dae.cpp @@ -0,0 +1,426 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_dae.h" +#include +#include + + +Material LoaderDAE::materialByName(const QVector & materials, const QString & name) { + foreach (const Material & m, materials) { + //qDebug() << m.name << " ??? " << name; + if (m.name == name) + return m; + } + return Material(); +} + + +QColor readXMLColor(QDomElement n) { + QStringList sl(n.firstChildElement("color").firstChild().nodeValue().trimmed().split(" ")); + sl.removeAll(""); + sl.removeAll(" "); + if (sl.size() >= 3) + return QColor::fromRgbF(sl[0].toDouble(), sl[1].toDouble(), sl[2].toDouble(), sl.size() >= 4 ? sl[3].toDouble() : 1.); + return QColor(); +} + + +QVector4D readXMLVector(QDomElement n) { + QStringList sl(n.firstChild().nodeValue().trimmed().split(" ")); + sl.removeAll(""); + sl.removeAll(" "); + if (sl.size() == 3) return QVector4D(sl[0].toFloat(), sl[1].toFloat(), sl[2].toFloat(), 0.); + if (sl.size() == 4) return QVector4D(sl[0].toFloat(), sl[1].toFloat(), sl[2].toFloat(), sl[3].toFloat()); + return QVector4D(); +} + + +QMatrix4x4 readXMLMatrix(QDomElement n) { + QStringList sl(n.firstChild().nodeValue().trimmed().split(" ")); + sl.removeAll(""); + sl.removeAll(" "); + if (sl.size() != 16) return QMatrix4x4(); + QMatrix4x4 m; + int ind = -1; + for (int r = 0; r < 4; ++r) + for (int c = 0; c < 4; ++c) + m(r, c) = sl[++ind].toFloat(); + return m; +} + + +float readXMLFloat(QDomElement n) { + return n.firstChildElement("float").firstChild().nodeValue().toFloat(); +} + + +QString readXMLTexture(QDomElement n, QDomElement prof, QDomElement li) { + QString tex = n.firstChildElement("texture").attribute("texture"); + if (tex.isEmpty()) return QString(); + QString tag; + QDomNodeList elist = prof.elementsByTagName("newparam"); + if (elist.isEmpty()) { + tag = tex; + } else { + bool found = false; + int cnt = 0; + while (!tex.isEmpty() && !found && cnt < 10) { + found = false; + cnt++; + for (int i = 0; i < elist.count(); ++i) { + QDomNode dn = elist.at(i); + if (dn.attributes().namedItem("sid").nodeValue() == tex) { + //qDebug() << "found!"; + if (dn.firstChild().nodeName() == "sampler2D") { + tex = dn.firstChildElement("sampler2D").firstChildElement("source").firstChild().nodeValue(); + break; + } + if (dn.firstChild().nodeName() == "surface") { + tag = dn.firstChildElement("surface").firstChildElement("init_from").firstChild().nodeValue(); + //qDebug() << tex << "->" << tag; + tex.clear(); + found = true; + break; + } + } + } + } + if (cnt == 10) return QString(); + } + //qDebug() << tag; + if (tag.isEmpty()) return QString(); + elist = li.elementsByTagName("image"); + for (int i = 0; i < elist.count(); ++i) { + QDomElement dn = elist.at(i).toElement(); + if (dn.attribute("id") == tag) { + tex = dn.firstChildElement("init_from").firstChild().nodeValue(); + tex.replace("\\", "/"); + if (tex.startsWith("file:") && tex.mid(5, 3) != "///") tex.insert(6, "/"); + //qDebug() << "found" << tex << QUrl(tex).toLocalFile(); + tex = QUrl(tex).toLocalFile(); + if (tex == "/") tex.clear(); + return tex; + } + } + return QString(); +} + + +QVector LoaderDAE::readMaterials(QDomElement le, QDomElement li, bool fbx) { + QVector ret; + QDomNodeList elist = le.elementsByTagName("effect"); + for (int i = 0; i < elist.count(); ++i) { + QDomNode dn = elist.at(i); + Material mat; + mat.name = dn.attributes().namedItem("id").nodeValue(); + QDomElement prof = dn.firstChildElement("profile_COMMON"); + QDomNode pn = prof.firstChildElement("technique").firstChild(); + QColor col; + QString text; + + col = readXMLColor(pn.firstChildElement("emission")); + if (col.isValid()) mat.color_self_illumination = col; + col = readXMLColor(pn.firstChildElement("diffuse")); + if (col.isValid()) mat.color_diffuse = col; + col = readXMLColor(pn.firstChildElement("specular")); + if (col.isValid()) mat.color_specular = col; + mat.map_specularity.color_amount = 2.f / expf(readXMLFloat(pn.firstChildElement("shininess"))); + mat.transparency = readXMLFloat(pn.firstChildElement("transparency")); + if (!fbx) mat.transparency = 1.f - mat.transparency; + text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li); + if (!text.isEmpty()) mat.map_diffuse.bitmap_path = text; + text = readXMLTexture(pn.firstChildElement("diffuse"), prof, li); + if (!text.isEmpty()) mat.map_diffuse.bitmap_path = text; + + pn = prof.firstChildElement("technique").firstChildElement("extra").firstChild(); + text = readXMLTexture(pn.firstChildElement("bump"), prof, li); + if (!text.isEmpty()) mat.map_normal.bitmap_path = text; + + ret << mat; + qDebug() << "** Material" << mat.name; + qDebug() << " emission" << mat.color_self_illumination; + qDebug() << " diffuse" << mat.color_diffuse; + qDebug() << " specular" << mat.color_specular; + qDebug() << " transparency" << mat.transparency; + } + return ret; +} + + +QMatrix4x4 readXMLTransformations(QDomElement n) { + QMatrix4x4 tm; + QDomNodeList trl = n.childNodes(); + for (int i = 0; i < trl.count(); ++i) { + QDomElement dt = trl.at(i).toElement(); + } + return tm; +} + + +void readScene(QDomElement n, QMatrix4x4 cm, QVector, QMatrix4x4> > & ret, QString last_name = QString()) { + QDomNodeList evsl = n.childNodes(); + if (n.hasAttribute("name")) last_name = n.attribute("name"); + for (int i = 0; i < evsl.count(); ++i) { + QDomElement dt = evsl.at(i).toElement(); + QVector4D v; +//qDebug() << dt.nodeName(); + if (dt.nodeName() == "translate") { + v = readXMLVector(dt); + cm.translate(v.toVector3D()); + continue; + } + if (dt.nodeName() == "rotate") { + v = readXMLVector(dt); + cm.rotate(v.w(), v.toVector3D()); + continue; + } + if (dt.nodeName() == "scale") { + v = readXMLVector(dt); + cm.scale(v.toVector3D()); + continue; + } + if (dt.nodeName() == "matrix") { + QMatrix4x4 m = readXMLMatrix(dt); + cm *= m; + continue; + } + if (dt.nodeName() == "node") { + readScene(dt, cm, ret, last_name); + continue; + } + if (dt.nodeName() == "instance_geometry" || dt.nodeName() == "instance_light") { + QString gid = dt.attribute("url"); + if (gid.startsWith("#")) gid.remove(0, 1); +//qDebug() << "matrix" << gid << cm; + ret << QPair, QMatrix4x4>(QPair(gid, last_name), cm); + continue; + } + //qDebug() << name << m; + } + ret << QPair, QMatrix4x4>(QPair("", last_name), cm); +} + + +GLObjectBase * loadFromDAEFile(const QString & filepath, float scale) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader DAE] Error: can`t open \"" + filepath + "\""; + return nullptr; + } + QTime tm; + tm.restart(); + QDomDocument dom(filepath); + if (!dom.setContent(&f)) { + qDebug() << "[Loader DAE] Error: can`t parse \"" + filepath + "\""; + return nullptr; + } + //qDebug() << "parse" << tm.elapsed(); + tm.restart(); + QDomElement maine = dom.firstChildElement("COLLADA"); + bool fbx = maine.firstChildElement("asset").firstChildElement("contributor").firstChildElement("authoring_tool").firstChild().nodeValue().startsWith("FBX"); + QVector materials = LoaderDAE::readMaterials(maine.firstChildElement("library_effects"), + maine.firstChildElement("library_images"), fbx); + GLObjectBase * root = new GLObjectBase(), * co = nullptr; + QMap > objects; + + QMap mat_names; + QDomElement mvse = maine.firstChildElement("library_visual_scenes").firstChildElement("visual_scene"); + QDomNodeList evsl = mvse.elementsByTagName("instance_material"); + QDomNodeList matl = maine.firstChildElement("library_materials").elementsByTagName("material"); + for (int i = 0; i < evsl.count(); ++i) { + QDomElement dn = evsl.at(i).toElement(); + QString tn = dn.attribute("target"); + if (tn.startsWith("#")) tn.remove(0, 1); + for (int j = 0; j < matl.count(); ++j) { + QDomElement dm = matl.at(j).toElement(); + if (dm.attribute("id") == tn) { + QString en = dm.firstChildElement("instance_effect").attribute("url"); + if (en.startsWith("#")) en.remove(0, 1); + mat_names[dn.attribute("symbol")] = en; + //qDebug() << dn.attribute("symbol") << "->" << en; + } + } + } + + QDomNodeList elist = maine.firstChildElement("library_geometries").elementsByTagName("geometry"); + for (int i = 0; i < elist.count(); ++i) { + QDomNode dn = elist.at(i); + QString name = dn.attributes().namedItem("name").nodeValue(); + QString gid = dn.attributes().namedItem("id").nodeValue(); + if (name.isEmpty()) continue; + dn = dn.firstChildElement("mesh"); + QMap source_names; + QMap > source_data; + QDomNodeList esrc = dn.toElement().elementsByTagName("source"); + for (int j = 0; j < esrc.count(); ++j) { + QDomNode ds = esrc.at(j); + QString id = ds.attributes().namedItem("id").nodeValue(); + QDomNodeList evert = dn.toElement().elementsByTagName("vertices"); + for (int k = 0; k < evert.count(); ++k) { + QDomNode dv = evert.at(k); + QString vid = dv.attributes().namedItem("id").nodeValue(); + if (dv.firstChildElement("input").attribute("source") == ("#" + id)) + source_names[vid] = id; +//qDebug() << " found source sin" << vid; + } + QVector & sd(source_data[id]); + int stride = ds.firstChildElement("technique_common").firstChildElement("accessor").attribute("stride").toInt(); + QString astr = ds.firstChildElement("float_array").firstChild().nodeValue().trimmed(); + astr.replace("\n", " "); + astr.remove("\r"); + QStringList sl = astr.split(" "); + sl.removeAll(""); + sl.removeAll(" "); + for (int c = 0; c < sl.size(); c += stride) { + Vector3d v; + if (stride >= 1) v.x = sl[c].toFloat(); + if (stride >= 2) v.y = sl[c + 1].toFloat(); + if (stride >= 3) v.z = sl[c + 2].toFloat(); + sd << v; + } +//qDebug() << " found source" << id << "stride =" << stride << ":" << sd; +//qDebug() << " readed" << sd.size(); + } + QDomNodeList etr = dn.toElement().elementsByTagName("triangles"); + //QMatrix4x4 m = matrices.value(gid); +//qDebug() << "found geom" << name; + QVector ol; + for (int j = 0; j < etr.count(); ++j) { + QDomElement ds = etr.at(j).toElement(); + QString matname = mat_names[ds.attribute("material")]; + QVector p; + QStringList psl = ds.firstChildElement("p").firstChild().nodeValue().trimmed().split(" "); + foreach (const QString & s, psl) + p << s.toInt(); + QDomNodeList einp = ds.elementsByTagName("input"); + int pbv = einp.count();//, tc = qMin(ds.attribute("count").toInt(), p.size() / pbv); + co = new GLObjectBase(); + co->setName(name + "_" + QString::number(j)); + //co->setTransform(m); + co->material() = LoaderDAE::materialByName(materials, matname); + qDebug() << " tri" << co->material().name << matname; + QVector & vertices(co->VBO().vertices()), & normals(co->VBO().normals()), & uvs(co->VBO().texcoords()); + for (int k = 0; k < einp.count(); ++k) { + QDomElement di = einp.at(k).toElement(); + QString src = di.attribute("source"), sem = di.attribute("semantic").toLower(); + int offset = di.attribute("offset").toInt(); + QVector * curv = nullptr; + int pccnt = 0; + if (sem == "vertex") {curv = &vertices; pccnt = 3;} + if (sem == "normal") {curv = &normals; pccnt = 3;} + if (sem == "texcoord") {curv = &uvs; pccnt = 2;} + if (curv == nullptr) continue; + if (src.startsWith("#")) src.remove(0, 1); + QVector & data(source_data[source_names.value(src, src)]); + for (int ii = offset; ii < p.size(); ii += pbv) { + if ((p[ii] >= 0) && (p[ii] < data.size())) { + Vector3d v = data[p[ii]]; + (*curv) << v.x << v.y; + if (pccnt == 3) (*curv) << v.z; + } + } + //qDebug() << " input" << sem << "from" << data.size() << "->" << (*curv) << pbv; + } + //qDebug() << "geom" << gid << co; + ol << co; + } + objects[gid] = ol; + } + + elist = maine.firstChildElement("library_lights").elementsByTagName("light"); + for (int i = 0; i < elist.count(); ++i) { + QDomElement dn = elist.at(i).toElement(); + QString name = dn.attributes().namedItem("name").nodeValue(); + QString gid = dn.attributes().namedItem("id").nodeValue(); + if (name.isEmpty() || name == "EnvironmentAmbientLight") continue; + QDomElement dl = dn.firstChildElement("technique_common").firstChild().toElement(); + Light * lo = new Light(); + if (dl.nodeName() == "point") lo->light_type = Light::Omni; + else if (dl.nodeName() == "spot") lo->light_type = Light::Cone; + else { + delete lo; + continue; + } + lo->setColor(readXMLColor(dl)); + QDomNodeList ml = dn.elementsByTagName("multiplier"); + if (!ml.isEmpty()) lo->intensity = ml.at(0).firstChild().nodeValue().toFloat(); + ml = dn.elementsByTagName("intensity"); + if (!ml.isEmpty()) lo->intensity = ml.at(0).firstChild().nodeValue().toFloat(); + lo->setColor(lo->color() / lo->intensity); + QString sv; + sv = dl.firstChildElement("constant_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_const = sv.toFloat(); + sv = dl.firstChildElement("linear_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_linear = sv.toFloat(); + sv = dl.firstChildElement("quadratic_attenuation").firstChild().nodeValue(); if (!sv.isEmpty()) lo->decay_quadratic = sv.toFloat(); + ///lo->setTransform(matrices.value(name)); + if (lo->light_type == Light::Cone) { + ml = dn.elementsByTagName("decay_falloff"); if (!ml.isEmpty()) lo->angle_end = ml.at(0).firstChild().nodeValue().toFloat(); + ml = dn.elementsByTagName("hotspot_beam"); if (!ml.isEmpty()) lo->angle_start = ml.at(0).firstChild().nodeValue().toFloat(); + } + QVector ol; + ol << lo; + objects[gid] = ol; + //qDebug() << "light" << name; + } + //qDebug() << "readed" << objects.size(); + + QVector, QMatrix4x4> > scene; + readScene(mvse, QMatrix4x4(), scene); + for (int i = 0; i < scene.size(); ++i) { + QPair, QMatrix4x4> so = scene[i]; + if (so.first.first.isEmpty()) continue; + QVector ol = objects.value(so.first.first); + foreach (GLObjectBase * o, ol) { + o = o->clone(); + o->setName(so.first.second); + o->setTransform(so.second); + root->addChild(o); + //qDebug() << " add" << so.first.second << o->name(); + } + //qDebug() << "add" << so.first << ol.size(); + } + + for (int i = 0; i < root->childCount(); ++i) { + GLObjectBase * o = root->child(i); + if (o->type() == GLObjectBase::glLight) { + Light * l = (Light*)o; + if (l->light_type == Light::Directional || l->light_type == Light::Cone) { + QString tn = l->name() + ".Target"; + //qDebug() << "search target" << tn; + for (int s = 0; s < scene.size(); ++s) { + QPair, QMatrix4x4> so = scene[s]; + if (so.first.second == tn) { + //qDebug() << "found target" << tn; + QVector3D tp = so.second.column(3).toVector3D(); + l->direction = (tp - l->pos()).normalized(); + //qDebug() << "dir" << l->direction; + } + } + } + } + } + + QList > dol = objects.values(); + for (int i = 0; i < dol.size(); ++i) + for (int j = 0; j < dol[i].size(); ++j) + delete dol[i][j]; + + root->setScale(0.001f); + qDebug() << "[Loader DAE] Loaded" << root->childCount() << "objects from" << filepath; + return root; +} diff --git a/test/qglview/loader_dae.h b/test/qglview/loader_dae.h new file mode 100644 index 0000000..42da2e6 --- /dev/null +++ b/test/qglview/loader_dae.h @@ -0,0 +1,35 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_DAE_H +#define LOADER_DAE_H + +#include "gltexture_manager.h" +#include "globject.h" +#include +#include +#include + +namespace LoaderDAE { + Material materialByName(const QVector & materials, const QString & name); + QVector readMaterials(QDomElement le, QDomElement li, bool fbx); +} + +GLObjectBase * loadFromDAEFile(const QString & filepath, float scale = 1.0); + +#endif // LOADER_DAE_H diff --git a/test/qglview/loader_obj.cpp b/test/qglview/loader_obj.cpp new file mode 100644 index 0000000..c4bce7f --- /dev/null +++ b/test/qglview/loader_obj.cpp @@ -0,0 +1,270 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_obj.h" + + +void LoaderOBJ::initOBJMesh(GLObjectBase * o, const QVector & src_vertices, const QVector & src_normals, const QVector & src_texcoords) { + QVector & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords()); + QVector & faces(o->faces), & uvws(o->uvws), & norms(o->norms); + //Vector3d pos = Vector3d(o->pos()); + bool has_uv = !uvws.isEmpty(); + Vector3i cf, ct, cn; + Vector3d v[3], t[3], n[3]; + //for (int i = 0; i < points.size(); ++i) + // points[i] -= pos; + int fcnt = faces.size() * 3; + vertices.resize(fcnt * 3); + normals.resize(vertices.size()); + if (has_uv) uvs.resize(fcnt * 2); + int ind = 0, induv = 0; + //qDebug() << "initOBJMesh" << faces.size(); + for (int i = 0; i < faces.size(); ++i) { + cf = faces[i]; + ct = uvws[i]; + cn = norms[i]; + v[0] = src_vertices[cf.p0]; + v[1] = src_vertices[cf.p1]; + v[2] = src_vertices[cf.p2]; + n[0] = src_normals[cn.p0]; + n[1] = src_normals[cn.p1]; + n[2] = src_normals[cn.p2]; + vertices[ind] = v[0].x; normals[ind] = n[0].x; ++ind; + vertices[ind] = v[0].y; normals[ind] = n[0].y; ++ind; + vertices[ind] = v[0].z; normals[ind] = n[0].z; ++ind; + vertices[ind] = v[1].x; normals[ind] = n[1].x; ++ind; + vertices[ind] = v[1].y; normals[ind] = n[1].y; ++ind; + vertices[ind] = v[1].z; normals[ind] = n[1].z; ++ind; + vertices[ind] = v[2].x; normals[ind] = n[2].x; ++ind; + vertices[ind] = v[2].y; normals[ind] = n[2].y; ++ind; + vertices[ind] = v[2].z; normals[ind] = n[2].z; ++ind; + if (has_uv) { + if ((ct.p0 >= 0) && (ct.p1 >= 0) && (ct.p2 >= 0)) { + t[0] = src_texcoords[ct.p0]; + t[1] = src_texcoords[ct.p1]; + t[2] = src_texcoords[ct.p2]; + uvs[induv] = t[0].x; ++induv; + uvs[induv] = t[0].y; ++induv; + uvs[induv] = t[1].x; ++induv; + uvs[induv] = t[1].y; ++induv; + uvs[induv] = t[2].x; ++induv; + uvs[induv] = t[2].y; ++induv; + } + } + } +} + + +Vector3d readVector3d(QString s) { + Vector3d ret; + QStringList sl(s.trimmed().split(" ")); + sl.removeAll(""); sl.removeAll(" "); + if (sl.size() > 0) ret.x = sl[0].toFloat(); + if (sl.size() > 1) ret.y = sl[1].toFloat(); + if (sl.size() > 2) ret.z = sl[2].toFloat(); + return ret; +} + + +Vector2d readVector2d(QString s) { + Vector2d ret; + QStringList sl(s.trimmed().split(" ")); + sl.removeAll(""); sl.removeAll(" "); + if (sl.size() > 0) ret.x = sl[0].toFloat(); + if (sl.size() > 1) ret.y = sl[1].toFloat(); + return ret; +} + + +QColor readColor(QString s) { + Vector3d c = readVector3d(s); + return QColor::fromRgbF(c.x, c.y, c.z); +} + + +void readFaces(QString s, GLObjectBase * co) { + QStringList sl(s.trimmed().split(" ")); + sl.removeAll(""); sl.removeAll(" "); + static Vector3i inds[4]; + for (int i = 0; i < sl.size(); ++i) { + inds[i].p0 = inds[i].p1 = inds[i].p2 = 0; + QStringList sl2(sl[i].split("/")); + if (sl2.size() > 4) continue; + inds[i].p0 = sl2[0].toInt(); + inds[i].p1 = sl2[1].toInt(); + inds[i].p2 = sl2[2].toInt(); + } + if (sl.size() == 3) { + co->faces << Vector3i(inds[0].p0 - 1, inds[1].p0 - 1, inds[2].p0 - 1); + co->uvws << Vector3i(inds[0].p1 - 1, inds[1].p1 - 1, inds[2].p1 - 1); + co->norms << Vector3i(inds[0].p2 - 1, inds[1].p2 - 1, inds[2].p2 - 1); + } + if (sl.size() == 4) { + co->faces << Vector3i(inds[0].p0 - 1, inds[1].p0 - 1, inds[2].p0 - 1); + co->uvws << Vector3i(inds[0].p1 - 1, inds[1].p1 - 1, inds[2].p1 - 1); + co->norms << Vector3i(inds[0].p2 - 1, inds[1].p2 - 1, inds[2].p2 - 1); + co->faces << Vector3i(inds[0].p0 - 1, inds[2].p0 - 1, inds[3].p0 - 1); + co->uvws << Vector3i(inds[0].p1 - 1, inds[2].p1 - 1, inds[3].p1 - 1); + co->norms << Vector3i(inds[0].p2 - 1, inds[2].p2 - 1, inds[3].p2 - 1); + } +} + + +QVector readMTL(QString obj_path, QString path) { + QVector ret; + QStringList sp = GLTextureManagerBase::searchPathes(); + sp.prepend(QFileInfo(obj_path).absoluteDir().path()); + QFile f(findFile(path, sp)); + if (!f.open(QIODevice::ReadOnly)) { + qDebug() << "[Loader OBJ] Warning: can`t open \"" + path + "\""; + return ret; + } + QTextStream stream(&f); + QString name; + Material mat; + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith("newmtl")) { + if (!mat.name.isEmpty()) + ret << mat; + mat = Material(); + mat.name = line.mid(6).trimmed(); + continue; + } + if (line.startsWith("Kd")) { + mat.color_diffuse = readColor(line.mid(2).trimmed()); + continue; + } + if (line.startsWith("Ke")) { + mat.color_self_illumination = readColor(line.mid(2).trimmed()); + continue; + } + if (line.startsWith("Ks")) { + Vector3d v = readVector3d(line.mid(2).trimmed()); + mat.map_specular.color_amount = v.length(); + float mc = qMax(v.x, qMax(v.y, v.z)); + if (mc > 0.f) v /= mc; + mat.color_specular = QColor::fromRgbF(v.x, v.y, v.z); + //qDebug() << mat.shine_strength << mat.color_specular; + continue; + } + if (line.startsWith("Ns")) { + mat.map_specularity.color_amount = 2.f / expf(line.mid(2).trimmed().toFloat()); + continue; + } + if (line.startsWith("d")) { + mat.transparency = 1.f - line.mid(1).trimmed().toFloat(); + continue; + } + if (line.startsWith("map_Kd")) { + mat.map_diffuse.bitmap_path = findFile(line.mid(6).trimmed(), sp); + continue; + } + if (line.startsWith("map_bump")) { + line = line.mid(8).trimmed(); + if (line.startsWith("-bm")) { + line = line.mid(3).trimmed(); + QString sv = line.left(line.indexOf(" ")); + line = line.mid(sv.size()).trimmed(); + mat.map_normal.color_amount = sv.toFloat(); + } + mat.map_normal.bitmap_path = findFile(line, sp); + //qDebug() << "BUMP" << mat.name << mat.bump_scale << mat.bump.bitmap_path; + continue; + } + } + if (!mat.name.isEmpty()) + ret << mat; + qDebug() << "load from MTL" << f.fileName() << ret.size() << "materials"; + return ret; +} + + +Material LoaderOBJ::materialByName(const QVector & materials, const QString & name) { + foreach (const Material & m, materials) + if (m.name == name) + return m; + return Material(); +} + + +GLObjectBase * loadFromOBJFile(const QString & filepath, float scale) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader OBJ] Error: can`t open \"" + filepath + "\""; + return nullptr; + } + f.open(QIODevice::ReadOnly); + QTextStream stream(&f); + QVector vertices, normals, texcoords; + QVector materials; + GLObjectBase * root = new GLObjectBase(), * co = nullptr; + QString name; + root->setName(QFileInfo(f).baseName()); + int cnt = 0; + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + if (line.startsWith("mtllib")) { + materials = readMTL(filepath, line.mid(6).trimmed()); + continue; + } + if (line.startsWith("v ")) { + vertices << readVector3d(line.mid(1)); + continue; + } + if (line.startsWith("vt ")) { + texcoords << readVector3d(line.mid(2)); + continue; + } + if (line.startsWith("vn ")) { + normals << readVector3d(line.mid(2)); + continue; + } + if (line.startsWith("g ")) { + name = line.mid(1).trimmed(); + if (co != nullptr) { + LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords); + root->addChild(co); + } + co = new GLObjectBase(); + co->setName(name); + //qDebug() << "new object" << co->name(); + continue; + } + if (line.startsWith("f ")) { + readFaces(line.mid(2), co); + continue; + } + if (line.startsWith("usemtl")) { + if (!co->faces.isEmpty()) { + LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords); + root->addChild(co); + co = new GLObjectBase(); + co->setName(name + "_" + QString::number(cnt++)); + } + co->material() = LoaderOBJ::materialByName(materials, line.mid(6).trimmed()); + continue; + } + } + if (co != nullptr) { + LoaderOBJ::initOBJMesh(co, vertices, normals, texcoords); + root->addChild(co); + } + qDebug() << "[Loader OBJ] Loaded" << root->childCount() << "objects from" << filepath; + return root; +} diff --git a/test/qglview/loader_obj.h b/test/qglview/loader_obj.h new file mode 100644 index 0000000..c73362b --- /dev/null +++ b/test/qglview/loader_obj.h @@ -0,0 +1,42 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_OBJ_H +#define LOADER_OBJ_H + +#include "gltexture_manager.h" +#include "globject.h" +#include +#include + +namespace LoaderOBJ { +#pragma pack(push, 1) + struct Face { + ushort v0; + ushort v1; + ushort v2; + ushort flags; + }; +#pragma pack(pop) + void initOBJMesh(GLObjectBase * o, const QVector & vertices, const QVector & normals, const QVector & texcoords); + Material materialByName(const QVector & materials, const QString & name); +} + +GLObjectBase * loadFromOBJFile(const QString & filepath, float scale = 1.0); + +#endif // LOADER_3DS_H diff --git a/test/qglview/loader_qgl.cpp b/test/qglview/loader_qgl.cpp new file mode 100644 index 0000000..2f94f09 --- /dev/null +++ b/test/qglview/loader_qgl.cpp @@ -0,0 +1,69 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_qgl.h" +#include "chunkstream.h" + + +GLObjectBase * loadFromQGLFile(const QString & filepath) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader QGL] Error: can`t open \"" + filepath + "\""; + return 0; + } + f.open(QIODevice::ReadOnly); + QDataStream s(&f); + s.setVersion(QDataStream::Qt_4_8); + char sign[4]; + s.readRawData(sign, 4); + if ((sign[0] != 'Q') || (sign[1] != 'G') || (sign[2] != 'L') || (sign[3] != 'F')) { + qDebug() << "[Loader QGL] Error: \"" + filepath + "\" is not valid QGL file!"; + return 0; + } + GLObjectBase * root = 0; + ushort version = 0xFFFF; + f.peek((char*)&version, 2); + if (version == 1) { + s.skipRawData(2); + s >> root; + } else { + qDebug() << "[Loader QGL] Error: \"" + filepath + "\" unsupported version!"; + return 0; + } + root->buildTransform(); + if (root->name().isEmpty()) + root->setName(QFileInfo(f).baseName()); + qDebug() << "[Loader QGL] Loaded" << root->childCount() << "objects from" << filepath; + return root; +} + + +bool saveToQGLFile(const QString & filepath, const GLObjectBase * o) { + QFile f(filepath); + if (!f.open(QIODevice::ReadWrite)) + return false; + f.resize(0); + QDataStream s(&f); + s.setVersion(QDataStream::Qt_4_8); + char sign[4] = {'Q', 'G', 'L', 'F'}; + ushort version = 1; + s.writeRawData(sign, 4); + s.writeRawData((char*)&version, 2); + s << o; + return true; +} diff --git a/test/qglview/loader_qgl.h b/test/qglview/loader_qgl.h new file mode 100644 index 0000000..08a1e35 --- /dev/null +++ b/test/qglview/loader_qgl.h @@ -0,0 +1,31 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_QGL_H +#define LOADER_QGL_H + +#include "globject.h" +#include + +namespace LoaderQGL { +} + +GLObjectBase * loadFromQGLFile(const QString & filepath); +bool saveToQGLFile(const QString & filepath, const GLObjectBase * o); + +#endif // LOADER_QGL_H diff --git a/test/qglview/material_editor.cpp b/test/qglview/material_editor.cpp new file mode 100644 index 0000000..af0cc78 --- /dev/null +++ b/test/qglview/material_editor.cpp @@ -0,0 +1,206 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "material_editor.h" +#include "ui_material_editor.h" + + +MaterialEditor::MaterialEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialEditor(); + ui->setupUi(this); + ui->frameReflection->hide(); + active = true; +} + + +void MaterialEditor::changeEvent(QEvent * e) { + return; + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialEditor::setMaterial(const Material & m) { + active = false; + ui->colorDiffuse->setColor(m.color_diffuse); + ui->colorSpecular->setColor(m.color_specular); + ui->colorSelfIllum->setColor(m.color_self_illumination); + ui->checkGlass->setChecked(m.glass); + ui->spinTransparent->setValue(m.transparency); + ui->spinReflect->setValue(m.reflectivity); + ui->spinIOF->setValue(m.iof); + ui->spinDispersion->setValue(m.dispersion); + ui->mapDiffuse->setMap(m.map_diffuse); + ui->mapSpecular->setMap(m.map_specular); + ui->mapSelfIllum->setMap(m.map_self_illumination); + ui->mapSpecularity->setMap(m.map_specularity); + ui->mapBump->setMap(m.map_normal); + ui->mapRelief->setMap(m.map_relief); + ui->lineReflFront->setProperty("GLpath", m.map_reflection.path(0)); ui->lineReflFront->setText(QFileInfo(m.map_reflection.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", m.map_reflection.path(1)); ui->lineReflBack->setText(QFileInfo(m.map_reflection.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", m.map_reflection.path(2)); ui->lineReflLeft->setText(QFileInfo(m.map_reflection.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", m.map_reflection.path(3)); ui->lineReflRight->setText(QFileInfo(m.map_reflection.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", m.map_reflection.path(4)); ui->lineReflTop->setText(QFileInfo(m.map_reflection.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", m.map_reflection.path(5)); ui->lineReflBottom->setText(QFileInfo(m.map_reflection.path(5)).fileName()); + active = true; +} + + +Material MaterialEditor::material() { + Material m; + m.color_diffuse = ui->colorDiffuse->color(); + m.color_specular = ui->colorSpecular->color(); + m.color_self_illumination = ui->colorSelfIllum->color(); + m.glass = ui->checkGlass->isChecked(); + m.transparency = ui->spinTransparent->value(); + m.reflectivity = ui->spinReflect->value(); + m.iof = ui->spinIOF->value(); + m.dispersion = ui->spinDispersion->value(); + m.map_diffuse = ui->mapDiffuse->map(); + m.map_specular = ui->mapSpecular->map(); + m.map_self_illumination = ui->mapSelfIllum->map(); + m.map_specularity = ui->mapSpecularity->map(); + m.map_normal = ui->mapBump->map(); + m.map_relief = ui->mapRelief->map(); + m.map_reflection.setPath(0, ui->lineReflFront->property("GLpath").toString()); + m.map_reflection.setPath(1, ui->lineReflBack->property("GLpath").toString()); + m.map_reflection.setPath(2, ui->lineReflLeft->property("GLpath").toString()); + m.map_reflection.setPath(3, ui->lineReflRight->property("GLpath").toString()); + m.map_reflection.setPath(4, ui->lineReflTop->property("GLpath").toString()); + m.map_reflection.setPath(5, ui->lineReflBottom->property("GLpath").toString()); + return m; +} + + +void MaterialEditor::on_buttonReflFrontSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflFront->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflFront->setProperty("GLpath", str); + ui->lineReflFront->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBack->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBack->setProperty("GLpath", str); + ui->lineReflBack->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflLeft->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflLeft->setProperty("GLpath", str); + ui->lineReflLeft->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflRight->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflRight->setProperty("GLpath", str); + ui->lineReflRight->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflTop->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflTop->setProperty("GLpath", str); + ui->lineReflTop->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBottom->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBottom->setProperty("GLpath", str); + ui->lineReflBottom->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflFrontClear_clicked() { + ui->lineReflFront->setText(""); + ui->lineReflFront->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackClear_clicked() { + ui->lineReflBack->setText(""); + ui->lineReflBack->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftClear_clicked() { + ui->lineReflLeft->setText(""); + ui->lineReflLeft->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightClear_clicked() { + ui->lineReflRight->setText(""); + ui->lineReflRight->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopClear_clicked() { + ui->lineReflTop->setText(""); + ui->lineReflTop->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomClear_clicked() { + ui->lineReflBottom->setText(""); + ui->lineReflBottom->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonLoadCubeDir_clicked() { + QString dir = QFileDialog::getExistingDirectory(this, "Select directory", ui->lineReflFront->property("GLpath").toString()); + if (dir.isEmpty()) return; + GLCubeTexture cb(0); + cb.loadPathesFromDirectory(dir); + active = false; + ui->lineReflFront->setProperty("GLpath", cb.path(0)); ui->lineReflFront->setText(QFileInfo(cb.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", cb.path(1)); ui->lineReflBack->setText(QFileInfo(cb.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", cb.path(2)); ui->lineReflLeft->setText(QFileInfo(cb.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", cb.path(3)); ui->lineReflRight->setText(QFileInfo(cb.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", cb.path(4)); ui->lineReflTop->setText(QFileInfo(cb.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", cb.path(5)); ui->lineReflBottom->setText(QFileInfo(cb.path(5)).fileName()); + active = true; + materialChanged(); +} diff --git a/test/qglview/material_editor.h b/test/qglview/material_editor.h new file mode 100644 index 0000000..fb23302 --- /dev/null +++ b/test/qglview/material_editor.h @@ -0,0 +1,65 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MATERIAL_EDITOR_H +#define MATERIAL_EDITOR_H + +#include +#include "glmaterial.h" + +namespace Ui { + class MaterialEditor; +}; + +class MaterialEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialEditor(QWidget * parent = 0); + + void setMaterial(const Material & m); + Material material(); + +protected: + void changeEvent(QEvent * e); + + bool active; + Ui::MaterialEditor * ui; + +private slots: + void materialChanged() {if (active) emit changed();} + void on_buttonReflFrontSelect_clicked(); + void on_buttonReflFrontClear_clicked(); + void on_buttonReflBackSelect_clicked(); + void on_buttonReflBackClear_clicked(); + void on_buttonReflLeftSelect_clicked(); + void on_buttonReflLeftClear_clicked(); + void on_buttonReflRightSelect_clicked(); + void on_buttonReflRightClear_clicked(); + void on_buttonReflTopSelect_clicked(); + void on_buttonReflTopClear_clicked(); + void on_buttonReflBottomSelect_clicked(); + void on_buttonReflBottomClear_clicked(); + void on_buttonLoadCubeDir_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIAL_EDITOR_H diff --git a/test/qglview/material_editor.ui b/test/qglview/material_editor.ui new file mode 100644 index 0000000..4ac23ce --- /dev/null +++ b/test/qglview/material_editor.ui @@ -0,0 +1,779 @@ + + + MaterialEditor + + + + 0 + 0 + 470 + 791 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + 2 + + + + + font:bold; + + + Diffuse + + + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Specular + + + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Self illumination + + + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Specularity + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Normal + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Relief + + + + + + font:normal; + + + + + + + + + + Glass + + + + + + + Transparency + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflectivity + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + IOF + + + + + + + 2.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Dispersion + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflection map + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Front: + + + + + + + Back: + + + + + + + Left: + + + + + + + Right: + + + + + + + Top: + + + + + + + Bottom: + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + load from directory + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + MaterialMapEditor + QWidget +
material_map_editor.h
+ 1 + + changed() + +
+
+ + + + colorDiffuse + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 326 + 49 + + + 282 + 17 + + + + + colorSpecular + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 325 + 128 + + + 284 + 45 + + + + + spinTransparent + valueChanged(double) + MaterialEditor + materialChanged() + + + 443 + 448 + + + 283 + 149 + + + + + spinReflect + valueChanged(double) + MaterialEditor + materialChanged() + + + 443 + 470 + + + 284 + 174 + + + + + spinIOF + valueChanged(double) + MaterialEditor + materialChanged() + + + 443 + 492 + + + 284 + 236 + + + + + checkGlass + toggled(bool) + MaterialEditor + materialChanged() + + + 443 + 382 + + + 284 + 84 + + + + + colorSelfIllum + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 376 + 202 + + + 326 + 63 + + + + + spinDispersion + valueChanged(double) + MaterialEditor + materialChanged() + + + 460 + 514 + + + 326 + 288 + + + + + mapDiffuse + changed() + MaterialEditor + materialChanged() + + + 445 + 63 + + + 469 + 63 + + + + + mapSpecular + changed() + MaterialEditor + materialChanged() + + + 443 + 143 + + + 467 + 143 + + + + + mapSelfIllum + changed() + MaterialEditor + materialChanged() + + + 447 + 216 + + + 468 + 216 + + + + + mapBump + changed() + MaterialEditor + materialChanged() + + + 450 + 308 + + + 469 + 260 + + + + + mapRelief + changed() + MaterialEditor + materialChanged() + + + 450 + 353 + + + 469 + 304 + + + + + mapSpecularity + changed() + MaterialEditor + materialChanged() + + + 442 + 257 + + + 474 + 250 + + + + + + materialChanged() + +
diff --git a/test/qglview/material_map_editor.cpp b/test/qglview/material_map_editor.cpp new file mode 100644 index 0000000..ceaddfe --- /dev/null +++ b/test/qglview/material_map_editor.cpp @@ -0,0 +1,88 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "material_map_editor.h" +#include "ui_material_map_editor.h" + + +MaterialMapEditor::MaterialMapEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialMapEditor(); + ui->setupUi(this); + active = true; +} + + +void MaterialMapEditor::changeEvent(QEvent * e) { + return; + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialMapEditor::resizeEvent(QResizeEvent * e) { + ui->iconedLabel->setFixedWidth(ui->iconedLabel->height()); + ui->iconedLabel->setIconSize(ui->iconedLabel->size()); +} + + +void MaterialMapEditor::updateIcon() { + ui->iconedLabel->setIcon(QIcon(ui->linePath->property("GLpath").toString())); +} + + +void MaterialMapEditor::setMap(const Map & m) { + active = false; + ui->sliderAmount->setValue(m.color_amount); + ui->sliderOffset->setValue(m.color_offset); + ui->linePath->setProperty("GLpath", m.bitmap_path); ui->linePath->setText(QFileInfo(m.bitmap_path).fileName()); + updateIcon(); + active = true; +} + + +Map MaterialMapEditor::map() { + Map m; + m.color_amount = ui->sliderAmount->value(); + m.color_offset = ui->sliderOffset->value(); + m.bitmap_path = ui->linePath->property("GLpath").toString(); + return m; +} + + +void MaterialMapEditor::on_buttonSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->linePath->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->linePath->setProperty("GLpath", str); + ui->linePath->setText(QFileInfo(str).fileName()); + updateIcon(); + mapChanged(); +} + + +void MaterialMapEditor::on_buttonClear_clicked() { + ui->linePath->setText(""); + ui->linePath->setProperty("GLpath", ""); + updateIcon(); + mapChanged(); +} diff --git a/test/qglview/material_map_editor.h b/test/qglview/material_map_editor.h new file mode 100644 index 0000000..4384558 --- /dev/null +++ b/test/qglview/material_map_editor.h @@ -0,0 +1,56 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MATERIAL_MAP_EDITOR_H +#define MATERIAL_MAP_EDITOR_H + +#include +#include "glmaterial.h" + +namespace Ui { + class MaterialMapEditor; +}; + +class MaterialMapEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialMapEditor(QWidget * parent = 0); + + void setMap(const Map & m); + Map map(); + +protected: + void changeEvent(QEvent * e); + void resizeEvent(QResizeEvent * e); + void updateIcon(); + + bool active; + Ui::MaterialMapEditor * ui; + +private slots: + void mapChanged() {if (active) emit changed();} + void on_buttonSelect_clicked(); + void on_buttonClear_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIAL_MAP_EDITOR_H diff --git a/test/qglview/material_map_editor.ui b/test/qglview/material_map_editor.ui new file mode 100644 index 0000000..f97cf3d --- /dev/null +++ b/test/qglview/material_map_editor.ui @@ -0,0 +1,178 @@ + + + MaterialMapEditor + + + + 0 + 0 + 509 + 74 + + + + + 0 + + + 0 + + + + + IconedLabel::RightToLeft + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + Amount: + + + + + + + -10.000000000000000 + + + 10.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + + + + + Offset: + + + + + + + -10.000000000000000 + + + 10.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + IconedLabel + QFrame +
iconedlabel.h
+
+
+ + + + linePath + textChanged(QString) + MaterialMapEditor + mapChanged() + + + 175 + 19 + + + 99 + 73 + + + + + sliderAmount + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 440 + 38 + + + 512 + 37 + + + + + sliderOffset + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 497 + 66 + + + 511 + 65 + + + + + + mapChanged() + +
diff --git a/test/qglview/openglwindow.cpp b/test/qglview/openglwindow.cpp new file mode 100644 index 0000000..8dc5d39 --- /dev/null +++ b/test/qglview/openglwindow.cpp @@ -0,0 +1,95 @@ +#include "openglwindow.h" +#include +#include +#include +#include + + +OpenGLWindow::OpenGLWindow(QWindow *parent) + : QWindow(parent) + , m_context(nullptr) + , m_device(nullptr) +{ + setFlag(Qt::FramelessWindowHint); + setSurfaceType(QWindow::OpenGLSurface); + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); +// qDebug() << format; +#ifdef QT_OPENGL_ES_2 + format.setRenderableType(QSurfaceFormat::OpenGLES); +#else + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + format.setVersion(2, 0); + format.setProfile(QSurfaceFormat::NoProfile); + } +#endif + format.setDepthBufferSize(24); + format.setSamples(8); +// format.setStencilBufferSize(8); + setFormat(format); + QSurfaceFormat::setDefaultFormat(format); +} + + +OpenGLWindow::~OpenGLWindow() { + delete m_device; +} + + +void OpenGLWindow::render(QPainter *painter) { +} + + +void OpenGLWindow::initialize() { +} + + +void OpenGLWindow::render() { +// if (!m_device) m_device = new QOpenGLPaintDevice; +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +// m_device->setSize(size() * devicePixelRatio()); +// m_device->setDevicePixelRatio(devicePixelRatio()); +// QPainter painter(m_device); +// render(&painter); +} + + +void OpenGLWindow::renderLater() { + requestUpdate(); +} + + +bool OpenGLWindow::event(QEvent *event) { + switch (event->type()) { + case QEvent::UpdateRequest: + renderNow(); + return true; + default: + return QWindow::event(event); + } +} + + +void OpenGLWindow::exposeEvent(QExposeEvent *event) { + if (isExposed()) renderNow(); +} + + +void OpenGLWindow::renderNow() { + if (!isExposed()) + return; + bool needsInitialize = false; + if (!m_context) { + m_context = new QOpenGLContext(this); + m_context->setFormat(requestedFormat()); + m_context->create(); + needsInitialize = true; + } + m_context->makeCurrent(this); + if (needsInitialize) { + initializeOpenGLFunctions(); + initialize(); + } + render(); + m_context->swapBuffers(this); +} + diff --git a/test/qglview/openglwindow.h b/test/qglview/openglwindow.h new file mode 100644 index 0000000..6816665 --- /dev/null +++ b/test/qglview/openglwindow.h @@ -0,0 +1,36 @@ +#include +#include + +class QPainter; +class QOpenGLContext; +class QOpenGLPaintDevice; + + +class OpenGLWindow : public QWindow, protected QOpenGLFunctions +{ + Q_OBJECT +public: + explicit OpenGLWindow(QWindow *parent = nullptr); + ~OpenGLWindow(); + + virtual void render(QPainter *painter); + virtual void render(); + + virtual void initialize(); + + QOpenGLContext * context() {return m_context;} + +public slots: + void renderLater(); + void renderNow(); + +protected: + bool event(QEvent *event) override; + + void exposeEvent(QExposeEvent *event) override; + +private: + QOpenGLContext *m_context; + QOpenGLPaintDevice *m_device; +}; + diff --git a/test/qglview/plugin/CMakeLists.txt b/test/qglview/plugin/CMakeLists.txt new file mode 100644 index 0000000..2cdd25f --- /dev/null +++ b/test/qglview/plugin/CMakeLists.txt @@ -0,0 +1,12 @@ +project(qglview_plugin) +include_directories("..") +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_NO_DEBUG) +add_definitions(-DQT_SHARED) +add_definitions(-DQDESIGNER_EXPORT_WIDGETS) +find_qt(${QtVersions} Core Designer Gui Widgets OpenGL) +qt_sources(SRC) +qt_wrap(${SRC} CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qglview) +qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtPlugins/designer) diff --git a/test/qglview/plugin/qglview_designerplugin.cpp b/test/qglview/plugin/qglview_designerplugin.cpp new file mode 100644 index 0000000..708776b --- /dev/null +++ b/test/qglview/plugin/qglview_designerplugin.cpp @@ -0,0 +1,14 @@ +#include "qglview_designerplugin.h" +#include "qglviewplugin.h" + + +QGLViewDesignerPlugin::QGLViewDesignerPlugin(QObject * parent): QObject(parent) +{ + m_widgets.append(new QGLViewPlugin(this)); +} + + +QList QGLViewDesignerPlugin::customWidgets() const { + return m_widgets; +} + diff --git a/test/qglview/plugin/qglview_designerplugin.h b/test/qglview/plugin/qglview_designerplugin.h new file mode 100644 index 0000000..db6fc67 --- /dev/null +++ b/test/qglview/plugin/qglview_designerplugin.h @@ -0,0 +1,22 @@ +#ifndef QGLVIEW_DESIGNERPLUGIN_H +#define QGLVIEW_DESIGNERPLUGIN_H + +#include +#include + + +class QGLViewDesignerPlugin: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "qad.qglview") + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +public: + QGLViewDesignerPlugin(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QGLVIEW_DESIGNERPLUGIN_H diff --git a/test/qglview/plugin/qglviewplugin.cpp b/test/qglview/plugin/qglviewplugin.cpp new file mode 100644 index 0000000..d20f2f2 --- /dev/null +++ b/test/qglview/plugin/qglviewplugin.cpp @@ -0,0 +1,95 @@ +#include "glwidget.h" +#include "qglviewplugin.h" +#include +#include "glprimitives.h" +#include "qglview.h" + + +QGLViewPlugin::QGLViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QGLViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QGLViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QGLViewPlugin::createWidget(QWidget * parent) { + GLWidget * w = new GLWidget(parent); + if (m_initialized) { + auto axis = new GLObjectBase(); + GLObjectBase * obj; + float al = 1.; + obj = new GLPrimitiveLine(QVector3D(0, 0, -al), QVector3D(0, 0, al)); + obj->material().color_diffuse = Qt::darkBlue; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(-al, 0, 0), QVector3D(al, 0, 0)); + obj->material().color_diffuse = Qt::darkRed; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(0, -al, 0), QVector3D(0, al, 0)); + obj->material().color_diffuse = Qt::darkGreen; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + w->view()->addObject(axis); + auto cam_light = new Light(); + cam_light->intensity = 0.5; + cam_light->setName("Camera_Light"); + w->view()->camera()->addChild(cam_light); + w->start(); + } + return w; +} + + +QString QGLViewPlugin::name() const { + return QLatin1String("GLWidget"); +} + + +QString QGLViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon QGLViewPlugin::icon() const { + return QIcon("://icons/qglview.png"); +} + + +QString QGLViewPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString QGLViewPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool QGLViewPlugin::isContainer() const { + return false; +} + + +QString QGLViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QGLViewPlugin::includeFile() const { + return QLatin1String("glwidget.h"); +} + diff --git a/test/qglview/plugin/qglviewplugin.h b/test/qglview/plugin/qglviewplugin.h new file mode 100644 index 0000000..6516afb --- /dev/null +++ b/test/qglview/plugin/qglviewplugin.h @@ -0,0 +1,33 @@ +#ifndef QGLVIEWPLUGIN_H +#define QGLVIEWPLUGIN_H + +#include +#include + + +class QGLViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit QGLViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif //QGLVIEWPLUGIN_H diff --git a/test/qglview/propertyeditor.cpp b/test/qglview/propertyeditor.cpp new file mode 100644 index 0000000..cce7e95 --- /dev/null +++ b/test/qglview/propertyeditor.cpp @@ -0,0 +1,408 @@ +#include "propertyeditor.h" + + +QWidget * Delegate::widgetForProperty(QWidget * parent, const QModelIndex & index) const { + QWidget * w = 0; + int type = 0; + QVariant value = index.data(Qt::UserRole); + if (index.data(Qt::UserRole + 2).toString() == "__flags") return 0; + if (index.data(Qt::UserRole + 1).toString() == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + //QMetaProperty prop = index.parent().data(Qt::UserRole + 1).value(); + w = new QCheckBox(parent); type = 14; ((QCheckBox*)w)->setChecked(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0)); + ((QCheckBox*)w)->setText("0x" + QString::number(key, 16).toUpper()); + connect((QCheckBox*)w, SIGNAL(clicked(bool)), this, SLOT(changedFlag())); + //qDebug() << prop.enumerator().name(); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + w = new QComboBox(parent); type = 13; ((QComboBox*)w)->setCurrentIndex(value.toInt()); + w->setProperty("__prop", QVariant::fromValue(prop.first)); + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + ((QComboBox*)w)->addItem(QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")", menum.value(i)); + if (menum.value(i) == prop.second.toInt()) + ((QComboBox*)w)->setCurrentIndex(i); + } + connect((QComboBox*)w, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); + } + } else { + switch (value.type()) { + case QVariant::Int: w = new QSpinBox(parent); type = 2; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::UInt: w = new QSpinBox(parent); type = 3; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::LongLong: w = new QSpinBox(parent); type = 4; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::ULongLong: w = new QSpinBox(parent); type = 5; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::Double: w = new QDoubleSpinBox(parent); type = 6; ((QDoubleSpinBox*)w)->setRange(-999999999, 999999999); ((QDoubleSpinBox*)w)->setDecimals(3); connect((QDoubleSpinBox*)w, SIGNAL(valueChanged(double)), this, SLOT(changed())); break; + case QVariant::Bool: w = new QCheckBox(parent); type = 7; ((QCheckBox*)w)->setChecked(value.toBool()); connect((QCheckBox*)w, SIGNAL(toggled(bool)), this, SLOT(changed())); break; + case QVariant::Color: w = new ColorButton(parent); type = 8; ((ColorButton*)w)->setUseAlphaChannel(true); ((ColorButton*)w)->setColor(value.value()); connect((ColorButton*)w, SIGNAL(colorChanged(QColor)), this, SLOT(changed())); break; + case QVariant::Point: w = new QPointEdit(parent); type = 9; ((QPointEdit*)w)->setDecimals(0); ((QPointEdit*)w)->setValue(QPointF(value.toPoint())); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::PointF: w = new QPointEdit(parent); type = 10; ((QPointEdit*)w)->setDecimals(3); ((QPointEdit*)w)->setValue(value.toPointF()); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::Rect: w = new QRectEdit(parent); type = 11; ((QRectEdit*)w)->setDecimals(0); ((QRectEdit*)w)->setValue(QRectF(value.toRect())); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::RectF: w = new QRectEdit(parent); type = 12; ((QRectEdit*)w)->setDecimals(3); ((QRectEdit*)w)->setValue(value.toRectF()); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::String: default: w = new CLineEdit(parent); type = 1; ((CLineEdit*)w)->setDefaultText(value.toString()); connect((CLineEdit*)w, SIGNAL(textChanged(QString)), this, SLOT(changed())); break; + } + } + } + if (w == 0) return 0; + /*QPalette pal = w->palette(); + pal.setColor(QPalette::Window, Qt::white); + w->setPalette(pal);*/ + w->setAutoFillBackground(true); + w->setProperty("__type", type); + return w; +} + + +void Delegate::setWidgetProperty(QWidget * w, const QVariant & value) const { + if (w == 0) return; + switch (w->property("__type").toInt()) { + case 1: ((CLineEdit*)w)->setText(value.toString()); break; + case 2: case 3: case 4: case 5: ((QSpinBox*)w)->setValue(value.toInt()); break; + case 6: ((QDoubleSpinBox*)w)->setValue(value.toDouble()); break; + case 7: ((QCheckBox*)w)->setChecked(value.toBool()); break; + case 8: ((ColorButton*)w)->setColor(value.value()); break; + case 9: ((QPointEdit*)w)->setValue(value.value()); break; + case 10: ((QPointEdit*)w)->setValue(value.value()); break; + case 11: ((QRectEdit*)w)->setValue(value.value()); break; + case 12: ((QRectEdit*)w)->setValue(value.value()); break; + } +} + + +const QVariant Delegate::widgetProperty(QWidget * w) const { + if (w == 0) return QVariant(); + switch (w->property("__type").toInt()) { + case 1: return QVariant::fromValue(((CLineEdit*)w)->text()); break; + case 2: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 3: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 4: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 5: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 6: return QVariant::fromValue(((QDoubleSpinBox*)w)->value()); break; + case 7: return QVariant::fromValue(((QCheckBox*)w)->isChecked()); break; + case 8: return QVariant::fromValue(((ColorButton*)w)->color()); break; + case 9: return QVariant::fromValue(((QPointEdit*)w)->value().toPoint()); break; + case 10: return QVariant::fromValue(((QPointEdit*)w)->value()); break; + case 11: return QVariant::fromValue(((QRectEdit*)w)->value().toRect()); break; + case 12: return QVariant::fromValue(((QRectEdit*)w)->value()); break; + case 13: return QVariant::fromValue(PropertyValuePair(w->property("__prop").value(), ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex()))); break; + default: return QVariant(); break; + } + return QVariant(); +} + + +void Delegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + if (index.data(Qt::UserRole + 1).toString() != "__flag") + model->setData(index, widgetProperty(editor), Qt::UserRole); +} + + +void Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QStyledItemDelegate::paint(painter, option, index); + QVariant value = index.data(Qt::UserRole); + QStyle * style = QApplication::style(); + QStyleOption * so = 0; + QStyleOptionComplex * soc = 0; + QString text; + QRect rect; + QPalette::ColorRole role = (option.state.testFlag(QStyle::State_Selected) && option.state.testFlag(QStyle::State_Active) ? QPalette::HighlightedText : QPalette::WindowText); + if (index.data(Qt::UserRole + 2).toString() == "__flags") { + text = "0x" + QString::number(value.toInt(), 16).toUpper(); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + return; + } + if (index.data(Qt::UserRole + 1) == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + so = new QStyleOptionButton(); + so->rect = option.rect; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0) ? QStyle::State_On : QStyle::State_Off) | option.state; + ((QStyleOptionButton*)so)->text = "0x" + QString::number(key, 16).toUpper(); + if (option.state.testFlag(QStyle::State_Selected)) + so->palette.setColor(QPalette::WindowText, so->palette.color(QPalette::HighlightedText)); + style->drawControl(QStyle::CE_CheckBox, so, painter); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + if (menum.value(i) == prop.second.toInt()) { + text = QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")"; + break; + } + } + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + } + } else { + switch (value.type()) { + case QVariant::Int: + text.setNum(value.toInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::UInt: + text.setNum(value.toUInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::LongLong: + text.setNum(value.toLongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::ULongLong: + text.setNum(value.toULongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Double: + text.setNum(value.toDouble(), 'f', 3); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Bool: + so = new QStyleOptionButton(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off) | option.state; + style->drawControl(QStyle::CE_CheckBox, so, painter); + break; + case QVariant::Color: + rect = option.rect;//style->subElementRect(QStyle::QStyle::SE_FrameContents, so); + rect.setRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6); + painter->fillRect(rect, ab); + painter->fillRect(rect, value.value()); + break; + case QVariant::Point: + text = pointString(value.toPoint()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::PointF: + text = pointString(value.toPointF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Rect: + text = rectString(value.toRect()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::RectF: + text = rectString(value.toRectF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::String: default: + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, value.toString()), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, value.toString(), role); + break; + } + } + } + /*so = new QStyleOptionFrame(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionFrame*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off); + style->drawPrimitive(QStyle::PE_PanelLineEdit, so, painter); + style->drawPrimitive(QStyle::PE_FrameLineEdit, so, painter); + break;*/ + if (so != 0) delete so; + if (soc != 0) delete soc; + +} + + +void Delegate::changedFlag() { + QAbstractItemModel * model = const_cast(cmi.model()); + model->setData(cmi, qobject_cast(sender())->isChecked(), Qt::UserRole + 3); + QModelIndex p = cmi.parent(), mi; + int row = 0; + qulonglong val = 0; + QList chldr; + mi = p.child(row, 1); + while (mi.isValid()) { + chldr << mi; + model->setData(mi, !mi.data(Qt::UserRole + 4).toBool(), Qt::UserRole + 4); + mi = p.child(++row, 1); + } + bool cc = cmi.data(Qt::UserRole + 3).toBool(); + qulonglong cv = cmi.data(Qt::UserRole).toULongLong(); + //qDebug() << "*****"; + if (cc && cv == 0) { + val = 0; + //qDebug() << "null" << cv; + } else { + if (!cc && cv != 0) { + //qDebug() << "uncheck" << cv; + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + //qDebug() << (chldr[i].data(Qt::UserRole).toULongLong() & cv); + if (chldr[i].data(Qt::UserRole).toULongLong() & cv) + model->setData(chldr[i], false, Qt::UserRole + 3); + } + } + for (int i = 0; i < chldr.size(); ++i) { + //qDebug() << chldr[i].data(Qt::UserRole + 3).toBool(); + if (chldr[i].data(Qt::UserRole + 3).toBool()) + val |= chldr[i].data(Qt::UserRole).toULongLong(); + } + } + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + cv = chldr[i].data(Qt::UserRole).toULongLong(); + model->setData(chldr[i], ((val & cv) == cv && cv != 0) || (val == 0 && cv == 0), Qt::UserRole + 3); + } + //qDebug() << val; + model->setData(p, val, Qt::UserRole); + model->setData(p.sibling(p.row(), 1), val, Qt::UserRole); +} + + + +PropertyEditor::PropertyEditor(QWidget * parent): QTreeWidget(parent) { + object = 0; + active_ = false; + configTree(); + connect(this, SIGNAL(itemClicked(QTreeWidgetItem * , int)), this, SLOT(itemClicked(QTreeWidgetItem * , int))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem * , int)), this, SLOT(itemChanged(QTreeWidgetItem * , int))); +} + + +PropertyEditor::~PropertyEditor() { + +} + + +void PropertyEditor::changeEvent(QEvent * e) { + QTreeWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + configTree(); + return; + } +} + + +void PropertyEditor::configTree() { + setColumnCount(2); + setRootIsDecorated(false); + setColumnWidth(0, 170); + setColumnWidth(1, 10); + header()->setStretchLastSection(true); + QStringList lbls; + lbls << tr("Property") << tr("Value"); + setHeaderLabels(lbls); + setAlternatingRowColors(true); + setItemDelegateForColumn(1, new Delegate()); + +} + + +void PropertyEditor::itemClicked(QTreeWidgetItem * item, int column) { + if (column == 0) + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + else { + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + editItem(item, 1); + } +} + + +void PropertyEditor::itemChanged(QTreeWidgetItem * item, int column) { + if (!active_) return; + if (column != 1) return; + QVariant value = item->data(1, Qt::UserRole); + if (value.canConvert()) { + value = value.value().second; + } + object->setProperty(item->text(0).toLatin1(), value); +} + + +void PropertyEditor::rebuild() { + clear(); + configTree(); + if (object == 0) return; + active_ = false; + const QMetaObject * mo = object->metaObject(); + QList mol; + while (mo != 0) { + mol.push_front(mo); + mo = mo->superClass(); + } + int ps, pe; + QTreeWidgetItem * ti, * tli, * tfi; + QVariant value; +// QWidget * pw = 0; + int chue = 0; + QColor bc; + font_b = font(); + font_b.setBold(true); + foreach (const QMetaObject * o, mol) { + ps = o->propertyOffset(); + pe = o->propertyCount();// - ps; + //qDebug() << i->className() << ps << pe; + tli = new QTreeWidgetItem(); + tli->setText(0, o->className()); + tli->setFont(0, font_b); + setItemBackColor(tli, Qt::darkGray); + setItemForeColor(tli, Qt::white); + addTopLevelItem(tli); + setFirstItemColumnSpanned(tli, true); + tli->setExpanded(true); + for (int i = ps; i < pe; ++i) { + props << o->property(i); + value = o->property(i).read(object); + ti = new QTreeWidgetItem(); + ti->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + (i % 2) * 20 - 10); + setItemBackColor(ti, bc); + ti->setText(0, o->property(i).name()); + if (props.back().isFlagType()) { + QMetaEnum menum = props.back().enumerator(); + for (int j = 0; j < menum.keyCount(); ++j) { + tfi = new QTreeWidgetItem(); + tfi->setText(0, menum.key(j)); + tfi->setData(1, Qt::UserRole, menum.value(j)); + tfi->setData(1, Qt::UserRole + 1, "__flag"); + tfi->setData(1, Qt::UserRole + 2, value.toULongLong()); + tfi->setData(1, Qt::UserRole + 3, (value.toULongLong() & menum.value(j)) > 0); + tfi->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + ((i + j + 1) % 2) * 20 - 10); + setItemBackColor(tfi, bc); + ti->addChild(tfi); + } + ti->setData(0, Qt::UserRole, value); + ti->setData(1, Qt::UserRole, value); + ti->setData(1, Qt::UserRole + 2, "__flags"); + ti->setData(0, Qt::UserRole + 1, QVariant::fromValue(props.back())); + } + else if (props.back().isEnumType()) + value.setValue(PropertyValuePair(props.back(), value)); + //ti->setText(1, value.toString()); + ti->setData(1, Qt::UserRole, value); + tli->addChild(ti); + //const_cast(indexFromItem(ti, 1)).; + //if (pw != 0) setItemWidget(ti, 1, pw); + } + chue += 60; + chue %= 360; + } + active_ = true; +} + + +void PropertyEditor::refresh() { + +} diff --git a/test/qglview/propertyeditor.h b/test/qglview/propertyeditor.h new file mode 100644 index 0000000..6b26b2c --- /dev/null +++ b/test/qglview/propertyeditor.h @@ -0,0 +1,77 @@ +#ifndef PROPERTYEDITOR_H +#define PROPERTYEDITOR_H + +#include +#include +#include +#include +#include +#include +#include + + +class Delegate: public QStyledItemDelegate { + Q_OBJECT +public: + Delegate(QObject * parent = 0): QStyledItemDelegate() {ab = QBrush(QImage(":/icons/alpha.png"));} + + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {cmi = const_cast(index); return widgetForProperty(parent, index);} + void setEditorData(QWidget * editor, const QModelIndex & index) const {setWidgetProperty(editor, index.data(Qt::UserRole));} + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const {editor->setGeometry(option.rect);} + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; + +private: + QWidget * widgetForProperty(QWidget * parent, const QModelIndex & index) const; + void setWidgetProperty(QWidget * w, const QVariant & value) const; + const QVariant widgetProperty(QWidget * w) const; + QString pointString(const QPoint & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString pointString(const QPointF & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString rectString(const QRect & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + QString rectString(const QRectF & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + + QBrush ab; + mutable QModelIndex cmi; + +private slots: + void changed() {setModelData((QWidget * )sender(), const_cast(cmi.model()), cmi);} + void changedFlag(); + +}; + +typedef QPair PropertyValuePair; +Q_DECLARE_METATYPE (PropertyValuePair) +Q_DECLARE_METATYPE (QMetaProperty) + +class PropertyEditor: public QTreeWidget { + Q_OBJECT +public: + explicit PropertyEditor(QWidget * parent = 0); + virtual ~PropertyEditor(); + + void assignObject(QObject * o) {object = o; rebuild();} + +protected: + void changeEvent(QEvent * e); + +private: + void configTree(); + void setItemBackColor(QTreeWidgetItem * i, const QColor & c) {i->setBackgroundColor(0, c); i->setBackgroundColor(1, c);} + void setItemForeColor(QTreeWidgetItem * i, const QColor & c) {i->setForeground(0, c); i->setForeground(1, c);} + void rebuild(); + void refresh(); + + QObject * object; + QFont font_b; + QList props; + bool active_; + +private slots: + void itemClicked(QTreeWidgetItem * item, int column); + void itemChanged(QTreeWidgetItem * item, int column); + +}; + +#endif // PROPERTYEDITOR_H diff --git a/test/qglview/qglview.cpp b/test/qglview/qglview.cpp new file mode 100644 index 0000000..ac184a8 --- /dev/null +++ b/test/qglview/qglview.cpp @@ -0,0 +1,770 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "qglview.h" + +#include +#include + + +QGLView::QGLView(): OpenGLWindow(), fbo_selection(3) { +// setFrameShape(QFrame::NoFrame); +// setViewportUpdateMode(FullViewportUpdate); +// setCacheMode(CacheNone); +// setMouseTracking(true); +// setFocusPolicy(Qt::WheelFocus); +// setScene(new QGraphicsScene()); +// setInteractive(true); + setIcon(QIcon("://icons/qglview.png")); + timer = 0; + need_init_ = is_first_draw = true; + objects_.is_root = true; + objects_.view_ = this; + backColor_ = Qt::darkGray; + hoverHaloColor_ = QColor(195, 140, 255, 96); + selectionHaloColor_ = QColor(175, 255, 140); + ambientColor_ = QColor(10, 10, 10); + lastPos = QPoint(-1, -1); + lineWidth_ = 1.; + max_anisotropic = 1; + max_texture_chanels = 8; + cameraOrbit_ = lightEnabled_ = true; + shaders_supported = selecting_ = customMouseMove_ = false; + sel_button = Qt::LeftButton; + sel_mod = Qt::NoModifier; + renderer_ = nullptr; + fps_cnt = 0; + fps_tm = fps_ = 0.; + sel_obj = hov_obj = nullptr; + fogDensity_ = fogEnd_ = 1.; + fogStart_ = 0.; + fogMode_ = Exp; + hoverHaloFill_ = 0.333f; + selectionHaloFill_ = 0.5f; + //lmode = Simple; + shader_select = shader_halo = nullptr; + setFeature(qglMSAA, false); + setFeature(qglFXAA, false); + setFeature(qglLinearFiltering, true); + setFeature(qglAnisotropicLevel, 8); + setFeature(qglHDR, false); + setFeature(qglEyeAccomodationEnabled, false); + setFeature(qglEyeAccomodationTime, 16.); + setFeature(qglEyeAccomodationMaxSpeed, 0.2); + setFeature(qglBloomEnabled, false); + setFeature(qglBloomThreshold, 0.9); + setFeature(qglBloomFactor, 1.); + setFeature(qglBloomRadius, 8); + setFeature(qglMotionBlurEnabled, false); + setFeature(qglMotionBlurFactor, 1.); + setFeature(qglMotionBlurSteps, 8); + setFeature(qglShadowsEnabled, false); + setFeature(qglShadowsMapSize, 512); + setFeature(qglShadowsSoftEnabled, true); + setFeature(qglReflectionsEnabled, false); + setFeature(qglReflectionsBlur, true); + setFeature(qglSSAOEnabled, false); + setFeature(qglSSAORadius, 5); + setFeature(qglDepthOfFieldEnabled, false); + setFeature(qglDepthOfFieldAutoFocusEnabled, true); + setFeature(qglDepthOfFieldAutoFocusSpeed, 0.1); + setFeature(qglDepthOfFieldFocus, 1.); + setFeature(qglDepthOfFieldDiaphragm, 8.); + mouse_first = mouseSelect_ = hoverHalo_ = selectionHalo_ = true; + mouseRotate_ = true; + fogEnabled_ = is_init = grabMouse_ = shaders_bind = changed_ = false; + rmode = GLObjectBase::Fill; + sel_mode = QGLView::SingleSelection; +// sel_pen = QPen(Qt::black, 1, Qt::DashLine); +// sel_brush = QBrush(QColor(170, 100, 255, 120)); + camera()->setAim(QVector3D()); + camera()->setPos(QVector3D(2, 2, 2)); + camera()->setName("Camera"); + addObject(camera()); + emit cameraPosChanged(camera()->pos()); + //camera().aim_ = camera().pos_; + ktm_.restart(); +} + + +QGLView::~QGLView() { + stop(); + if (shader_select) delete shader_select; + if (shader_halo) delete shader_halo; +} + + +void QGLView::stop() { + if (timer) killTimer(timer); +} + + +void QGLView::start(float freq) { + timer = startTimer(freq <= 0.f ? 0 : int(1000.f / freq)); +} + + +GLRendererBase * QGLView::renderer() { + return renderer_; +} + + +void QGLView::setRenderer(GLRendererBase * r, GLRendererBase ** prev) { + if (prev != nullptr) *prev = renderer_; + renderer_ = r; +} + + +void QGLView::addObject(GLObjectBase * o) { + objects_.addChild(o); + o->setView(this); + collectLights(); + QList cl = o->children(true); + cl << o; + foreach (GLObjectBase * i, cl) { + emit objectAdded(i); + } + if (is_init) { + o->init(); + } +} + + +int QGLView::objectsCount(bool all) { + if (!all) return objects_.childCount(); + int cnt = 0; + objectsCountInternal(&cnt, &objects_); + return cnt; +} + + +void QGLView::removeObject(GLObjectBase * o, bool inChildren) { + o->setView(nullptr); + if (inChildren) + removeObjectInternal(o, &objects_); + else + objects_.removeChild(o); + objectDeleted(o); +} + + +void QGLView::removeObject(GLObjectBase & o, bool inChildren) { + removeObject(&o, inChildren); +} + + +void QGLView::clearObjects(bool deleteAll) { + removeObject(camera_); + objects_.clearChildren(deleteAll); + addObject(camera()); + selectObject(nullptr); + hov_obj = nullptr; +} + + +QList QGLView::objects(bool all) { + return objects_.children(all); +} + + +int QGLView::lightsCount() const { + return lights_.size(); +} + + +void QGLView::removeLight(int index) { + removeObject(lights_.at(index)); + lights_.removeAt(index); +} + + +void QGLView::removeLight(Light * l) { + foreach (Light * i, lights_) + if (i == l) removeObject(i); + lights_.removeAll(l); +} + + +void QGLView::clearLights(bool deleteAll) { + if (deleteAll) + foreach (Light * i, lights_) delete i; + lights_.clear(); +} + + +void QGLView::addTexture(const QString & path) { + textures_manager->addTexture(path); +} + + +void QGLView::addAnimation(const QString & dir, const QString & name) { + textures_manager->addAnimation(dir, name); +} + + +Light * QGLView::light(int index) { + return lights_[index]; +} + + +Light * QGLView::light(const QString & name) { + foreach (Light * i, lights_) + if (i->name_ == name) return i; + return nullptr; +} + + +void QGLView::selectObject(GLObjectBase * o) { + if (o == sel_obj) return; + GLObjectBase * pso = sel_obj; + sel_obj = o; + emit selectionChanged(sel_obj, pso); +} + + +void QGLView::resizeEvent(QResizeEvent * e) { + renderLater(); +} + +void QGLView::timerEvent(QTimerEvent *) { + renderNow(); + if (ktm_.elapsed() < QApplication::keyboardInputInterval()) return; + Qt::KeyboardModifiers km = QApplication::keyboardModifiers(); + foreach (int i, keys_) + emit keyEvent((Qt::Key)i, km); +} + + +void QGLView::render() { + resizeGL(width(), height()); + QRect g_rect(QPoint(), size()); + emit glBeforePaint(); + //qDebug() << "paintGL"; + //QMutexLocker ml_v(&v_mutex); + glEnable(GL_CULL_FACE); + //glDisable(GL_CULL_FACE); + camera()->apply(aspect); + //objects_.preparePos(camera()); + start_rp.cam_offset_matrix = camera()->offsetMatrix(); + start_rp.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); + start_rp.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + //objects_.buildTransform(); + + /// Selection detect + //glClearFramebuffer(QColor(100, 0, 0, 0)); + if (mouseSelect_) { + glReleaseTextures(); + glEnableDepth(); + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_CUBE_MAP); + glDisable(GL_MULTISAMPLE); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDisable(GL_RESCALE_NORMAL); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + fbo_selection.bind(); + fbo_selection.setWriteBuffer(0); + glClearFramebuffer(QColor(0, 0, 0, 0)); + if (shaders_supported && shader_select->isLinked()) shader_select->bind(); + renderSelection(); + if (shaders_supported && shader_select->isLinked()) shader_select->release(); + uchar cgid[4] = {0, 0, 0, 0}; + uint iid = 0; + GLObjectBase * so = nullptr; + if (!g_rect.contains(lastPos)) { + if (hov_obj != nullptr) { + hov_obj = nullptr; + emit hoverChanged(nullptr, hov_obj); + } + } else { + glReadPixels(lastPos.x(), height() - lastPos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, cgid); + iid = uint(cgid[0] << 24) | uint(cgid[1] << 16) | uint(cgid[2] << 8) | cgid[3]; + so = ids.value(iid, nullptr); + //qDebug() <name() << cgid[3]; + } + if (selectionHalo_ && sel_obj) { + fbo_selection.setWriteBuffer(2); + renderHalo(sel_obj, qHash((quint64)sel_obj), selectionHaloColor_, selectionHaloFill_); + } + if (hoverHalo_ && hov_obj) { + fbo_selection.setWriteBuffer(1); + renderHalo(hov_obj, iid, hoverHaloColor_, hoverHaloFill_); + } + fbo_selection.release(); + glEnableDepth(); + /*glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY);*/ + } + + camera()->apply(aspect); + start_rp.cam_offset_matrix = camera()->offsetMatrix(); + cur_mvpm = start_rp.proj_matrix * start_rp.view_matrix * start_rp.cam_offset_matrix; + //objects_.preparePos(camera()); + + static GLRendererBase * prev_rend = nullptr; + glShadeModel(GL_SMOOTH); + if (prev_rend != renderer_) { + prev_rend = renderer_; + if (renderer_ != nullptr) { + renderer_->init(width(), height()); + renderer_->resize(width(), height()); + renderer_->reloadShaders(); + } + } + emit glBeginPaint(); + if (renderer_ != nullptr) { + renderer_->rp.prepare(); + renderer_->prepareScene(); + renderer_->renderScene(); + } + emit glPainting(); + if (selectionHalo_ || hoverHalo_) { + glReleaseTextures(); + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_CUBE_MAP); + glDisable(GL_LIGHTING); + glDisableDepth(); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + if (selectionHalo_ && sel_obj) { + glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture(2)); + //qDebug() << "draw sel"; + glDrawQuad(); + } + if (hoverHalo_ && hov_obj) { + glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture(1)); + //qDebug() << "draw hover"; + glDrawQuad(); + } + } + + glUseProgram(0); + glResetAllTransforms(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + emit glEndPaint(); + + /*releaseShaders(); + glActiveTextureChannel(0); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.3, 0.5, 0.8, 0.5); + glResetAllTransforms(); + glBegin(GL_QUADS); + glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, -1.f); + glTexCoord2f(1.f, 0.f); glVertex2f(1.f, -1.); + glTexCoord2f(1.f, 1.f); glVertex2f(1.f, 1.f); + glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, 1.f); + glEnd();*/ + /* + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDisable(GL_LIGHTING); + glActiveTextureChannel(0); + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + glDisable(GL_DEPTH_TEST); + glBegin(GL_QUADS); + glColor3f(1.f, 1.f, 1.f); + glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, -1.f); + glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, 1.f); + glTexCoord2f(1.f, 1.f); glVertex2f(1.f, 1.f); + glTexCoord2f(1.f, 0.f); glVertex2f(1.f, -1.); + glEnd(); + glEnable(GL_DEPTH_TEST);*/ + fps_tm += time.elapsed(); + time.restart(); + fps_cnt++; + if (fps_tm < 1000.) return; + fps_ = fps_cnt / fps_tm * 1000.; + fps_tm = 0.; + fps_cnt = 0; +} + + +void QGLView::initialize() { + //initializeOpenGLFunctions(); + glEnable(GL_TEXTURE_MAX_ANISOTROPY_EXT); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glEnableDepth(); + glEnable(GL_CULL_FACE); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); + glActiveTexture(GL_TEXTURE0 + 3); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE0); + glShadeModel(GL_SMOOTH); + glCullFace(GL_BACK); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT, GL_DIFFUSE); + + textures_manager->loadTextures(); + objects_.initInternal(); + checkCaps(); + + shader_select = new QOpenGLShaderProgram(context()); + shader_halo = new QOpenGLShaderProgram(context()); + reloadThisShaders(); + is_init = true; + //resizeGL(width(), height()); + need_init_ = false; + emit glInitializeDone(); +} + + +void QGLView::renderHalo(const GLObjectBase * obj, const uint iid, const QColor & color, const float & fill) { + if (!shaders_supported) return; + if (!shader_halo) return; + if (!shader_halo->isLinked()) return; + if (obj) { + shader_halo->bind(); + shader_halo->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_selection.colorTexture()); + shader_halo->setUniformValue("t0", 0); + shader_halo->setUniformValue("dt", QVector2D(1.f / width(), 1.f / height())); + shader_halo->setUniformValue("selected", QVector4D(float((iid >> 24) & 0xFF) / 255.f, + float((iid >> 16) & 0xFF) / 255.f, + float((iid >> 8) & 0xFF) / 255.f, + float(iid & 0xFF) / 255.f)); + shader_halo->setUniformValue("color", color); + shader_halo->setUniformValue("fill", GLfloat(fill)); + //qDebug() << "render halo" << iid << shader_halo->log() << shader_halo->programId(); + glDisableDepth(); + glDrawQuad(shader_halo); + glDepthMask(GL_TRUE); + //glFlush(); + shader_halo->release(); + } else { + glClearFramebuffer(Qt::black, false); + } +} + + +void QGLView::renderSelection() { +// cid = 1; + ids.clear(); + if (shaders_supported) { + if (shader_select) { + if (shader_select->isLinked()) { + sh_id_loc = shader_select->uniformLocation("id"); + shader_select->setUniformValue("z_far", GLfloat(depthEnd())); + shader_select->setUniformValue("z_near", GLfloat(depthStart())); + } + } + } + //qDebug() << sh_id_loc; + start_rp.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + glPushMatrix(); + renderSingleSelection(objects_); + glPopMatrix(); +} + + +void QGLView::renderSingleSelection(GLObjectBase & o) { + if (!o.isInit()) { + o.init(); + o.loadTextures(); + } + if (!o.visible_ || !o.select_) return; + QMatrix4x4 curview = start_rp.view_matrix * start_rp.cam_offset_matrix * o.itransform_; + uint id = qHash((quint64)&o); + ids.insert(id, &o); + glLineWidth(o.line_width > 0.f ? o.line_width : lineWidth_); + glPointSize(o.line_width > 0.f ? o.line_width : lineWidth_); + if (shaders_supported){ + if (shader_select) { + if (shader_select->isLinked()) { + setUniformMatrices(shader_select, start_rp.proj_matrix, curview); + shader_select->setUniformValue(sh_id_loc, QVector4D(float((id >> 24) & 0xFF) / 255.f, + float((id >> 16) & 0xFF) / 255.f, + float((id >> 8) & 0xFF) / 255.f, + float(id & 0xFF) / 255.f)); + } + } + } else { + setGLMatrix(curview); + glColor4f(float((id >> 24) & 0xFF) / 255.f, + float((id >> 16) & 0xFF) / 255.f, + float((id >> 8) & 0xFF) / 255.f, + float(id & 0xFF) / 255.f); + } + //qDebug() << o.name() << "assign to" << sh_id_loc << cid; + //glColor4f(float((cid >> 24) & 0xFF) / 255.f, float((cid >> 16) & 0xFF) / 255.f, float((cid >> 8) & 0xFF) / 255.f, float(cid & 0xFF) / 255.f); +// ++cid; + o.draw(nullptr, true); + foreach (GLObjectBase * i, o.children_) + renderSingleSelection(*i); +} + + +void QGLView::collectLights() { + lights_.clear(); + collectObjectLights(&objects_); +} + + +void QGLView::objectDeleted(GLObjectBase * o) { + //qDebug() << "del" << o; + if (sel_obj == o) selectObject(nullptr); + if (hov_obj == o) hov_obj = nullptr; +} + + +void QGLView::collectObjectLights(GLObjectBase * o) { + if (o->type_ == GLObjectBase::glLight) { + lights_ << globject_cast(o); + o->view_ = this; + } + foreach (GLObjectBase * i, o->children()) + collectObjectLights(i); +} + + +void QGLView::objectsCountInternal(int * cnt, GLObjectBase * where) { + ++(*cnt); + foreach (GLObjectBase * i, where->children_) + objectsCountInternal(cnt, i); +} + + +void QGLView::removeObjectInternal(GLObjectBase * o, GLObjectBase * where) { + foreach (GLObjectBase * i, where->children_) { + if (o == i) + where->removeChild(i); + else + removeObjectInternal(o, i); + objectDeleted(i); + } +} + + +void QGLView::checkCaps() { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropic); + //glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_chanels); + //qDebug() << max_texture_chanels; + //qDebug() << max_texture_chanels; + shaders_supported = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); +} + + +void QGLView::reloadThisShaders() { + if (!shaders_supported) return; + loadShaders(shader_select, "selection", "://shaders"); + loadShaders(shader_halo, "selection_halo", "://shaders"); + //loadShaders(shader_rope, "rope", "://shaders"); +} + +void QGLView::glReleaseTextures(int channels) { + for (int i = channels - 1; i >= 0; --i) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + } +} + + +void QGLView::applyFog() { + GLfloat fog_col[4] = {float(fogColor_.redF()), float(fogColor_.greenF()), float(fogColor_.blueF()), .0f}; + if (fogEnabled_) { + glEnable(GL_FOG); + glFogf(GL_FOG_DENSITY, fogDensity_); + glFogf(GL_FOG_START, fogStart_); + glFogf(GL_FOG_END, fogEnd_); + glFogi(GL_FOG_MODE, fogMode_); + fog_col[0] = fogColor_.redF(); + fog_col[1] = fogColor_.greenF(); + fog_col[2] = fogColor_.blueF(); + glFogfv(GL_FOG_COLOR, fog_col); + } else glDisable(GL_FOG); +} + + +void QGLView::resizeGL(int width, int height) { + if (!is_init) return; + if (width <= 0 || height <= 0) return; + if (prev_size == QSize(width, height)) return; + prev_size = QSize(width, height); + aspect = float(width) / float(height); + if (renderer_) renderer_->resize(width, height); + mouse_first = true; + //qDebug() << "resize" << width << height; + fbo_selection.resize(width, height); + iaspect = (aspect == 0.f) ? 0. : 1 / aspect; + glViewport(0, 0, width, height); + emit glResize(width, height); +} + + +void QGLView::mouseReleaseEvent(QMouseEvent * e) { +// qDebug() << "mouseReleaseEvent" << e << isActive(); +// QGraphicsView::mouseReleaseEvent(e); + //setCursor(QCursor(Qt::ArrowCursor)); + selecting_ = false; + if (mouseSelect_ && e->button() == Qt::LeftButton) { + if ((lastPos - downPos).manhattanLength() < 8) { + if (sel_obj != hov_obj) + selectObject(hov_obj); + } + } + emit glMouseReleaseEvent(e); +} + + +void QGLView::mousePressEvent(QMouseEvent * e) { +// qDebug() << "mousePressEvent" << e << isActive(); +// QGraphicsView::mousePressEvent(e); +// mouseThis_ = (scene()->itemAt(mapToScene(e->pos()) , QTransform() ) == 0); + selecting_ = false; + if (!QRect(QPoint(), size()).contains(e->pos())) return; + /// TODO select by rect + //if (e->button() == sel_button && e->modifiers() == sel_mod) + // selecting_ = true; + lastPos = e->pos(); + downPos = lastPos; + //qDebug() << mouseThis_; + emit glMousePressEvent(e); +} + + +void QGLView::mouseMoveEvent(QMouseEvent * e) { +// qDebug() << "mouseMoveEvent" << e << isActive(); +// QGraphicsView::mouseMoveEvent(e); + //lastPos = e->pos(); + if (selecting_) { + return; + } +// if (!QRect(QPoint(), size()).contains(e->pos())) return; + //if (scene()->itemAt(mapToScene(e->pos())) != 0) return; + ///qDebug() << e->x() << e->y(); + QRect g_rect(QPoint(), size()); + if (mouseRotate_) { + float dx = e->x() - lastPos.x(); + float dy = e->y() - lastPos.y(); + if (e->buttons() & Qt::LeftButton) { + //camera().angle_z += dx / 4.; + //camera().angle_xy += dy / 4.; + if (cameraOrbit_) { + camera()->orbitZ(dx / 4.f); + camera()->orbitXY(dy / 4.f); + } else { + camera()->rotateZ(dx / 4.f); + camera()->rotateXY(dy / 4.f); + } + emit cameraPosChanged(camera()->pos()); + } else if (e->buttons() & Qt::RightButton) { + float ad = camera()->distance(); + camera()->moveLeft(dx / 1000.f * ad); + camera()->moveUp(dy / 1000.f * ad); + //camera().pos.setX(camera().pos.x() + camera().pos.z() * dx / 500.); + //camera().pos.setY(camera().pos.y() - camera().pos.z() * dy / 500.); + emit cameraPosChanged(camera()->pos()); + } + //lights[0]->pos_ = camera().pos(); + } + if (customMouseMove_) emit customMouseMoveEvent(e->pos(), lastPos, e->buttons()); + lastPos = e->pos(); + if (grabMouse_) { + //if (!isrunning) return; + QCursor::setPos(mapToGlobal(QRect(QPoint(), size()).center())); + static bool mouse_sec = false; + if (mouse_sec) { + mouse_sec = false; + return; + } + if (mouse_first) { + mouse_first = false; + mouse_sec = true; + //qDebug() << "first" << e->pos(); + return; + } + lastPos = g_rect.center(); + int dx = e->x() - lastPos.x(); + int dy = e->y() - lastPos.y(); + emit glMouseMoveEvent(new QMouseEvent(QEvent::MouseMove, QPoint(dx, dy), e->button(), e->buttons(), e->modifiers())); + return; + } + emit glMouseMoveEvent(e); +} + + +void QGLView::wheelEvent(QWheelEvent * e) { + if (mouseRotate_) { + if (e->delta() > 0) camera()->flyCloser(0.1f); //camera().pos.setZ(camera().pos.z() - 0.1 * camera().pos.z()); + if (e->delta() < 0) camera()->flyFarer(0.1f); //camera().pos.setZ(camera().pos.z() + 0.1 * camera().pos.z()); + emit cameraPosChanged(camera()->pos()); + } + emit glWheelEvent(e); +} + + +void QGLView::leaveEvent(QEvent * ) { + lastPos = QPoint(-1, -1); + //qDebug() << lastPos; +} + + +void QGLView::keyPressEvent(QKeyEvent * e) { + emit glKeyPressEvent(e); + if (e->key() > 0) keys_.insert(e->key()); + if (e->key() == Qt::Key_F11) { + emit doubleClick(); + } +} + + +void QGLView::keyReleaseEvent(QKeyEvent * e) { + emit glKeyReleaseEvent(e); + keys_.remove(e->key()); +} + + +void QGLView::focusOutEvent(QFocusEvent *) { + keys_.clear(); +} + + +void QGLView::mouseDoubleClickEvent(QMouseEvent * e) { + if (e->buttons().testFlag(Qt::MidButton)) + emit doubleClick(); +} diff --git a/test/qglview/qglview.h b/test/qglview/qglview.h new file mode 100644 index 0000000..845e776 --- /dev/null +++ b/test/qglview/qglview.h @@ -0,0 +1,296 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef QGLVIEW_H +#define QGLVIEW_H + +#include "openglwindow.h" +#include "glframebuffer.h" +#include "glprimitives.h" +#include "glparticles_system.h" +#include "glrendererbase.h" +#include + + +class QGLView: public OpenGLWindow, public QGLViewBase +{ + friend class GLRendererBase; + friend class GLObjectBase; + Q_OBJECT + Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY (float lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY (float FOV READ FOV WRITE setFOV) + Q_PROPERTY (float depthStart READ depthStart WRITE setDepthStart) + Q_PROPERTY (float depthEnd READ depthEnd WRITE setDepthEnd) + Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor) + Q_PROPERTY (QColor fogColor READ fogColor WRITE setFogColor) + Q_PROPERTY (float fogDensity READ fogDensity WRITE setFogDensity) + Q_PROPERTY (float fogStart READ fogStart WRITE setFogStart) + Q_PROPERTY (float fogEnd READ fogEnd WRITE setFogEnd) + Q_PROPERTY (FogMode fogMode READ fogMode WRITE setFogMode) + Q_PROPERTY (bool fogEnabled READ isFogEnabled WRITE setFogEnabled) + Q_PROPERTY (int renderMode READ renderMode WRITE setRenderMode) + Q_PROPERTY (bool grabMouse READ isGrabMouseEnabled WRITE setGrabMouseEnabled) + Q_PROPERTY (bool mouseRotate READ isMouseRotateEnabled WRITE setMouseRotateEnabled) + Q_PROPERTY (bool mouseSelection READ isMouseSelectionEnabled WRITE setMouseSelectionEnabled) + Q_PROPERTY (bool cameraOrbit READ isCameraOrbit WRITE setCameraOrbit) + Q_PROPERTY (bool hoverHalo READ isHoverHaloEnabled WRITE setHoverHaloEnabled) + Q_PROPERTY (QColor hoverHaloColor READ hoverHaloColor WRITE setHoverHaloColor) + Q_PROPERTY (float hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha) + Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled) + Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor) + Q_PROPERTY (float selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha) + Q_PROPERTY (Qt::MouseButton selectionButton READ selectionButton WRITE setSelectionButton) + Q_PROPERTY (Qt::KeyboardModifier selectionModifier READ selectionModifier WRITE setSelectionModifier) + Q_PROPERTY (SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + +public: + QGLView(); + virtual ~QGLView(); + + enum FogMode {Linear = GL_LINEAR, Exp = GL_EXP, Exp2 = GL_EXP2}; + enum SelectionMode {NoSelection, SingleSelection, MultiSelection}; + enum Feature { + qglMSAA, + qglFXAA, + qglLinearFiltering, + qglAnisotropicLevel, + qglHDR, + qglEyeAccomodationEnabled, + qglEyeAccomodationTime, + qglEyeAccomodationMaxSpeed, + qglBloomEnabled, + qglBloomThreshold, + qglBloomFactor, + qglBloomRadius, + qglMotionBlurEnabled, + qglMotionBlurFactor, + qglMotionBlurSteps, + qglShadowsEnabled, + qglShadowsMapSize, + qglShadowsSoftEnabled, + qglReflectionsEnabled, + qglReflectionsBlur, + qglSSAOEnabled, + qglSSAORadius, + qglDepthOfFieldEnabled, + qglDepthOfFieldAutoFocusEnabled, + qglDepthOfFieldAutoFocusSpeed, + qglDepthOfFieldFocus, + qglDepthOfFieldDiaphragm + }; + + Q_ENUMS (FogMode) + Q_ENUMS (SelectionMode) + + void stop(); + void start(float freq = 60.); + + GLRendererBase * renderer(); + void setRenderer(GLRendererBase * r, GLRendererBase ** prev = nullptr); + + QColor backColor() const {return backColor_;} + float lineWidth() const {return lineWidth_;} + float FOV() const {return camera()->fov_;} + float depthStart() const {return camera()->depth_start;} + float depthEnd() const {return camera()->depth_end;} + float currentFPS() const {return fps_;} + int maxAnisotropicLevel() const {return max_anisotropic;} + + QColor ambientColor() const {return ambientColor_;} + QColor fogColor() const {return fogColor_;} + float fogDensity() const {return fogDensity_;} + float fogStart() const {return fogStart_;} + float fogEnd() const {return fogEnd_;} + FogMode fogMode() const {return fogMode_;} + bool isFogEnabled() const {return fogEnabled_;} + bool isLightEnabled() const {return lightEnabled_;} + bool isGrabMouseEnabled() const {return grabMouse_;} + bool isMouseRotateEnabled() const {return mouseRotate_;} + bool isMouseSelectionEnabled() const {return mouseSelect_;} + bool isCameraOrbit() const {return cameraOrbit_;} + bool isHoverHaloEnabled() const {return hoverHalo_;} + QColor hoverHaloColor() const {return hoverHaloColor_;} + float hoverHaloFillAlpha() const {return hoverHaloFill_;} + bool isSelectionHaloEnabled() const {return selectionHalo_;} + QColor selectionHaloColor() const {return selectionHaloColor_;} + float selectionHaloFillAlpha() const {return selectionHaloFill_;} + + QVariant feature(Feature f) const {return features_.value(int(f));} + QVariant setFeature(Feature f, const QVariant & value) {QVariant ret = features_.value(int(f)); features_[int(f)] = value; return ret;} + bool isFeatureEnabled(Feature f) const {return features_[int(f)].toBool();} + + int renderMode() const {return (int)rmode;} + void setRenderMode(int mode) {rmode = (GLObjectBase::RenderMode)mode;} + + void addObject(GLObjectBase * o); +// void addObject(GLObjectBase & o) {addObject(&o);} + int objectsCount(bool all = false); + void removeObject(GLObjectBase * o, bool inChildren = true); + void removeObject(GLObjectBase & o, bool inChildren = true); + void clearObjects(bool deleteAll = false); + QList objects(bool all = false); + + int lightsCount() const; + void removeLight(int index); + void removeLight(Light * l); + void clearLights(bool deleteAll = false); + QList lights() {return lights_;} + + void addTexture(const QString & path); + void addAnimation(const QString & dir, const QString & name); + + const GLObjectBase & rootObject() {return objects_;} + GLObjectBase * object(int index) {return objects_.child(index);} + GLObjectBase * object(const QString & name) {return objects_.child(name);} + Light * light(int index); + Light * light(const QString & name); + + SelectionMode selectionMode() const {return sel_mode;} + Qt::MouseButton selectionButton() const {return sel_button;} + Qt::KeyboardModifier selectionModifier() const {return sel_mod;} + + void setSelectionMode(SelectionMode v) {sel_mode = v;} + void setSelectionButton(Qt::MouseButton v) {sel_button = v;} + void setSelectionModifier(Qt::KeyboardModifier v) {sel_mod = v;} + void selectObject(GLObjectBase * o); + GLObjectBase * selectedObject() const {return sel_obj;} + + void glReleaseTextures(int channels = 8); + + GLfloat aspect, iaspect; + QMatrix4x4 cur_mvpm; + +protected: + void render(); + void resizeEvent(QResizeEvent * e); + + void timerEvent(QTimerEvent * ); + void initialize(); + void resizeGL(int width, int height); + void mousePressEvent(QMouseEvent * e); + void mouseMoveEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent * e); + void wheelEvent(QWheelEvent * e); + void leaveEvent(QEvent * ); + void mouseDoubleClickEvent(QMouseEvent * e); + + void keyPressEvent(QKeyEvent * e); + void keyReleaseEvent(QKeyEvent * e); + void focusOutEvent(QFocusEvent *); + + void applyFog(); + void renderSelection(); + + void checkCaps(); + void collectLights(); + +private: + void objectDeleted(GLObjectBase * o); + void collectObjectLights(GLObjectBase * o); + void objectsCountInternal(int * cnt, GLObjectBase * where); + void removeObjectInternal(GLObjectBase * o, GLObjectBase * where); + void renderSingleSelection(GLObjectBase & o); + //void renderSingleShadow(GLObjectBase & o); + void renderHalo(const GLObjectBase * obj, const uint iid, const QColor & color, const float & fill); + void reloadThisShaders(); + void processKeys(); + bool setupViewport(); + + QPoint lastPos, downPos; + GLObjectBase objects_; + QList lights_; +// uint cid; + QHash ids; + QSet keys_; + FogMode fogMode_; + QColor backColor_, fogColor_, ambientColor_, hoverHaloColor_, selectionHaloColor_; + QTime time, ktm_; + GLint max_anisotropic, max_texture_chanels; + GLObjectBase::RenderMode rmode; + GLObjectBase * sel_obj, * hov_obj; + GLFramebuffer fbo_selection; + QOpenGLShaderProgram * shader_select, * shader_halo; + GLRendererBase * renderer_; + SelectionMode sel_mode; + Qt::MouseButton sel_button; + Qt::KeyboardModifier sel_mod; + GLRendererBase::RenderingParameters start_rp; + QHash features_; + QSize prev_size; + float lineWidth_; + float fogDensity_, fogStart_, fogEnd_, fps_, fps_tm, hoverHaloFill_, selectionHaloFill_, m_motionBlurFactor; + int timer, fps_cnt, sh_id_loc; + bool is_first_draw, is_init, fogEnabled_, lightEnabled_, grabMouse_, mouse_first, mouseRotate_, mouseSelect_, customMouseMove_; + bool shaders_supported, changed_, cameraOrbit_, need_init_; + bool hoverHalo_, selectionHalo_, shaders_bind, selecting_; + +public slots: + void setBackColor(const QColor & arg) {backColor_ = arg;} + void setLineWidth(const float & arg) {lineWidth_ = arg;} + void setFOV(const float & arg) {camera()->fov_ = arg;} + void setDepthStart(const float & arg) {camera()->depth_start = arg;} + void setDepthEnd(const float & arg) {camera()->depth_end = arg;} + void setAmbientColor(const QColor & arg) {ambientColor_ = arg;} + void setFogColor(const QColor & arg) {fogColor_ = arg;} + void setFogDensity(const float & arg) {fogDensity_ = arg;} + void setFogStart(const float & arg) {fogStart_ = arg;} + void setFogEnd(const float & arg) {fogEnd_ = arg;} + void setFogMode(const FogMode & arg) {fogMode_ = arg;} + void setFogEnabled(const bool & arg) {fogEnabled_ = arg;} + void setLightEnabled(const bool & arg) {lightEnabled_ = arg;} + void setGrabMouseEnabled(const bool & arg) {grabMouse_ = arg; mouse_first = true;} + void setMouseRotateEnabled(const bool & arg) {mouseRotate_ = arg;} + void setMouseSelectionEnabled(const bool & arg) {mouseSelect_ = arg;} + void setCustomMouseMove(const bool & arg) {customMouseMove_ = arg;} + void setCameraOrbit(const bool & arg) {cameraOrbit_ = arg;} + void setHoverHaloEnabled(const bool & arg) {hoverHalo_ = arg;} + void setHoverHaloColor(const QColor & arg) {hoverHaloColor_ = arg;} + void setHoverHaloFillAlpha(const float & arg) {hoverHaloFill_ = arg;} + void setSelectionHaloEnabled(const bool & arg) {selectionHalo_ = arg;} + void setSelectionHaloColor(const QColor & arg) {selectionHaloColor_ = arg;} + void setSelectionHaloFillAlpha(const float & arg) {selectionHaloFill_ = arg;} + + void reloadShaders() {if (renderer_ != nullptr) renderer_->reloadShaders(); reloadThisShaders();} + void deselect() {sel_obj = nullptr;} + +signals: + void glBeforePaint(); + void glBeginPaint(); + void glPainting(); + void glEndPaint(); + void glKeyPressEvent(QKeyEvent * e); + void glKeyReleaseEvent(QKeyEvent * e); + void glMousePressEvent(QMouseEvent * e); + void glMouseMoveEvent(QMouseEvent * e); + void glMouseReleaseEvent(QMouseEvent * e); + void glWheelEvent(QWheelEvent * e); + void glResize(int, int); + void glInitializeDone(); + void cameraPosChanged(QVector3D pos); + void keyEvent(Qt::Key key, Qt::KeyboardModifiers mod); + void customMouseMoveEvent(QPoint curpos, QPoint lastpos, Qt::MouseButtons buttons); + + void hoverChanged(GLObjectBase * cur, GLObjectBase * prev); + void selectionChanged(GLObjectBase * cur, GLObjectBase * prev); + void objectAdded(GLObjectBase * ); + void doubleClick(); + +}; + +#endif // QGLVIEW_H diff --git a/test/qglview/qglview.qrc b/test/qglview/qglview.qrc new file mode 100644 index 0000000..c50a430 --- /dev/null +++ b/test/qglview/qglview.qrc @@ -0,0 +1,76 @@ + + + icons/document-save-all.png + icons/document-new.png + icons/document-import.png + icons/dialog-close.png + icons/edit-clear.png + icons/edit-guides.png + icons/type-camera.png + icons/type-geo.png + icons/type-light.png + icons/view-grid.png + icons/zoom-fit-best.png + icons/configure.png + icons/document-save.png + icons/edit-clear-locationbar-rtl.png + icons/edit-find.png + icons/list-add.png + icons/edit-delete.png + icons/item.png + icons/node-add.png + icons/application-exit.png + icons/document-open.png + icons/document-save-.png + icons/node.png + icons/edit-copy.png + icons/edit-paste.png + icons/qglview.png + shaders/bloom_0.frag + shaders/bloom_1.frag + shaders/bloom_pass_0.frag + shaders/bloom_pass_0.vert + shaders/bloom_pass_1.frag + shaders/bloom_pass_1.vert + shaders/downscale.frag + shaders/downscale.vert + shaders/dsl_pass_0.frag + shaders/dsl_pass_0.vert + shaders/dsl_pass_1.frag + shaders/dsl_pass_1.vert + shaders/dsl_pass_2.frag + shaders/dsl_pass_2.vert + shaders/fbo_add.frag + shaders/fbo_add.vert + shaders/FXAA.frag + shaders/FXAA.vert + shaders/hdr.frag + shaders/hdr.vert + shaders/hdr_scale_0.frag + shaders/hdr_scale_1.frag + shaders/light_models.frag + shaders/motion_blur.frag + shaders/motion_blur.vert + shaders/post.frag + shaders/ppl.frag + shaders/ppl.vert + shaders/selection.frag + shaders/selection.vert + shaders/selection_halo.frag + shaders/selection_halo.vert + shaders/shadow.frag + shaders/shadow.vert + shaders/ssao_blur.frag + shaders/ssao_blur.vert + shaders/ssao_merge.frag + shaders/ssao_merge.vert + shaders/ssr.frag + shaders/ssr.vert + shaders/ssr_blur.frag + shaders/ssr_blur.vert + shaders/ssr_merge.frag + shaders/ssr_merge.vert + shaders/dof.frag + shaders/dof.vert + + diff --git a/test/qglview/qglview_test/main.cpp b/test/qglview/qglview_test/main.cpp new file mode 100644 index 0000000..3560ee7 --- /dev/null +++ b/test/qglview/qglview_test/main.cpp @@ -0,0 +1,34 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include "qglview_window.h" + +int main(int argc, char ** argv) { + QApplication a(argc, argv); + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + QGLViewWindow w; + w.show(); + QStringList al(a.arguments()); + al.pop_front(); + foreach (QString s, al) + w.loadFile(s); + return a.exec(); +} diff --git a/test/qglview/qglview_test/qglview_window.cpp b/test/qglview/qglview_test/qglview_window.cpp new file mode 100644 index 0000000..b871b9b --- /dev/null +++ b/test/qglview/qglview_test/qglview_window.cpp @@ -0,0 +1,338 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "qglview_window.h" +#include "loader_qgl.h" +#include "loader_ase.h" +#include "loader_3ds.h" +#include "loader_obj.h" +#include "loader_dae.h" +#include +#include +#include + + +QGLViewWindow::QGLViewWindow(QWidget * parent): QMainWindow(parent), Ui::QGLViewWindow() { + setupUi(this); + session.setFile("session_qglview_test.conf"); + session.addEntry(this); + icon_geo = QIcon(":/icons/type-geo.png"); + icon_camera = QIcon(":/icons/type-camera.png"); + icon_light = QIcon(":/icons/type-light.png"); + + sel_obj = nullptr; + view->view()->camera()->setAim(QVector3D()); + view->view()->camera()->setPos(QVector3D(2, 2, 2)); +// view->setFrameShape(QFrame::NoFrame); + view->view()->setRenderer(new RendererSimple(view->view())); + view->view()->setMouseRotateEnabled(true); + view->view()->setMouseSelectionEnabled(true); + view->view()->setSelectionHaloEnabled(true); + view->view()->setHoverHaloEnabled(true); + view->view()->setHoverHaloFillAlpha(0.); + view->view()->setSelectionHaloFillAlpha(0.); + view->view()->setBackColor(Qt::lightGray); + view->view()->setDepthStart(0.001); + view->view()->setDepthEnd(100000.); + + spinFOV->setValue(view->view()->FOV()); + spinDepthStart->setValue(view->view()->depthStart()); + spinDepthEnd->setValue(view->view()->depthEnd()); + groupHoverHalo->setChecked(view->view()->isHoverHaloEnabled()); + groupSelectionHalo->setChecked(view->view()->isSelectionHaloEnabled()); + spinHoverHaloFill->setValue(view->view()->hoverHaloFillAlpha()); + spinSelectionHaloFill->setValue(view->view()->selectionHaloFillAlpha()); + colorHoverHalo->setColor(view->view()->hoverHaloColor()); + colorSelectionHalo->setColor(view->view()->selectionHaloColor()); + checkFXAA->setChecked(view->view()->isFeatureEnabled(QGLView::qglFXAA)); + checkMSAA->setChecked(view->view()->isFeatureEnabled(QGLView::qglMSAA)); + colorBack->setColor(view->view()->backColor()); + colorAmbient->setColor(view->view()->ambientColor()); + checkCameraOrbit->setChecked(view->view()->isCameraOrbit()); + + groupShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsEnabled)); + groupEyeAccomodation->setChecked(view->view()->isFeatureEnabled(QGLView::qglEyeAccomodationEnabled)); + groupBloom->setChecked(view->view()->isFeatureEnabled(QGLView::qglBloomEnabled)); + groupMotionBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglMotionBlurEnabled)); + groupReflections->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsEnabled)); + checkSoftShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsSoftEnabled)); + groupSSAO->setChecked(view->view()->isFeatureEnabled(QGLView::qglSSAOEnabled)); + spinAccom->setValue(view->view()->feature(QGLView::qglEyeAccomodationTime).toDouble()); + spinAccomMS->setValue(view->view()->feature(QGLView::qglEyeAccomodationMaxSpeed).toDouble()); + checkReflectionsBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsBlur)); + spinShadowmapSize->setValue(view->view()->feature(QGLView::qglShadowsMapSize).toInt()); + spinMotionBlurFactor->setValue(view->view()->feature(QGLView::qglMotionBlurFactor).toDouble()); + spinMotionBlurSteps->setValue(view->view()->feature(QGLView::qglMotionBlurSteps).toInt()); + spinBloomFactor->setValue(view->view()->feature(QGLView::qglBloomFactor).toDouble()); + spinBloomRadius->setValue(view->view()->feature(QGLView::qglBloomRadius).toInt()); + spinBloomThreshold->setValue(view->view()->feature(QGLView::qglBloomThreshold).toDouble()); + spinSSAORadius->setValue(view->view()->feature(QGLView::qglSSAORadius).toInt()); + groupDOF->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldEnabled)); + checkDOFAutoFocus->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldAutoFocusEnabled)); + spinDOFFocus->setValue(view->view()->feature(QGLView::qglDepthOfFieldFocus).toDouble()); + spinDOFDiaphragm->setValue(view->view()->feature(QGLView::qglDepthOfFieldDiaphragm).toDouble()); + spinDOFSpeed->setValue(view->view()->feature(QGLView::qglDepthOfFieldAutoFocusSpeed).toDouble()); + + axis = new GLObjectBase(); + GLObjectBase * obj; + float al = 1.; + obj = new GLPrimitiveLine(QVector3D(0, 0, -al), QVector3D(0, 0, al)); + obj->material().color_diffuse = Qt::darkBlue; obj->setAcceptLight(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(-al, 0, 0), QVector3D(al, 0, 0)); + obj->material().color_diffuse = Qt::darkRed; obj->setAcceptLight(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(0, -al, 0), QVector3D(0, al, 0)); + obj->material().color_diffuse = Qt::darkGreen; obj->setAcceptLight(false); + axis->addChild(obj); + view->view()->addObject(axis); + + cam_light = new Light(); + cam_light->intensity = 0.5; + cam_light->setName("Camera_Light"); + view->view()->camera()->addChild(cam_light); + view->view()->start(-1); + startTimer(1000/60); + + connect(view->view(), SIGNAL(selectionChanged(GLObjectBase*,GLObjectBase*)), this, SLOT(selectionChanged(GLObjectBase*,GLObjectBase*))); + connect(view->view(), SIGNAL(keyEvent(Qt::Key, Qt::KeyboardModifiers)), this, SLOT(view_keyEvent(Qt::Key, Qt::KeyboardModifiers))); + connect(matEditor, SIGNAL(changed()), this, SLOT(materialChanged())); + + //view->view()->addObject(&partsys); + partsys.material().color_diffuse = Qt::red; + treeProps->assignObject(&partsys); + session.load(); +} + + +QGLViewWindow::~QGLViewWindow() { + session.save(); + //delete ps; +} + + +void QGLViewWindow::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + retranslateUi(this); + return; + } +} + + +void QGLViewWindow::timerEvent(QTimerEvent * ) { + //static double t = 0.; + //cam_light->intensity = checkCameraLight->isChecked() ? 0.5 : 0.; + cam_light->setVisible(checkCameraLight->isChecked()); + //((RendererSimple*)(view->view()->renderer()))->mpos = view->view()->mapFromGlobal(QCursor::pos()); + statusBar()->showMessage(QString("FPS: %1").arg(QString::number(view->view()->currentFPS(), 'f', 2))); +} + + +void QGLViewWindow::loadFile(const QString & path) { + prev_path = path; + importFile(path); +} + + +void QGLViewWindow::importFile(const QString & path) { + QApplication::setOverrideCursor(Qt::WaitCursor); + QFileInfo fi(path); + GLObjectBase * o = nullptr; + if (fi.suffix().toLower() == "qgl") o = loadFromQGLFile(path); + if (fi.suffix().toLower() == "ase") o = loadFromASEFile(path); + if (fi.suffix().toLower() == "3ds") o = loadFrom3DSFile(path); + if (fi.suffix().toLower() == "obj") o = loadFromOBJFile(path); + if (fi.suffix().toLower() == "dae") o = loadFromDAEFile(path); + QApplication::restoreOverrideCursor(); + if (!o) { + QMessageBox::critical(this, "Import", "Can`t load " + path + "!"); + return; + } + o->setName(fi.baseName()); + view->view()->addObject(o); + objectsTreeChanged(); +} + + +void QGLViewWindow::makeObjetTree(const GLObjectBase * o, QTreeWidgetItem * ti) { + if (o == axis) return; + for (int i = 0; i < o->childCount(); ++i) { + const GLObjectBase * co = o->child(i); + QTreeWidgetItem * ci = new QTreeWidgetItem(ti); + ci->setText(0, co->name()); + ci->setData(0, Qt::UserRole, quintptr(co)); + switch (co->type()) { + case GLObjectBase::glMesh: ci->setIcon(0, icon_geo); break; + case GLObjectBase::glLight: ci->setIcon(0, icon_light); break; + case GLObjectBase::glCamera: ci->setIcon(0, icon_camera); break; + default: break; + } + makeObjetTree(co, ci); + } +} + + +void QGLViewWindow::selectionChanged(GLObjectBase * cur, GLObjectBase *) { + sel_obj = cur; + //qDebug() << "selected" << (cur != 0 ? cur->name() : "0"); + labelName->setText(cur != nullptr ? cur->name() : ""); + /**if (cur == 0) box->hide(); + else { + box->setScale(cur->boundingBox().size()); + box->setPos(cur->boundingBox().pos()); + Box3D b = cur->boundingBox().movedTo(-cur->boundingBox().center()); + b.z = -b.z - b.height; + ps->setEmitterRect(b); + cur->addChild(box); + box->show(); + }*/ + objectEditor->setObject(sel_obj); + if (sel_obj == nullptr) return; + matEditor->setMaterial(sel_obj->material()); + //qDebug() << sel_obj->boundingBox(); +} + + +void QGLViewWindow::materialChanged() { + if (sel_obj == nullptr) return; + sel_obj->setMaterial(matEditor->material()); +} + + +void QGLViewWindow::on_comboRenderer_currentIndexChanged(int val) { + GLRendererBase * pr = nullptr; + switch (val) { + case 0: view->view()->setRenderer(new RendererSimple(view->view()), &pr); break; + case 1: view->view()->setRenderer(new RendererDeferredShading(view->view()), &pr); break; + //case 2: view->view()->setRenderer(new RendererRT(view), &pr); break; + } + if (pr != nullptr) delete pr; +} + + +void QGLViewWindow::on_actionReset_triggered() { + view->view()->removeObject(axis, false); + view->view()->clearObjects(true); + view->view()->addObject(axis); + objectsTreeChanged(); +} + + +void QGLViewWindow::on_actionImport_triggered() { + QStringList fl = QFileDialog::getOpenFileNames(this, "Select files", prev_path, "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;" + "QGLView(*.qgl);;" + "Ascii Scene Export(*.ase);;" + "3D Studio(*.3ds);;" + "Wavefront OBJ(*.obj);;" + "Collada(*.dae)"); + if (fl.isEmpty()) return; + prev_path = fl.back(); + foreach (QString f, fl) + importFile(f); +} + + +void QGLViewWindow::on_actionSave_triggered() { + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + view->view()->removeObject(axis); + QApplication::setOverrideCursor(Qt::WaitCursor); + saveToQGLFile(f, &(view->view()->rootObject())); + QApplication::restoreOverrideCursor(); + view->view()->addObject(axis); +} + + +void QGLViewWindow::on_actionSaveSelected_triggered() { + if (!sel_obj) return; + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + QApplication::setOverrideCursor(Qt::WaitCursor); + saveToQGLFile(f, sel_obj); + QApplication::restoreOverrideCursor(); +} + + +void QGLViewWindow::on_actionOpen_triggered() { + QString f = QFileDialog::getOpenFileName(this, "Select file", prev_path, "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;" + "QGLView(*.qgl);;" + "Ascii Scene Export(*.ase);;" + "3D Studio(*.3ds);;" + "Wavefront OBJ(*.obj);;" + "Collada(*.dae)"); + if (f.isEmpty()) return; + prev_path = f; + importFile(f); +} + + +void QGLViewWindow::view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m) { + //qDebug() << k; + double spd = 0.2; + if (m.testFlag(Qt::ShiftModifier)) + spd = 0.5; + switch (k) { + case Qt::Key_W: view->view()->camera()->moveForward(spd); break; + case Qt::Key_S: view->view()->camera()->moveBackward(spd); break; + case Qt::Key_A: view->view()->camera()->moveLeft(spd); break; + case Qt::Key_D: view->view()->camera()->moveRight(spd); break; + default: break; + } +} + + +void QGLViewWindow::on_treeObjects_itemClicked(QTreeWidgetItem * ti, int) { + ((GLObjectBase*)(ti->data(0, Qt::UserRole).toULongLong()))->select(); + //qDebug() << ((GLObjectBase*)(ti->data(0, Qt::UserRole).toULongLong()))->type(); + if (sel_obj->type() == GLObjectBase::glCamera) + view->view()->setCamera((Camera*)sel_obj); +} + + +void QGLViewWindow::objectsTreeChanged() { + treeObjects->clear(); + makeObjetTree(&(view->view()->rootObject()), treeObjects->invisibleRootItem()); + treeObjects->expandAll(); +} + + +void QGLViewWindow::on_pushButton_clicked() { + //view->view()->removeLight(view->view()->lightsCount() - 1); + //setWindowTitle(QString::number(view->view()->lightsCount())); + QVector3D wp = view->view()->light(0)->worldPos(); + view->view()->camera()->setPos(wp); + view->view()->camera()->setAim(wp + (view->view()->light(0)->worldTransform() * QVector4D(view->view()->light(0)->direction)).toVector3D()*100); +} + + +void QGLViewWindow::on_pushButton_3_clicked() { + QList ol = view->view()->objects(true); + qDebug() << ol.size(); + foreach (GLObjectBase * i, ol) { + i->VBO().rebuffer(); + } +} + + diff --git a/test/qglview/qglview_test/qglview_window.h b/test/qglview/qglview_test/qglview_window.h new file mode 100644 index 0000000..2486417 --- /dev/null +++ b/test/qglview/qglview_test/qglview_window.h @@ -0,0 +1,144 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef QGLVIEWWINDOW_H +#define QGLVIEWWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_qglview_window.h" +#include "loader_3ds.h" +#include "loader_ase.h" +#include "renderer_simple.h" +#include "renderer_deferred_shading.h" +#include "session_manager.h" +//#include "renderer_rt.h" +#include "glparticles_system.h" +#include "qglview.h" +#include "ui_qglview_window.h" + + +class QGLViewWindow: public QMainWindow, public Ui::QGLViewWindow +{ + Q_OBJECT +public: + QGLViewWindow(QWidget * parent = 0); + ~QGLViewWindow(); + + void loadFile(const QString & path); + +private: + // Qt`s overloaded + void changeEvent(QEvent * e); + void timerEvent(QTimerEvent * ); + + void importFile(const QString & path); + void makeObjetTree(const GLObjectBase * o, QTreeWidgetItem * ti); + + QTranslator translator; + QIcon icon_geo, icon_camera, icon_light; + QString prev_path; + GLObjectBase * sel_obj, * axis; + Light * cam_light; + GLPrimitiveCube * box; + Material m; + SessionManager session; + bool isChanged; + + GLParticlesSystem partsys; + +private slots: + void on_spinFOV_valueChanged(double val) {view->view()->setFOV(val);} + void on_spinDepthStart_valueChanged(double val) {view->view()->setDepthStart(val);} + void on_spinDepthEnd_valueChanged(double val) {view->view()->setDepthEnd(val);} + void on_comboRenderer_currentIndexChanged(int val); + void on_comboViewRenderMode_currentIndexChanged(int val) {static int modes[] = {GL_POINT, GL_LINE, GL_FILL}; view->view()->setRenderMode((GLObjectBase::RenderMode)modes[val]);} + void on_groupHoverHalo_clicked(bool val) {view->view()->setHoverHaloEnabled(val);} + void on_groupSelectionHalo_clicked(bool val) {view->view()->setSelectionHaloEnabled(val);} + void on_spinHoverHaloFill_valueChanged(double val) {view->view()->setHoverHaloFillAlpha(val);} + void on_spinSelectionHaloFill_valueChanged(double val) {view->view()->setSelectionHaloFillAlpha(val);} + void on_colorHoverHalo_colorChanged(QColor color) {view->view()->setHoverHaloColor(color);} + void on_colorSelectionHalo_colorChanged(QColor color) {view->view()->setSelectionHaloColor(color);} + void on_checkFXAA_clicked(bool val) {view->view()->setFeature(QGLView::qglFXAA, val);} + void on_checkMSAA_clicked(bool val) {view->view()->setFeature(QGLView::qglMSAA, val);} + void on_colorBack_colorChanged(QColor color) {view->view()->setBackColor(color);} + void on_colorAmbient_colorChanged(QColor color) {view->view()->setAmbientColor(color);} + void on_checkCameraOrbit_clicked(bool val) {view->view()->setCameraOrbit(val);} + void on_spinViewLineWidth_valueChanged(double val) {view->view()->setLineWidth(val);} + + void on_groupShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsEnabled, val);} + void on_groupEyeAccomodation_clicked(bool val) {view->view()->setFeature(QGLView::qglEyeAccomodationEnabled, val);} + void on_groupBloom_clicked(bool val) {view->view()->setFeature(QGLView::qglBloomEnabled, val);} + void on_groupMotionBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglMotionBlurEnabled, val);} + void on_groupReflections_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsEnabled, val);} + void on_checkSoftShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsSoftEnabled, val);} + void on_groupSSAO_clicked(bool val) {view->view()->setFeature(QGLView::qglSSAOEnabled, val);} + void on_groupDOF_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldEnabled, val);} + void on_checkDOFAutoFocus_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusEnabled, val);} + void on_spinDOFFocus_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldFocus, val);} + void on_spinDOFDiaphragm_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldDiaphragm, val);} + void on_spinDOFSpeed_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusSpeed, val);} + void on_spinAccom_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationTime, val);} + void on_spinAccomMS_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationMaxSpeed, val);} + void on_checkReflectionsBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsBlur, val);} + void on_spinShadowmapSize_valueChanged(double val) {view->view()->setFeature(QGLView::qglShadowsMapSize, val);} + void on_spinMotionBlurFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglMotionBlurFactor, val);} + void on_spinMotionBlurSteps_valueChanged(int val) {view->view()->setFeature(QGLView::qglMotionBlurSteps, val);} + void on_spinBloomFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomFactor, val);} + void on_spinBloomRadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglBloomRadius, val);} + void on_spinBloomThreshold_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomThreshold, val);} + void on_spinSSAORadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglSSAORadius, val);} + + void on_actionExit_triggered() {close();} + void on_actionReset_triggered(); + void on_actionImport_triggered(); + void on_actionSave_triggered(); + void on_actionSaveSelected_triggered(); + void on_actionOpen_triggered(); + + void view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m); + + void on_treeObjects_itemClicked(QTreeWidgetItem * ti, int); + + void objectsTreeChanged(); + void materialChanged(); + void selectionChanged(GLObjectBase * cur, GLObjectBase *); + + void on_pushButton_clicked(); + void on_pushButton_2_clicked() {view->view()->reloadShaders();} + void on_pushButton_3_clicked(); + +public slots: + +signals: + +private: + QMatrix4x4 cam_mat; + +}; + +#endif // QGLVIEWWINDOW_H diff --git a/test/qglview/qglview_test/qglview_window.ui b/test/qglview/qglview_test/qglview_window.ui new file mode 100644 index 0000000..ba6799b --- /dev/null +++ b/test/qglview/qglview_test/qglview_window.ui @@ -0,0 +1,1234 @@ + + + QGLViewWindow + + + + 0 + 0 + 1125 + 1056 + + + + QGLView converter + + + + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 10 + 75 + true + + + + + + + Qt::AlignCenter + + + + + + + 0 + + + + View + + + + + + 0 + + + + Common + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + FOV + + + + + + + 0.100000000000000 + + + 179.900000000000006 + + + 60.000000000000000 + + + + + + + Depth + + + + + + + 0 + + + + + 3 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + - + + + + + + + 3 + + + 999999999.000000000000000 + + + + + + + + + Renderer + + + + + + + 0 + + + + Simple + + + + + Deferred shading + + + + + RT + + + + + + + + Draw mode + + + + + + + 2 + + + + Point + + + + + Wireframe + + + + + Solid + + + + + + + + Back color + + + + + + + + 10 + 10 + 10 + + + + + + + + Ambient + + + + + + + + 10 + 10 + 10 + + + + + + + + MSAA + + + + + + + FXAA + + + + + + + Hover halo + + + true + + + + + + + 10 + 10 + 10 + + + + true + + + + + + + Fill + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + + + Selection halo + + + true + + + + + + + 10 + 10 + 10 + + + + true + + + + + + + Fill + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + + + Camera + + + + + + Orbit + + + true + + + + + + + Camera Light + + + + + + + + + + 0.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Line width + + + + + + + + Features + + + + + + Shadows + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Shadowmap size + + + + + + + 16.000000000000000 + + + 2048.000000000000000 + + + 512.000000000000000 + + + 0 + + + 16.000000000000000 + + + 512.000000000000000 + + + true + + + + + + + Soft + + + true + + + + + + + + + + Bloom + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + 0.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + Radius + + + + + + + Threshold + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.900000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + false + + + + + + + + + + Motion blur + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + Steps + + + + + + + 0.000000000000000 + + + 10000.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + Eye accomodation + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + Max speed + + + + + + + 0.000000000000000 + + + 256.000000000000000 + + + 32.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.100000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + + + + Depth of field + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Diaphragm + + + + + + + Max speed + + + + + + + 0.100000000000000 + + + 1024.000000000000000 + + + 8.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Focus + + + + + + + 0.100000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + true + + + 999999.000000000000000 + + + + + + + Auto focus + + + + + + + + + + Reflections + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Blur + + + + + + + + + + SSAO + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + 0.000000000000000 + + + 16.000000000000000 + + + 5.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + + + + + remove light + + + + + + + reload shaders + + + + + + + rebuff all + + + + + + + Qt::Vertical + + + + 20 + 107 + + + + + + + + + Object + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + Material + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + Страница + + + + + + + 1 + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + Objects + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + true + + + + Name + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + + + 0 + 0 + 1125 + 21 + + + + + File + + + + + + + + + + + + + + + + + :/icons/application-exit.png:/icons/application-exit.png + + + Exit + + + + + + :/icons/document-import.png:/icons/document-import.png + + + Import ... + + + Ctrl+I + + + + + + :/icons/document-open.png:/icons/document-open.png + + + Open ... + + + Ctrl+O + + + + + + :/icons/document-save-all.png:/icons/document-save-all.png + + + Save ... + + + Ctrl+S + + + + + + :/icons/document-new.png:/icons/document-new.png + + + Reset + + + Ctrl+N + + + + + + :/icons/document-save-.png:/icons/document-save-.png + + + Save selected ... + + + Save selected + + + Ctrl+Shift+S + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + GLWidget + QWidget +
glwidget.h
+ 1 +
+ + MaterialEditor + QWidget +
material_editor.h
+ 1 +
+ + GLObjectEditor + QWidget +
globject_editor.h
+ 1 +
+ + PropertyEditor + QTreeWidget +
propertyeditor.h
+
+
+ + + + + + +
diff --git a/test/qglview/renderer_deferred_shading.cpp b/test/qglview/renderer_deferred_shading.cpp new file mode 100644 index 0000000..bb094fd --- /dev/null +++ b/test/qglview/renderer_deferred_shading.cpp @@ -0,0 +1,597 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "renderer_deferred_shading.h" +#include + + +RendererDeferredShading::RendererDeferredShading(QGLView * view_): GLRendererBase(view_), +fbo_g(5, true, GL_RGBA16F), fbo_out(3, false, GL_RGBA16F), fbo_hsmall(1, false, GL_RGB16F) { + shaders << ShaderPair("FXAA", &shader_fxaa) + << ShaderPair("dsl_pass_0", &shader_ds_0) + << ShaderPair("dsl_pass_1", &shader_ds_1) + << ShaderPair("hdr", &shader_hdr) + << ShaderPair("downscale", &shader_small) + << ShaderPair("bloom_pass_0", &shader_bloom_0) + << ShaderPair("bloom_pass_1", &shader_bloom_1) + << ShaderPair("fbo_add", &shader_fbo_add) + << ShaderPair("motion_blur", &shader_motion_blur) + << ShaderPair("shadow", &shader_shadow) + << ShaderPair("ssr", &shader_ssr) + << ShaderPair("ssr_blur", &shader_ssr_blur) + << ShaderPair("ssr_merge", &shader_ssr_merge) + << ShaderPair("ssao_blur", &shader_ssao_blur) + << ShaderPair("ssao_merge", &shader_ssao_merge) + << ShaderPair("dof", &shader_dof); + for (int i = 0; i < shaders.size(); ++i) + *(shaders[i].second) = nullptr; + lights_per_pass = 8; + tnoise = 0; + exposure_ = 1.; + df = new QWidget(); + df->setLayout(new QBoxLayout(QBoxLayout::TopToBottom)); + label_exp = new QLabel(); + label_exp_step = new QLabel(); + df->layout()->addWidget(label_exp); + df->layout()->addWidget(label_exp_step); + QPalette pal(df->palette()); + pal.setBrush(QPalette::Window, QColor(255, 255, 255, 192)); + df->setPalette(pal); + if (view_) + ;//view_->addObject(df); +} + + +RendererDeferredShading::~RendererDeferredShading() { + for (int i = 0; i < shaders.size(); ++i) { + QOpenGLShaderProgram * p(*(shaders[i].second)); + if (p) delete p; + } + delete df; +} + + +void RendererDeferredShading::renderScene() { + //qDebug() << lights_per_pass; + QMatrix4x4 mproj = rp.proj_matrix; + QMatrix4x4 mproji = rp.proj_matrix_i; + QMatrix4x4 mview = rp.view_matrix; + QMatrix4x4 mviewi = rp.view_matrix_i; + QMatrix4x4 mviewproji = (mproj * mview).inverted(); + QMatrix4x4 moffset = view.camera()->offsetMatrix(); + QMatrix4x4 moffseti = moffset.inverted(); + rp.prev_proj_matrix = prev_proj; + rp.prev_view_matrix = prev_view; + QMatrix4x4 vc_proji; + vc_proji.perspective(90., 1., view.camera()->depthStart(), view.camera()->depthEnd()); + vc_proji = vc_proji.inverted(); + corner_dirs[0] = (mproji * QVector4D( 1, 1, 0, 1)); + corner_dirs[1] = (mproji * QVector4D(-1, 1, 0, 1)); + corner_dirs[2] = (mproji * QVector4D(-1, -1, 0, 1)); + corner_dirs[3] = (mproji * QVector4D( 1, -1, 0, 1)); + //qDebug() << corner_dirs[0] << corner_dirs[1] << corner_dirs[2] << corner_dirs[3]; + fbo_g.bind(); + int buffs[] = {0, 1, 2, 3, 4}; + fbo_g.setWriteBuffers(buffs, 5); + if (white_image_id == 0) { + glActiveTexture(GL_TEXTURE0 + 6); + white_image_id = view.textureManager()->loadTexture(white_image, false); + glBindTexture(GL_TEXTURE_2D, white_image_id); + glActiveTexture(GL_TEXTURE0); + } + if (violent_image_id == 0) { + glActiveTexture(GL_TEXTURE0 + 7); + violent_image_id = view.textureManager()->loadTexture(violent_image, false); + glBindTexture(GL_TEXTURE_2D, violent_image_id); + glActiveTexture(GL_TEXTURE0); + } + glEnableDepth(); + glClearFramebuffer(QColor(0, 0, 0, 0)); + glDisable(GL_RESCALE_NORMAL); + shader_ds_0->bind(); + rp.setUniform(shader_ds_0); + shader_ds_0->setUniformValue("z_far", view.depthEnd()); + shader_ds_0->setUniformValue("z_near", view.depthStart()); + shader_ds_0->setUniformValue("t0", 0); + shader_ds_0->setUniformValue("t1", 1); + shader_ds_0->setUniformValue("t2", 2); + shader_ds_0->setUniformValue("t3", 3); + shader_ds_0->setUniformValue("t4", 4); + shader_ds_0->setUniformValue("dt", QVector2D(1.f / view.width(), 1.f / view.height())); + //qDebug() << rp.view_matrix << prev_view; + //shader_ds_0->setUniformValue("qgl_ModelViewMatrix", rp.view_matrix); + renderObjects(GLObjectBase::Solid, 0, shader_ds_0, true, false, false); + //glReleaseShaders(); + fbo_g.release(); + + if (view.isFeatureEnabled(QGLView::qglShadowsEnabled)) { + shader_shadow->bind(); + int sms = view.feature(QGLView::qglShadowsMapSize).toInt(); + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_CUBE_MAP); + glDisable(GL_MULTISAMPLE); + glDisable(GL_LIGHTING); + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDisable(GL_RESCALE_NORMAL); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + //qDebug() << "render shadows"; + for (int i = 0; i < view.lightsCount(); ++i) { + Light * l = view.light(i); + if (l->light_type == Light::Omni) continue; + l->shadow_map.resize(sms, sms); + l->shadow_map.bind(); + l->shadow_map.setWriteBuffer(0); + glClearFramebuffer(); + //glClear(GL_DEPTH_BUFFER_BIT); + renderShadow(l, shader_shadow, moffseti*mviewi); + l->shadow_map.release(); + } + } +// glUseProgram(0); +//// fbo_g.bindColorTextures(); +// glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(0)); +// glActiveTexture(GL_TEXTURE0); +// glDrawQuad(); +// return; + + glResetAllTransforms(); + glSetLightEnabled(false); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + //glBlendFunc(GL_ONE, GL_ONE); + glDisableDepth(); + rp.prepare(); + //qDebug() << rp.view_matrix; + shader_ds_1->bind(); + shader_ds_1->setUniformValue("z_far", view.depthEnd()); + shader_ds_1->setUniformValue("z_near", view.depthStart()); + shader_ds_1->setUniformValue("t0", 0); + shader_ds_1->setUniformValue("t1", 1); + shader_ds_1->setUniformValue("t2", 2); + shader_ds_1->setUniformValue("t3", 3); + shader_ds_1->setUniformValue("t4", 4); + shader_ds_1->setUniformValue("td", 5); + shader_ds_1->setUniformValue("back_color", view.backColor()); + shader_ds_1->setUniformValue("mat_proji", mproji); + shader_ds_1->setUniformValue("mat_view", mview); + shader_ds_1->setUniformValue("mat_viewi", mviewi); + shader_ds_1->setUniformValue("mat_viewproji", mviewproji); + shader_ds_1->setUniformValue("shadow_on", view.isFeatureEnabled(QGLView::qglShadowsEnabled) ? 1 : 0); + shader_ds_1->setUniformValue("dt", QVector2D(1.f / view.width(), 1.f / view.height())); + rp.setUniform(shader_ds_1); + fbo_g.bindColorTextures(); + fbo_g.bindDepthTexture(5); + fbo_out.bind(); + fbo_out.setWriteBuffer(0); + glClearFramebuffer(Qt::black, false); + //QVector lpos; + //qDebug() << view_matrix; + shader_ds_1->setUniformValue("t_pp", 6); + int passes = (view.lightsCount() - 1) / lights_per_pass + 1; + if (passes < 1) passes = 1; + //qDebug() << "render in" << passes << "passes (" << lights_per_pass << ")"; + int wi, ri; + for (int l = 0; l < passes; ++l) { + wi = 1 - l % 2; + ri = l % 2; + //qDebug() << " pass" << l << "read from" << ri << "write to" << wi; + glActiveTexture(GL_TEXTURE0 + 6); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + fbo_out.setWriteBuffer(wi); + setupDSLights(l, mview * moffset); + glDrawQuad(shader_ds_1, corner_dirs); + //break; + } + //fbo_out.release(); + wi = 1 - passes % 2; + ri = passes % 2; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + + if (view.isFeatureEnabled(QGLView::qglSSAOEnabled)) { + fbo_out.setWriteBuffer(2); + fbo_out.setReadBuffer(ri); + glBlitFramebuffer(0, 0, fbo_out.width(), fbo_out.height(), 0, 0, fbo_out.width(), fbo_out.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(2)); + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(1)); + glActiveTexture(GL_TEXTURE0); + //glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + //glActiveTextureChannel(1); + //glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(0)); + int lri = ri, lwi = wi;//, lms = ri; + shader_ssao_blur->bind(); + shader_ssao_blur->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_ssao_blur->setUniformValue("dt", QVector2D(1.f / fbo_out.width(), 1.f / fbo_out.height())); + shader_ssao_blur->setUniformValue("t0", 0); + shader_ssao_blur->setUniformValue("ts", 1); + shader_ssao_blur->setUniformValue("tg1", 2); + int passes = view.feature(QGLView::qglSSAORadius).toInt(); + int crad = 1; + for (int p = 0; p < passes; ++p) { + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(lri)); + fbo_out.setWriteBuffer(lwi); + shader_ssao_blur->setUniformValue("radius", GLfloat(crad)); + glDrawQuad(shader_ssao_blur); + piSwap(lwi, lri); + crad *= 2; + } + //qDebug() << wi << ri << lms; + /*wi = lri; + ri = 1 - lms;*/ + glEnable(GL_TEXTURE_1D); + if (tnoise == 0) { + glGenTextures(1, &tnoise); + glBindTexture(GL_TEXTURE_1D, tnoise); + QByteArray ba; + for (int i = 0; i < 32*3; ++i) + ba.push_back(char(random() % 256)); + //qDebug() << ba; + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB8, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, ba.constData()); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(lri)); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(2)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(1)); + glActiveTexture(GL_TEXTURE0 + 3); + glBindTexture(GL_TEXTURE_1D, tnoise); + shader_ssao_merge->bind(); + shader_ssao_merge->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_ssao_merge->setUniformValue("z_far", view.depthEnd()); + shader_ssao_merge->setUniformValue("z_near", view.depthStart()); + shader_ssao_merge->setUniformValue("mat_proj", mproj); + shader_ssao_merge->setUniformValue("n0", 3); + shader_ssao_merge->setUniformValue("t0", 0); + shader_ssao_merge->setUniformValue("ts", 1); + shader_ssao_merge->setUniformValue("tg1", 2); + fbo_out.setWriteBuffer(lwi); + glDrawQuad(shader_ssao_merge, corner_dirs); + glDisable(GL_TEXTURE_1D); + wi = lri; + ri = lwi; + //piSwap(wi, ri); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + //piSwap(wi, ri); + } + + if (view.isFeatureEnabled(QGLView::qglReflectionsEnabled)) { + fbo_out.setWriteBuffer(2); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//_MIPMAP_LINEAR); + //glGenerateMipmap(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(0)); + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(1)); + fbo_g.bindDepthTexture(7); + shader_ssr->bind(); + shader_ssr->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_ssr->setUniformValue("z_far", view.depthEnd()); + shader_ssr->setUniformValue("z_near", view.depthStart()); + shader_ssr->setUniformValue("mat_proj", mproj); + shader_ssr->setUniformValue("t0", 1); + shader_ssr->setUniformValue("t1", 2); + shader_ssr->setUniformValue("ts", 0); + shader_ssr->setUniformValue("td", 7); + glDrawQuad(shader_ssr, corner_dirs); + + glActiveTexture(GL_TEXTURE0); + int lri = 2, lwi = wi, lms = ri; + if (view.isFeatureEnabled(QGLView::qglReflectionsBlur)) { + shader_ssr_blur->bind(); + shader_ssr_blur->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_ssr_blur->setUniformValue("dt", QVector2D(1.f / fbo_out.width(), 1.f / fbo_out.height())); + shader_ssr_blur->setUniformValue("t0", 0); + int passes = 5; + int crad = 1; + for (int p = 0; p < passes; ++p) { + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(lri)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//_MIPMAP_LINEAR); + fbo_out.setWriteBuffer(lwi); + shader_ssr_blur->setUniformValue("radius", GLfloat(crad)); + glDrawQuad(shader_ssr_blur); + piSwap(lwi, lri); + crad *= 2; + } + } + //qDebug() << wi << ri << lms; + wi = lri; + ri = 1 - lms; + + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(wi)); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(1)); + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(lms)); + shader_ssr_merge->bind(); + shader_ssr_merge->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_ssr_merge->setUniformValue("t0", 0); + shader_ssr_merge->setUniformValue("tg1", 1); + shader_ssr_merge->setUniformValue("ts", 2); + fbo_out.setWriteBuffer(ri); + glDrawQuad(shader_ssr_blur); + wi = ri; + ri = 1 - ri; + //piSwap(wi, ri); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(wi)); + piSwap(wi, ri); + } + + if (view.isFeatureEnabled(QGLView::qglDepthOfFieldEnabled)) { + if (view.isFeatureEnabled(QGLView::qglDepthOfFieldAutoFocusEnabled)) { + GLfloat cw; + //glReadBuffer(); + fbo_g.bind(); + glReadPixels(fbo_out.width() / 2, fbo_out.height() / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cw); + fbo_out.bind(); + const float _pe = 2.4e-7f; + float cz = cw + cw - 1; + cz = ((_pe - 2.f) * view.depthStart()) / (cz + _pe - 1.f); // infinite depth + float z = view.feature(QGLView::qglDepthOfFieldFocus).toFloat(), + s = view.feature(QGLView::qglDepthOfFieldAutoFocusSpeed).toFloat(); + z = z * (1.f - s) + cz * s; + view.setFeature(QGLView::qglDepthOfFieldFocus, z); + } + shader_dof->bind(); + shader_dof->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_dof->setUniformValue("z_far", view.depthEnd()); + shader_dof->setUniformValue("z_near", view.depthStart()); + shader_dof->setUniformValue("focus", view.feature(QGLView::qglDepthOfFieldFocus).toFloat()); + shader_dof->setUniformValue("diaphragm", view.feature(QGLView::qglDepthOfFieldDiaphragm).toFloat()); + shader_dof->setUniformValue("t0", 0); + shader_dof->setUniformValue("td", 7); + fbo_g.bindDepthTexture(7); + glActiveTexture(GL_TEXTURE0); + + int passes = 3; + float crad = 1.; + for (int p = 0; p < passes; ++p) { + shader_dof->setUniformValue("radius", crad); + fbo_out.setWriteBuffer(wi); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + shader_dof->setUniformValue("dt", QVector2D(1.f / fbo_out.width(), 0.f)); + glDrawQuad(shader_dof); + piSwap(wi, ri); + fbo_out.setWriteBuffer(wi); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + shader_dof->setUniformValue("dt", QVector2D(0.f, 1.f / fbo_out.height())); + glDrawQuad(shader_dof); + piSwap(wi, ri); + crad *= 2.f; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + } + + if (view.isFeatureEnabled(QGLView::qglEyeAccomodationEnabled)) { + fbo_hsmall.bind(); + fbo_hsmall.setWriteBuffer(0); + shader_small->bind(); + shader_small->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_small->setUniformValue("t0", 0); + glDrawQuad(shader_small); + hcontent.resize(fbo_hsmall.width() * fbo_hsmall.height()); + glReadPixels(0, 0, fbo_hsmall.width(), fbo_hsmall.height(), GL_RGB, GL_FLOAT, hcontent.data()); + GLfloat max[3] = {0.,0.,0.};//min[3] = {10000.,10000.,10000.},; + for (int i = 0; i < hcontent.size(); ++i) { + //if (min[0] > hcontent[i].x) min[0] = hcontent[i].x; + if (max[0] < hcontent[i].x) max[0] = hcontent[i].x; + //if (min[1] > hcontent[i].y) min[1] = hcontent[i].y; + if (max[1] < hcontent[i].y) max[1] = hcontent[i].y; + //if (min[2] > hcontent[i].z) min[2] = hcontent[i].z; + if (max[2] < hcontent[i].z) max[2] = hcontent[i].z; + } + GLfloat mluma = (0.299f * max[0]) + (0.587f * max[1]) + (0.114f * max[2]); + float nexp = mluma / 16.f, dexp = nexp - exposure_, mestep = exposure_ * view.feature(QGLView::qglEyeAccomodationMaxSpeed).toFloat(); + dexp /= view.feature(QGLView::qglEyeAccomodationTime).toFloat(); + if (dexp > 0.f && dexp > mestep/4) dexp = mestep/4; + if (dexp < 0.f && dexp < -mestep) dexp = -mestep; + exposure_ += dexp; + label_exp->setText(QString("exposure: %1").arg(exposure_)); + label_exp_step->setText(QString("d_exposure: %1").arg(dexp)); + //qDebug() << min[0] << max[0] << min[1] << max[1] << min[2] << max[2]; + fbo_hsmall.release(); + + //glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + fbo_out.bind(); + fbo_out.setWriteBuffer(wi); + shader_hdr->bind(); + shader_hdr->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_hdr->setUniformValue("t0", 0); + shader_hdr->setUniformValue("exposure", GLfloat(1.f/exposure_)); + glDrawQuad(shader_hdr); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(wi)); + piSwap(wi, ri); + } + + if (view.isFeatureEnabled(QGLView::qglMotionBlurEnabled)) { + fbo_out.setWriteBuffer(wi); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_g.colorTexture(4)); + shader_motion_blur->bind(); + shader_motion_blur->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_motion_blur->setUniformValue("dt", QVector2D(1.f / fbo_out.width(), 1.f / fbo_out.height())); + shader_motion_blur->setUniformValue("t0", 0); + shader_motion_blur->setUniformValue("ts", 1); + shader_motion_blur->setUniformValue("factor", view.feature(QGLView::qglMotionBlurFactor).toFloat()); + shader_motion_blur->setUniformValue("steps", view.feature(QGLView::qglMotionBlurSteps).toInt()); + glDrawQuad(shader_motion_blur); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(wi)); + piSwap(wi, ri); + } + + if (view.isFeatureEnabled(QGLView::qglBloomEnabled)) { + fbo_out.setWriteBuffer(2); + fbo_out.setReadBuffer(ri); + glBlitFramebuffer(0, 0, fbo_out.width(), fbo_out.height(), 0, 0, fbo_out.width(), fbo_out.height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + //QTime tm; + //tm.restart(); + fbo_out.setWriteBuffer(wi); + shader_bloom_0->bind(); + shader_bloom_0->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_bloom_0->setUniformValue("factor", view.feature(QGLView::qglBloomFactor).toFloat()); + shader_bloom_0->setUniformValue("threshold", view.feature(QGLView::qglBloomThreshold).toFloat()); + shader_bloom_0->setUniformValue("t0", 0); + glDrawQuad(shader_bloom_0); + glActiveTexture(GL_TEXTURE0); + piSwap(wi, ri); + + shader_bloom_1->bind(); + shader_bloom_1->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_bloom_1->setUniformValue("dt", QVector2D(1.f / fbo_out.width(), 1.f / fbo_out.height())); + shader_bloom_1->setUniformValue("t0", 0); + int radius = view.feature(QGLView::qglBloomRadius).toInt(); + int passes = qMax(int(ceil(log2(radius))), 1); + int crad = 1; + for (int p = 0; p < passes; ++p) { + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + fbo_out.setWriteBuffer(wi); + if (p == passes - 1) + crad = piMax(1, radius - crad); + shader_bloom_1->setUniformValue("radius", crad); + glDrawQuad(shader_bloom_1); + piSwap(wi, ri); + crad *= 2; + } + //qDebug() << tm.elapsed(); + fbo_out.setWriteBuffer(wi); +// glActiveTextureChannel(0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(ri)); + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(2)); + shader_fbo_add->bind(); + shader_fbo_add->setUniformValue("qgl_ModelViewProjectionMatrix", QMatrix4x4()); + shader_fbo_add->setUniformValue("t0", 0); + shader_fbo_add->setUniformValue("t1", 1); + glDrawQuad(shader_fbo_add); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo_out.colorTexture(wi)); + piSwap(wi, ri); + } + + glUseProgram(0); + fbo_out.release(); + + if (view.isFeatureEnabled(QGLView::qglFXAA)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + shader_fxaa->bind(); + shader_fxaa->setUniformValue("dt", QVector2D(1.f / view.width(), 1.f / view.height())); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glDrawQuad(); + if (view.isFeatureEnabled(QGLView::qglFXAA)) + shader_fxaa->release(); + prev_proj = mproj; + prev_view = mview; +} + + +void RendererDeferredShading::init(int width, int height) { + initializeOpenGLFunctions(); + resize(width, height); +} + + +void RendererDeferredShading::resize(int width, int height) { + fbo_g.resize(width, height); + fbo_out.resize(width, height); + fbo_hsmall.resize(width / 16, height / 16); +// view.setSceneRect(QRect(0, 0, width, height)); + //df->move(-width / 2, -height / 2); +} + + +void RendererDeferredShading::reloadShaders() { + for (int i = 0; i < shaders.size(); ++i) { + QOpenGLShaderProgram * p(*(shaders[i].second)); + if (!p) p = new QOpenGLShaderProgram(view.context()); + loadShaders(p, shaders[i].first, "://shaders"); + *(shaders[i].second) = p; + } +} + + +void RendererDeferredShading::setupShadersTextures(GLObjectBase & object, GLRendererBase::RenderingParameters & rp) { + glActiveTexture(GL_TEXTURE0 + 6); + glBindTexture(GL_TEXTURE_2D, white_image_id); + glActiveTexture(GL_TEXTURE0 + 7); + glBindTexture(GL_TEXTURE_2D, violent_image_id); +} + + +void RendererDeferredShading::setupDSLights(int pass, const QMatrix4x4 & view_matrix) { + int light_start, light_end, lmax, shadow_start = 7; + light_start = pass * lights_per_pass; + light_end = qMin((pass + 1) * lights_per_pass, view.lights().size()); + lmax = light_start + lights_per_pass; + amb_light.intensity = (pass == 0 ? 1. : 0.); + amb_light.setColor(pass == 0 ? view.ambientColor() : Qt::black); + amb_light.setName("ambient"); + setUniformLight(shader_ds_1, &amb_light, "qgl_AmbientLight"); + amb_light.intensity = 0.; + QVector lv; + for (int i = light_start; i < light_end; ++i) { + lv << view.lights()[i]; + glActiveTexture(GL_TEXTURE0 + shadow_start + i - light_start); + glBindTexture(GL_TEXTURE_2D, lv.back()->shadow_map.depthTexture()); + if (view.isFeatureEnabled(QGLView::qglShadowsSoftEnabled)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + } + amb_light.setName("null"); + for (int i = light_end; i < lmax; ++i) + lv << &amb_light; + //QStringList lnl; foreach (Light * l, lv) lnl << l->name(); + //qDebug() << " lights" << light_start << "->" << light_end << ", inactive" << (lmax - light_end) << lnl; + setUniformLights(shader_ds_1, lv, view_matrix, shadow_start); +} + + +void RendererDeferredShading::setupAmbientLight(const QColor & a, bool first_pass) { + +} diff --git a/test/qglview/renderer_deferred_shading.h b/test/qglview/renderer_deferred_shading.h new file mode 100644 index 0000000..19c567d --- /dev/null +++ b/test/qglview/renderer_deferred_shading.h @@ -0,0 +1,67 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_DEFERRED_SHADING_H +#define RENDERER_DEFERRED_SHADING_H + +#include "qglview.h" +#include + + +class RendererDeferredShading: public GLRendererBase +{ +public: + RendererDeferredShading(QGLView * view); + virtual ~RendererDeferredShading(); + + virtual void renderScene(); + void init(int width, int height); + void resize(int width, int height); + void reloadShaders(); + +protected: + void setupShadersTextures(GLObjectBase & object, RenderingParameters & rp); + void setupShadersLights(int lights_count) {cplc = lights_count;} + void setupDSLights(int pass, const QMatrix4x4 & view_matrix); + void setupAmbientLight(const QColor & a, bool first_pass); + +private: + typedef QPair ShaderPair; + + int cplc, lights_per_pass; + float exposure_; + GLFramebuffer fbo_g, fbo_out, fbo_hsmall; + QOpenGLShaderProgram * shader_fxaa, * shader_ds_0, * shader_ds_1, * shader_hdr, * shader_small; + QOpenGLShaderProgram * shader_bloom_0, * shader_bloom_1, * shader_motion_blur, * shader_fbo_add; + QOpenGLShaderProgram * shader_shadow, * shader_ssr, * shader_ssr_blur, * shader_ssr_merge; + QOpenGLShaderProgram * shader_ssao_blur, * shader_ssao_merge, * shader_dof; + GLuint tnoise; + QVector shaders; + + QMatrix4x4 prev_view, prev_proj; + QMatrix3x3 nm; + QVector4D corner_dirs[4]; + QVector hcontent; + Light amb_light; + + QWidget * df; + QLabel * label_exp, * label_exp_step; + +}; + +#endif // RENDERER_DEFERRED_SHADING_H diff --git a/test/qglview/renderer_simple.cpp b/test/qglview/renderer_simple.cpp new file mode 100644 index 0000000..164af78 --- /dev/null +++ b/test/qglview/renderer_simple.cpp @@ -0,0 +1,130 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "renderer_simple.h" + + +RendererSimple::RendererSimple(QGLView * view_): GLRendererBase(view_), fbo(2) +, fbo_c(1, true, GL_RGBA32F) /// WARNING +{ + shader_fxaa = 0; + shader = 0; /// WARNING +} + + +void RendererSimple::reloadShaders() { + if (shader_fxaa == 0) { + shader_fxaa = new QOpenGLShaderProgram(view.context()); + loadShaders(shader_fxaa, "FXAA", "://shaders"); + } + /*if (shader == 0) { + shader = new QOpenGLShaderProgram(view.context()); /// WARNING + loadShaders(shader, "test", "shaders"); /// WARNING + }*/ +} + + +void RendererSimple::resizeFBO(int w, int h) { + initializeOpenGLFunctions(); + fbo.resize(w, h); + fbo_c.resize(w, h); /// WARNING +} + + +void RendererSimple::renderScene() { + int passes = (view.lightsCount() - 1) / 8 + 1; + //QMatrix4x4 pm = getGLMatrix(GL_PROJECTION_MATRIX), mvm = getGLMatrix(GL_MODELVIEW_MATRIX), pmvm = pm * mvm, lpm, lmvm, lpmvm; + glSetCapEnabled(GL_MULTISAMPLE, view.isFeatureEnabled(QGLView::qglMSAA)); + if (passes < 1) passes = 1; + //glEnable(GL_FOG); + if (view.isFeatureEnabled(QGLView::qglFXAA)) fbo.bind(); + //glEnable(GL_TEXTURE_2D); + if (passes > 1) { + fbo.bind(); + fbo.setWriteBuffer(0); + glClearFramebuffer(); + } + glEnable(GL_RESCALE_NORMAL); + for (int l = 0; l < passes; ++l) { + if (passes > 1) fbo.setWriteBuffer(1); + if (l == 0) { + glClearFramebuffer(view.backColor()); + glEnableDepth(); + } else { + glClearFramebuffer(Qt::black, false); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDepthFunc(GL_EQUAL); + } + //view.camera().apply(view.aspect); + rp.cam_offset_matrix = view.camera()->offsetMatrix(); + rp.prepare(); + setupLights(l, 8); + + + /*fbo_c.bind(); + glClearFramebuffer(); + //shader->bind(); /// WARNING + renderObjects(GLObjectBase::Solid, l, 0, true, view.isLightEnabled(), view.isFogEnabled()); + //shader->release(); /// WARNING + if (QRect(QPoint(), fbo_c.size()).contains(mpos)) { + //qDebug() << mpos; + GLfloat data[4] = {0, 0, 0, 0}; + glReadPixels(mpos.x(), fbo_c.height() - mpos.y(), 1, 1, GL_RGBA, GL_FLOAT, data); + //qDebug() << QVector3D(data[0], data[1], data[2]); + } + fbo_c.release();*/ + //qDebug() << rp.viewproj_matrix << (getGLMatrix(GL_PROJECTION_MATRIX)*getGLMatrix(GL_MODELVIEW_MATRIX)); + renderObjects(GLObjectBase::Solid, l, nullptr, true, view.isLightEnabled(), view.isFogEnabled()); + + + //renderObjects(GLObjectBase::Solid, l, 0, true, true, view.isFogEnabled()); + renderObjects(GLObjectBase::Transparent, l, nullptr, true, true, view.isFogEnabled()); + if (passes > 1) { + glSetLightEnabled(false); + glSetCapEnabled(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + view.glReleaseTextures(); + glBindTexture(GL_TEXTURE_2D, fbo.colorTexture(1)); + glDisableDepth(); + fbo.setWriteBuffer(0); + glDrawQuad(); + } + } + if (view.isFeatureEnabled(QGLView::qglFXAA) || passes > 1) { + fbo.release(); + //glClearFramebuffer(); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fbo.colorTexture()); + glSetLightEnabled(false); + glSetCapEnabled(GL_BLEND, false); + glDisableDepth(); + if (view.isFeatureEnabled(QGLView::qglFXAA)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + shader_fxaa->bind(); + shader_fxaa->setUniformValue("dt", QVector2D(1.f / view.width(), 1.f / view.height())); + glDrawQuad(); + shader_fxaa->release(); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glDrawQuad(); + } + } +} diff --git a/test/qglview/renderer_simple.h b/test/qglview/renderer_simple.h new file mode 100644 index 0000000..c55cd4f --- /dev/null +++ b/test/qglview/renderer_simple.h @@ -0,0 +1,47 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_SIMPLE_H +#define RENDERER_SIMPLE_H + +#include "qglview.h" + +class RendererSimple: public GLRendererBase +{ +public: + RendererSimple(QGLView * view); + virtual ~RendererSimple() {if (shader_fxaa != 0) delete shader_fxaa;} + + virtual void renderScene(); + virtual void reloadShaders(); + virtual void init(int width, int height) {resizeFBO(width, height);} + virtual void resize(int width, int height) {resizeFBO(width, height);} + + QPoint mpos; + +protected: + +private: + void resizeFBO(int w, int h); + + GLFramebuffer fbo, fbo_c; + QOpenGLShaderProgram * shader_fxaa, * shader; + +}; + +#endif // RENDERER_SIMPLE_H diff --git a/test/qglview/shaders/FXAA.frag b/test/qglview/shaders/FXAA.frag new file mode 100644 index 0000000..25082d4 --- /dev/null +++ b/test/qglview/shaders/FXAA.frag @@ -0,0 +1,230 @@ +#version 130 + +in vec4 posPos; + +uniform sampler2D tex0; // 0 +//uniform float vx_offset; +uniform vec2 dt; +uniform float FXAA_SPAN_MAX = 8.; +uniform float FXAA_REDUCE_MUL = 1. / 8.; + +#define FxaaInt2 ivec2 +//#define vec2 vec2 + +vec3 FxaaPixelShader(vec4 posPos, // Output of FxaaVertexShader interpolated across screen. + sampler2D tex, // Input texture. + vec2 rcpFrame) { // Constant {1.0/frameWidth, 1.0/frameHeight}. +/*---------------------------------------------------------*/ +// #define FXAA_REDUCE_MIN (1. / 128.) + #define FXAA_REDUCE_MIN (1. / 128.) + //#define FXAA_REDUCE_MUL (1.0/8.0) + //#define FXAA_SPAN_MAX 8.0 +/*---------------------------------------------------------*/ + vec3 rgbNW = texture2D(tex, posPos.zw).xyz; + vec3 rgbNE = textureOffset(tex, posPos.zw, ivec2(1, 0)).xyz; + vec3 rgbSW = textureOffset(tex, posPos.zw, ivec2(0, 1)).xyz; + vec3 rgbSE = textureOffset(tex, posPos.zw, ivec2(1, 1)).xyz; + vec3 rgbM = texture2D(tex, posPos.xy).xyz; +/*---------------------------------------------------------*/ + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); +/*---------------------------------------------------------*/ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); +/*---------------------------------------------------------*/ + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); +/*---------------------------------------------------------*/ + float dirReduce = max( + (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), + FXAA_REDUCE_MIN); + float rcpDirMin = 1. / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) * rcpFrame.xy; +/*--------------------------------------------------------*/ + vec3 rgbA = (1. / 2.) * ( + texture2D(tex, posPos.xy + dir * (1. / 3. - 0.5)).xyz + + texture2D(tex, posPos.xy + dir * (2. / 3. - 0.5)).xyz); + vec3 rgbB = rgbA * (1. / 2.) + (1. / 4.) * ( + texture2D(tex, posPos.xy + dir * (0. / 3. - 0.5)).xyz + + texture2D(tex, posPos.xy + dir * (3. / 3. - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + if ((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA; + return rgbB; +} + + +vec3 Fxaa3PixelShader ( + // {xy} = center of pixel + vec4 pos, + // {xyzw} = not used on FXAA3 Quality + //vec4 posPos, + // {rgb_} = color in linear or perceptual color space + // {__a} = luma in perceptual color space (not linear) + sampler2D tex, + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + vec2 rcpFrame + // {xyzw} = not used on FXAA3 Quality + //vec4 rcpFrameOpt +) { +/*--------------------------------------------------------------------------*/ + //#if (FXAA_GATHER4_ALPHA == 1) + /*vec4 luma4A = FxaaTexOffAlpha4(tex, pos.xy, FxaaInt2(-1, -1), rcpFrame.xy); + #if (FXAA_DISCARD == 0) + vec4 rgbyM = texture2D(tex, pos.xy); + #endif + vec4 luma4B = FxaaTexAlpha4(tex, pos.xy, rcpFrame.xy); + float lumaNE = textureOffset(tex, pos.xy, ivec(1, -1)).w; + float lumaSW = textureOffset(tex, pos.xy, ivec(-1, 1)).w; + float lumaNW = luma4A.w; + float lumaN = luma4A.z; + float lumaW = luma4A.x; + float lumaM = luma4A.y; + float lumaE = luma4B.z; + float lumaS = luma4B.x; + float lumaSE = luma4B.y;*/ + //#else + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaN = dot(textureLodOffset(tex, pos.xy, 0., ivec2(0, -1)).rgb, luma); + float lumaW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1, 0)).rgb, luma); + vec4 rgbyM = texture2D(tex, pos.xy); + float lumaE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1, 0)).rgb, luma); + float lumaS = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 0, 1)).rgb, luma); + float lumaM = dot(rgbyM.rgb, luma); + //#endif +/*--------------------------------------------------------------------------*/ + float rangeMin = min(lumaM, min(min(lumaN, lumaW), min(lumaS, lumaE))); + float rangeMax = max(lumaM, max(max(lumaN, lumaW), max(lumaS, lumaE))); + float range = rangeMax - rangeMin; + //return vec4(range); +/*--------------------------------------------------------------------------*/ + #define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/12.0) + #define FXAA_QUALITY_EDGE_THRESHOLD (1.0/6.0) + if(range < max(FXAA_QUALITY_EDGE_THRESHOLD_MIN, rangeMax * FXAA_QUALITY_EDGE_THRESHOLD)) + /*#if (FXAA_DISCARD == 1) + FxaaDiscard; + #else*/ + return rgbyM.rgb; + //return vec3(0); + //#endif +/*--------------------------------------------------------------------------*/ + //#if (FXAA_GATHER4_ALPHA == 0) + float lumaNW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1,-1)).rgb, luma); + float lumaNE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1,-1)).rgb, luma); + float lumaSW = dot(textureLodOffset(tex, pos.xy, 0., ivec2(-1, 1)).rgb, luma); + float lumaSE = dot(textureLodOffset(tex, pos.xy, 0., ivec2( 1, 1)).rgb, luma); + //#endif +/*--------------------------------------------------------------------------*/ + #define FXAA_QUALITY_SUBPIX_CAP (3.0/4.0) + //#define FXAA_QUALITY_SUBPIX_CAP 0 + #define FXAA_QUALITY_SUBPIX_TRIM (1.0/4.0) + #define FXAA_QUALITY_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_QUALITY_SUBPIX_TRIM)) +/*--------------------------------------------------------------------------*/ + float lumaL = (lumaN + lumaW + lumaE + lumaS) * 0.25; + float rangeL = abs(lumaL - lumaM); + float blendL = clamp((rangeL / range) - FXAA_QUALITY_SUBPIX_TRIM, 0., 1.) * FXAA_QUALITY_SUBPIX_TRIM_SCALE; + blendL = min(FXAA_QUALITY_SUBPIX_CAP, blendL); +/*--------------------------------------------------------------------------*/ + float edgeVert = + abs(lumaNW + (-2.0 * lumaN) + lumaNE) + + 10.0 * abs(lumaW + (-2.0 * lumaM) + lumaE ) + + abs(lumaSW + (-2.0 * lumaS) + lumaSE); + float edgeHorz = + abs(lumaNW + (-2.0 * lumaW) + lumaSW) + + 10.0 * abs(lumaN + (-2.0 * lumaM) + lumaS ) + + abs(lumaNE + (-2.0 * lumaE) + lumaSE); + //return vec3(edgeHorz); + //float edgeVert = abs(lumaS - lumaN); + //float edgeHorz = abs(lumaE - lumaW); + bool horzSpan = edgeHorz >= edgeVert; +/*--------------------------------------------------------------------------*/ + float lengthSign = horzSpan ? -rcpFrame.y : -rcpFrame.x; + //if (edgeHorz == edgeVert) ;//lengthSign = 0.; + if(!horzSpan) { + lumaN = lumaW; + lumaS = lumaE; + } + float gradientN = abs(lumaN - lumaM); + float gradientS = abs(lumaS - lumaM); + lumaN = (lumaN + lumaM) * 0.5; + lumaS = (lumaS + lumaM) * 0.5; +/*--------------------------------------------------------------------------*/ + bool pairN = gradientN >= gradientS; + if(!pairN) { + lumaN = lumaS; + gradientN = gradientS; + lengthSign = -lengthSign; + } + vec2 posN; + posN = pos.xy + (horzSpan ? vec2(0.0, lengthSign * 0.5) : vec2(lengthSign * 0.5, 0.0)); + //posN.x = pos.x + (horzSpan ? 0.0 : lengthSign * 0.5); + //posN.y = pos.y + (horzSpan ? lengthSign * 0.5 : 0.0); +/*--------------------------------------------------------------------------*/ + #define FXAA_SEARCH_STEPS 8 + #define FXAA_SEARCH_THRESHOLD (1.0/8.0) +/*--------------------------------------------------------------------------*/ + gradientN *= FXAA_SEARCH_THRESHOLD; +/*--------------------------------------------------------------------------*/ + vec2 posP = posN; + vec2 offNP = horzSpan ? vec2(rcpFrame.x, 0.0) : vec2(0.0, rcpFrame.y); + float lumaEndN = 0.; + float lumaEndP = 0.; + bool doneN = false; + bool doneP = false; + posN += offNP * (-1.5); + posP += offNP * ( 1.5); + for(int i = 0; i < FXAA_SEARCH_STEPS; i++) { + lumaEndN = dot(texture2DLod(tex, posN.xy, 0.).rgb, luma); + lumaEndP = dot(texture2DLod(tex, posP.xy, 0.).rgb, luma); + bool doneN2 = abs(lumaEndN - lumaN) >= gradientN; + bool doneP2 = abs(lumaEndP - lumaN) >= gradientN; + if(doneN2 && !doneN) posN += offNP; + if(doneP2 && !doneP) posP -= offNP; + if(doneN2 && doneP2) break; + doneN = doneN2; + doneP = doneP2; + if(!doneN) posN -= offNP * 2.0; + if(!doneP) posP += offNP * 2.0; + } +/*--------------------------------------------------------------------------*/ + float dstN = horzSpan ? pos.x - posN.x : pos.y - posN.y; + float dstP = horzSpan ? posP.x - pos.x : posP.y - pos.y; +/*--------------------------------------------------------------------------*/ + bool directionN = dstN < dstP; + lumaEndN = directionN ? lumaEndN : lumaEndP; +/*--------------------------------------------------------------------------*/ + if(((lumaM - lumaN) < 0.0) == ((lumaEndN - lumaN) < 0.0)) + lengthSign = 0.0; +/*--------------------------------------------------------------------------*/ + float spanLength = (dstP + dstN); + dstN = directionN ? dstN : dstP; + float subPixelOffset = 0.5 + (dstN * (-1.0/spanLength)); + subPixelOffset += blendL * (1.0/8.0); + subPixelOffset *= lengthSign; + vec3 rgbF = texture2DLod(tex, pos.xy + (horzSpan ? vec2(0., subPixelOffset) : vec2(subPixelOffset, 0.)), 0.).xyz; +/*--------------------------------------------------------------------------*/ + /*#if (FXAA_LINEAR == 1) + lumaL *= lumaL; + #endif*/ + float lumaF = dot(rgbF, vec3(0.299, 0.587, 0.114)) + (1.0/(65536.0*256.0)); + float lumaB = mix(lumaF, lumaL, blendL); + float scale = min(4.0, lumaB/lumaF); + rgbF *= scale; + return vec3(rgbF);//, lumaM.rgb); + //, lumaM.rgb); +} + + +void main() { + gl_FragColor.rgb = Fxaa3PixelShader(posPos, tex0, dt); +} + + diff --git a/test/qglview/shaders/FXAA.vert b/test/qglview/shaders/FXAA.vert new file mode 100644 index 0000000..d02e1b6 --- /dev/null +++ b/test/qglview/shaders/FXAA.vert @@ -0,0 +1,13 @@ +#version 130 + +out vec4 posPos; + +uniform float FXAA_SUBPIX_SHIFT = 1. / 4.; +uniform vec2 dt; + +void main(void) { + posPos.xy = gl_MultiTexCoord0.xy; + posPos.zw = gl_MultiTexCoord0.xy - (dt * (0.5 + FXAA_SUBPIX_SHIFT)); + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); +} diff --git a/test/qglview/shaders/bloom_0.frag b/test/qglview/shaders/bloom_0.frag new file mode 100644 index 0000000..15714e1 --- /dev/null +++ b/test/qglview/shaders/bloom_0.frag @@ -0,0 +1,8 @@ +#version 120 + +uniform sampler2D t0; +uniform float clum; + +void main(void) { + gl_FragColor = max((texture2D(t0, gl_TexCoord[0].xy) / clum - 0.8) * 5., vec4(0.)); +} diff --git a/test/qglview/shaders/bloom_1.frag b/test/qglview/shaders/bloom_1.frag new file mode 100644 index 0000000..17fa461 --- /dev/null +++ b/test/qglview/shaders/bloom_1.frag @@ -0,0 +1,30 @@ +#version 130 + +uniform sampler2D t0; +uniform vec2 vsize; + +void main(void) { + vec2 tc = gl_TexCoord[0].xy, dt = 2. / vsize, dt2 = dt + dt; + gl_FragColor = (texture2D(t0, tc) * 3. + + textureOffset(t0, tc, ivec2(1, 0)) * 2. + + textureOffset(t0, tc, ivec2(0, 1)) * 2. + + textureOffset(t0, tc, ivec2(-1, 0)) * 2. + + textureOffset(t0, tc, ivec2(0, -1)) * 2. + + textureOffset(t0, tc, ivec2(1, 1)) * 1.5 + + textureOffset(t0, tc, ivec2(1, -1)) * 1.5 + + textureOffset(t0, tc, ivec2(-1, -1)) * 1.5 + + textureOffset(t0, tc, ivec2(-1, 1)) * 1.5 + + textureOffset(t0, tc, ivec2(2, 0)) + + textureOffset(t0, tc, ivec2(0, 2)) + + textureOffset(t0, tc, ivec2(-2, 0)) + + textureOffset(t0, tc, ivec2(0, -2)) + + textureOffset(t0, tc, ivec2(2, 1)) + + textureOffset(t0, tc, ivec2(2, -1)) + + textureOffset(t0, tc, ivec2(-2, -1)) + + textureOffset(t0, tc, ivec2(-2, 1)) + + textureOffset(t0, tc, ivec2(1, 2)) + + textureOffset(t0, tc, ivec2(1, -2)) + + textureOffset(t0, tc, ivec2(-1, -2)) + + textureOffset(t0, tc, ivec2(-1, 2))) / 29.; + //gl_FragColor = texture2D(t0, tc); +} diff --git a/test/qglview/shaders/bloom_pass_0.frag b/test/qglview/shaders/bloom_pass_0.frag new file mode 100644 index 0000000..abd5404 --- /dev/null +++ b/test/qglview/shaders/bloom_pass_0.frag @@ -0,0 +1,8 @@ +#version 150 + +uniform sampler2D t0; +uniform float factor = 1., threshold = 0.8; + +void main(void) { + qgl_FragData[0].rgb = max(texelFetch(t0, ivec2(gl_FragCoord.xy), 0).rgb - vec3(threshold), vec3(0.)) * factor; +} diff --git a/test/qglview/shaders/bloom_pass_0.vert b/test/qglview/shaders/bloom_pass_0.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/bloom_pass_0.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/bloom_pass_1.frag b/test/qglview/shaders/bloom_pass_1.frag new file mode 100644 index 0000000..12ad08b --- /dev/null +++ b/test/qglview/shaders/bloom_pass_1.frag @@ -0,0 +1,55 @@ +#version 150 + +uniform sampler2D t0; +uniform int radius = 2; +uniform vec2 dt; + +void main(void) { + vec2 tc = qgl_FragTexture.xy; + float r = float(radius); + int o = radius, o2 = radius * 2; + vec3 scol = (texture(t0, tc).rgb * 3. + + texture(t0, tc + dt * vec2( o, 0 )).rgb * 2. + + texture(t0, tc + dt * vec2( 0, o )).rgb * 2. + + texture(t0, tc + dt * vec2( -o, 0 )).rgb * 2. + + texture(t0, tc + dt * vec2( 0, -o)).rgb * 2. + + texture(t0, tc + dt * vec2( o, o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( o, -o)).rgb * 1.5 + + texture(t0, tc + dt * vec2( -o, -o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( -o, o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( o2, 0 )).rgb + + texture(t0, tc + dt * vec2( 0, o2)).rgb + + texture(t0, tc + dt * vec2(-o2, 0 )).rgb + + texture(t0, tc + dt * vec2( 0, -o2)).rgb + + texture(t0, tc + dt * vec2( o2, o )).rgb + + texture(t0, tc + dt * vec2( o2, -o)).rgb + + texture(t0, tc + dt * vec2(-o2, -o )).rgb + + texture(t0, tc + dt * vec2(-o2, o )).rgb + + texture(t0, tc + dt * vec2( o, o2)).rgb + + texture(t0, tc + dt * vec2( o, -o2)).rgb + + texture(t0, tc + dt * vec2( -o, -o2)).rgb + + texture(t0, tc + dt * vec2( -o, o2)).rgb) / 29.; + /*vec3 scol = (texelFetch(t0, tc, 0).rgb * 3. + + texelFetch(t0, tc + ivec2( o, 0 ), 0).rgb * 2. + + texelFetch(t0, tc + ivec2( 0, o ), 0).rgb * 2. + + texelFetch(t0, tc + ivec2( -o, 0 ), 0).rgb * 2. + + texelFetch(t0, tc + ivec2( 0, -o), 0).rgb * 2. + + texelFetch(t0, tc + ivec2( o, o ), 0).rgb * 1.5 + + texelFetch(t0, tc + ivec2( o, -o), 0).rgb * 1.5 + + texelFetch(t0, tc + ivec2( -o, -o ), 0).rgb * 1.5 + + texelFetch(t0, tc + ivec2( -o, o ), 0).rgb * 1.5 + + texelFetch(t0, tc + ivec2( o2, 0 ), 0).rgb + + texelFetch(t0, tc + ivec2( 0, o2), 0).rgb + + texelFetch(t0, tc + ivec2(-o2, 0 ), 0).rgb + + texelFetch(t0, tc + ivec2( 0, -o2), 0).rgb + + texelFetch(t0, tc + ivec2( o2, o ), 0).rgb + + texelFetch(t0, tc + ivec2( o2, -o), 0).rgb + + texelFetch(t0, tc + ivec2(-o2, -o ), 0).rgb + + texelFetch(t0, tc + ivec2(-o2, o ), 0).rgb + + texelFetch(t0, tc + ivec2( o, o2), 0).rgb + + texelFetch(t0, tc + ivec2( o, -o2), 0).rgb + + texelFetch(t0, tc + ivec2( -o, -o2), 0).rgb + + texelFetch(t0, tc + ivec2( -o, o2), 0).rgb) / 29.;*/ + qgl_FragData[0].rgb = scol; + //qgl_FragData[0].rgb = vec3(r/10.); +} diff --git a/test/qglview/shaders/bloom_pass_1.vert b/test/qglview/shaders/bloom_pass_1.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/bloom_pass_1.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/dof.frag b/test/qglview/shaders/dof.frag new file mode 100644 index 0000000..4d1d81e --- /dev/null +++ b/test/qglview/shaders/dof.frag @@ -0,0 +1,47 @@ +#version 150 + +uniform float z_near, z_far; +uniform sampler2D t0, td; +uniform vec2 dt; +uniform float radius, focus, diaphragm; + +float w, sum; +const float _pe = 2.4e-7; + +vec4 getTexel(vec2 c, float coeff) { + vec4 ret = texture(t0, c); + float cw = texture(td, c).r; + cw = clamp(1. - abs(cw - w) * 1000, 0., 1.) * coeff; + sum += cw; + return ret * cw; +} + +void main(void) { + vec2 tc = qgl_FragTexture.xy; + vec4 scol = texture(t0, tc); + w = texture(td, tc).r; + float z = w + w - 1; + z = ((_pe - 2.) * z_near) / (z + _pe - 1.); // infinite depth + z = 1./(z + 1); + float cf = 1./(focus + 1); + float factor = clamp(abs(z - cf) * diaphragm, 0, 100); + factor = 1. - 1. / (factor + 1.); + factor *= factor; + factor *= factor; + //factor *= factor; + factor *= radius; + vec2 cdt = factor * dt; + sum = 1.; + scol += getTexel(tc + (cdt * 1), 2.5); + scol += getTexel(tc + (cdt * 2), 2.0); + scol += getTexel(tc + (cdt * 3), 1.5); + scol += getTexel(tc + (cdt * 4), 1.0); + scol += getTexel(tc + (cdt * 5), 0.5); + scol += getTexel(tc + (cdt * -1), 2.5); + scol += getTexel(tc + (cdt * -2), 2.0); + scol += getTexel(tc + (cdt * -3), 1.5); + scol += getTexel(tc + (cdt * -4), 1.0); + scol += getTexel(tc + (cdt * -5), 0.5); + scol /= sum; + qgl_FragData[0].rgba = scol; +} diff --git a/test/qglview/shaders/dof.vert b/test/qglview/shaders/dof.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/dof.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/downscale.frag b/test/qglview/shaders/downscale.frag new file mode 100644 index 0000000..0cef038 --- /dev/null +++ b/test/qglview/shaders/downscale.frag @@ -0,0 +1,7 @@ +#version 150 + +uniform sampler2D t0; + +void main(void) { + qgl_FragData[0].rgb = textureLod(t0, qgl_FragTexture.xy, 3).rgb; +} diff --git a/test/qglview/shaders/downscale.vert b/test/qglview/shaders/downscale.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/downscale.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/dsl_pass_0.frag b/test/qglview/shaders/dsl_pass_0.frag new file mode 100644 index 0000000..e1e6782 --- /dev/null +++ b/test/qglview/shaders/dsl_pass_0.frag @@ -0,0 +1,67 @@ +#version 150 + +//#extension GL_ARB_conservative_depth: enable +//layout(depth_less) out float gl_FragDepth; + +in vec3 src_normal, normal, binormal;//, et; +in vec4 pos, ppos; +in float fogCoord, FC, C; +in mat3 TBN; + +uniform bool acc_fog; +uniform vec2 dt; +uniform float z_far, z_near; + +const vec3 luma = vec3(0.299, 0.587, 0.114); + +void main(void) { + //float z = pos.w;//((z_near / (z_near-z_far)) * z_far) / (pos.w - (z_far / (z_far-z_near))); + //float logz = log(pos.w * C + 1.) * FC; + vec4 dc = qgl_FragColor; + vec2 tc = qgl_FragTexture.xy; + float hei = dot(texture(qgl_Material.map_relief.map, tc).rgb, luma) * qgl_Material.map_relief.amount + qgl_Material.map_relief.offset; + + vec3 n, dn, bn; + dn = texture(qgl_Material.map_normal.map, tc).rgb - vec3(0.5, 0.5, 1.); + dn = dn * qgl_Material.map_normal.amount + qgl_Material.map_normal.offset; + dn.y = -dn.y; + dn = TBN * dn; + //dn.z = 0; + n = normalize(qgl_NormalMatrix * (normal + dn)); + bn = normalize(qgl_NormalMatrix * binormal); + //n = dn; + + dc *= texture(qgl_Material.map_diffuse.map, tc) * qgl_Material.map_diffuse.amount + qgl_Material.map_diffuse.offset; + + vec4 spec = texture(qgl_Material.map_specular.map, tc) * qgl_Material.map_specular.amount + qgl_Material.map_specular.offset; + spec *= qgl_Material.color_specular; + float specularity = dot(texture(qgl_Material.map_specularity.map, tc).rgb, luma) * qgl_Material.map_specularity.amount + qgl_Material.map_specularity.offset; + specularity = clamp(specularity, 0.05, 0.99); + float reflectivity = qgl_Material.reflectivity;//dot(texture(qgl_Material.map_specularity.map, tc).rgb, luma) * qgl_Material.map_specularity.amount + qgl_Material.map_specularity.offset; + reflectivity = clamp(reflectivity, 0., 1.); + vec4 self = texture(qgl_Material.map_self_illumination.map, tc) * qgl_Material.map_self_illumination.amount + qgl_Material.map_self_illumination.offset; + self *= qgl_Material.color_self_illumination; + + vec3 speed = pos.xyz/pos.w - ppos.xyz/ppos.w; + //speed /= abs(pos.z); + + //gl_FragDepth = logz; + qgl_FragData[0] = vec4(dc.rgb, 0.); + qgl_FragData[1] = vec4(n.xyz, specularity + round(reflectivity * 100)); + qgl_FragData[2] = vec4(spec.rgb, hei); + qgl_FragData[3] = vec4(self.rgb, bn.x); + qgl_FragData[4] = vec4(speed.xy, bn.yz); + + //gl_FragData[0] = vec4(et.xyz, pos.w); + //gl_FragDepth = gl_FragCoord.z - clamp(hei / pos.z / pos.z / (abs(n.z) + 1), -0.01, 0.01); + /*vec4 dp = pos; + dp.x = floor(dp.w / 255.) / 255.; + dp.w -= dp.x * 65025; + dp.y = floor(dp.w) / 255.; + dp.w -= dp.y * 255.; + dp.z = fract(dp.w);*/ + //dp.x = dp.w; + //dp.w = (gl_FrontMaterial.specular.r + gl_FrontMaterial.specular.g + gl_FrontMaterial.specular.b) / 3. / 25.5; + //gl_FragData[2] = dp; + //gl_FragData[3] = vec4(light_model / 255., 0, 0, 0); +} diff --git a/test/qglview/shaders/dsl_pass_0.vert b/test/qglview/shaders/dsl_pass_0.vert new file mode 100644 index 0000000..ab3f806 --- /dev/null +++ b/test/qglview/shaders/dsl_pass_0.vert @@ -0,0 +1,46 @@ +#version 150 + +out vec3 src_normal, normal, binormal;//, et; +out vec4 pos, ppos; +out float fogCoord, FC, C; +out mat3 TBN; + +uniform bool acc_fog; +uniform vec2 dt; +uniform vec3 eye; +uniform mat4 prev_ModelViewProjectioMatrix; +uniform float z_near, z_far; + +void main(void) { + normal = qgl_Normal;//(qgl_NormalMatrix * qgl_Normal); + binormal = qgl_Bitangent;//(qgl_NormalMatrix * qgl_Normal); + pos = qgl_ftransform(); + TBN = mat3(qgl_Tangent, qgl_Bitangent, qgl_Normal); + /*if (acc_fog) { + fogCoord = (gl_Fog.end - length(pos.xyz) * 0.85) / (gl_Fog.end - gl_Fog.start); + fogCoord = 1. - clamp(fogCoord, 0., 1.); + }*/ + //gl_TexCoord[0] = gl_MultiTexCoord0; + //gl_TexCoord[1] = gl_MultiTexCoord1; + + src_normal = normalize(vec3(pos.xy * dt * 2., 0)); + + qgl_FragTexture = qgl_Texture; + qgl_FragColor = qgl_Color; + //tp /= tp.w; + ppos = prev_ModelViewProjectioMatrix * qgl_Vertex; + //pos.w = pos.w; + //speed = tp - ppos; + //speed /= (abs(speed) + 1.); + //speed.xyz *= speed.w; + //pos *= pos.w; + + //logz = gl_Position.w*C + 1; //version with fragment code + C = 0.01; + FC = 1. / log(z_far * C + 1.); + + //pos.z = (logz + logz - 1) * pos.w; + + gl_Position = pos; + +} diff --git a/test/qglview/shaders/dsl_pass_1.frag b/test/qglview/shaders/dsl_pass_1.frag new file mode 100644 index 0000000..e2aaf74 --- /dev/null +++ b/test/qglview/shaders/dsl_pass_1.frag @@ -0,0 +1,166 @@ +#version 150 +//#extension GL_EXT_gpu_shader4 : enable + +in vec3 view_dir; +in vec4 view_pos; + +uniform vec3 ambient; +uniform sampler2D t0, t1, t2, t3, t4, t_pp; +uniform sampler2D td; +uniform int gid, lightsCount, shadow_on; +uniform float z_near, z_far; +uniform bool firstPass; +uniform vec2 dt; +uniform vec4 back_color; +uniform mat4 mat_proji, mat_view, mat_viewi, mat_viewproji; + +float light_diffuse(int model, vec3 l, vec3 n) {return max(0., dot(l, n));} +float light_specular(int model, vec3 l, vec3 n, vec3 h, vec3 v, float shininess) {return max(0., pow(dot(n, h), shininess));} + +vec4 pos, lpos, shp; +vec3 li, si, ldir, halfV, bn, bn2, lwdir; +vec3 vds, vds2; +float sh_pow, sh_mul, dist, NdotL, NdotH, spot, ldist, diff, sdist, shadow; + +float getShadow(int light, vec3 view_pos, vec3 dpos) { + shp = qgl_Light[light].shadowMatrix * vec4(view_pos + dpos, 1); + shp.z -= z_near / 20.; + return textureProj(qgl_Light[light].shadow, shp); +} + +void calcLight(in int index, in vec3 n, in vec3 v, in vec4 v2) { + lpos = qgl_Light[index].position; + ldir = lpos.xyz - (pos.xyz * lpos.w); + ldist = length(ldir); + ldir = normalize(ldir); + halfV = normalize(ldir + v); + NdotL = max(dot(n, ldir), 0.0001); + NdotH = max(dot(n, halfV), 0.0001); + spot = step(0., NdotL) * qgl_Light[index].intensity; + if (qgl_Light[index].endAngle <= 90.) { + float scos = max(dot(-ldir, qgl_Light[index].direction.xyz), 0.); + spot *= scos * step(qgl_Light[index].endAngleCos, scos); + spot *= smoothstep(qgl_Light[index].endAngleCos, qgl_Light[index].startAngleCos, scos); + lwdir = mat3(mat_viewi) * qgl_Light[index].direction.xyz; + //bn = normalize(cross(lwdir, vec3(1, 0, 0))); + //bn2 = normalize(cross(lwdir, bn)); + float ds = ldist/200.;//max(abs(sdist) / 5000, 0.02); + //spot *= clamp(1. - sdist, 0, 1); + vds = ds * bn.xyz; + vds2 = ds * bn2.xyz; + float shadow = getShadow(index, pos.xyz, vec3(0)) * 3. + + getShadow(index, pos.xyz, vds ) * 2. + + getShadow(index, pos.xyz, - vds ) * 2. + + getShadow(index, pos.xyz, - vds2 ) * 2. + + getShadow(index, pos.xyz, + vds2 ) * 2. + + getShadow(index, pos.xyz, vds - vds2 ) * 1.5 + + getShadow(index, pos.xyz, vds + vds2 ) * 1.5 + + getShadow(index, pos.xyz, - vds - vds2 ) * 1.5 + + getShadow(index, pos.xyz, - vds + vds2 ) * 1.5 + + getShadow(index, pos.xyz, vds + vds ) + + getShadow(index, pos.xyz, - vds - vds ) + + getShadow(index, pos.xyz, - vds2 - vds2) + + getShadow(index, pos.xyz, + vds2 + vds2) + + getShadow(index, pos.xyz, vds + vds - vds2 ) + + getShadow(index, pos.xyz, - vds - vds - vds2 ) + + getShadow(index, pos.xyz, vds + vds + vds2 ) + + getShadow(index, pos.xyz, - vds - vds + vds2 ) + + getShadow(index, pos.xyz, vds - vds2 - vds2) + + getShadow(index, pos.xyz, vds + vds2 + vds2) + + getShadow(index, pos.xyz, - vds - vds2 - vds2) + + getShadow(index, pos.xyz, - vds + vds2 + vds2); + spot *= mix(1., shadow / 29., shadow_on); + } + spot /= (qgl_Light[index].constantAttenuation + ldist * (qgl_Light[index].linearAttenuation + ldist * qgl_Light[index].quadraticAttenuation)); + ///li += spot * gl_LightSource[index].diffuse.rgb * light_diffuse(0, ldir, n); + //si += spot * qgl_Light[index].color.rgb * sh_mul * light_specular(0, ldir, n, halfV, v, sh_pow); + float NdotLs = NdotL*NdotL; + float NdotHs = NdotH*NdotH; + float ndlc = (1. - NdotLs) / NdotLs; + float der = NdotLs * (sh_mul + ndlc); + diff = 2. / (1. + sqrt(1. + (1. - sh_mul) * ndlc)); + li += spot * qgl_Light[index].color.rgb * diff;// * light_diffuse(0, ldir, n); + ndlc = (1. - NdotHs) / NdotHs; + der = NdotHs * (sh_mul + ndlc); + si += spot * qgl_Light[index].color.rgb * (sh_mul / (der*der) / 3.1416); +} + + +const float _pe = 2.4e-7; + +void main(void) { + //if (d == 1.) discard; + ivec2 tc = ivec2(gl_FragCoord.xy); + float z = texelFetch(td, tc, 0).r; + if (z == 1.) { + qgl_FragData[0] = back_color; + return; + } + vec4 v0 = texelFetch(t0, tc, 0), + v1 = texelFetch(t1, tc, 0), + v2 = texelFetch(t2, tc, 0), + v3 = texelFetch(t3, tc, 0), + v4 = texelFetch(t4, tc, 0); + + z = z + z - 1; + z = ((_pe - 2.) * z_near) / (z + _pe - 1.); // infinite depth + + vec2 sp = gl_FragCoord.xy * dt * 2 - vec2(1, 1); + vec3 dc = v0.rgb, n = v1.xyz; + bn = normalize(vec3(v3.w, v4.zw)); + bn2 = normalize(cross(n, bn)); + float height = v2.w; + li = qgl_AmbientLight.color.rgb * qgl_AmbientLight.intensity; + si = vec3(0.); + + /*float posz = z_near * z_far / (texelFetch(td, tc, 0).r * (z_far - z_near) - z_far); + pos = vec4(sp, 0., 1) * mat_proji; + pos.xy *= v0.z; + pos.z = posz;*/ + + pos.w = 1; + pos.xyz = view_dir * z; + pos.z = -pos.z; + + //pos.z = posz; + //pos.xyz += n * height; + //pos.xyz = v3.xyz; + //pos = v3; + //pos = vec4(sp, 0, 1.) * mat_proji; + //pos *= v0.w; + //pos.z += 1; + //pos.xy *= 10.; + //pos.z = v0.w; + vec3 v = normalize(-pos.xyz); + + float reflectivity = 0.; + float specularity = modf(v1.w, reflectivity); + sh_pow = 1. / max((1. - specularity), 0.0001); + sh_mul = max(1. - specularity, 0.0001); + for (int i = 0; i < 8; ++i) + calcLight(i, n, v, v2); + // calcLight(0, n, v, v2); + + qgl_FragData[0] = vec4(max(vec3(0), li * dc + si * v2.rgb + v3.rgb + texelFetch(t_pp, tc, 0).rgb), v0.w); + //qgl_FragData[0].rgb = vec3(-z); + //qgl_FragData[0].rgb = li + vec3(texelFetch(t_pp, tc, 0).xyz); + //shd = shd - shp.w; + + /*vec3 fp = pos.xyz;// * lpos.w; + vec3 _dlp = fp - qgl_Light[0].position.xyz; + vec3 _ld = qgl_Light[0].direction.xyz; + float lz = dot(_ld, _dlp); + vec3 _lt = normalize(cross(_ld, _dlp)); + vec3 _lt2 = normalize(cross(_lt, _dlp)); + float ly = dot(qgl_Light[0].shadowDir0.xyz, normalize(_dlp)); + float lx = dot(qgl_Light[0].shadowDir1.xyz, normalize(_dlp)); + vec3 dd = mat3(mat_viewi)*(normalize(_dlp) - _ld); + //qgl_FragData[0].rgb = vec3(abs(shp.xy/shp.w)/1,0); + float Y = dot(_dlp, qgl_Light[0].shadowDir0.xyz) / length(qgl_Light[0].shadowDir0.xyz); + float X = dot(_dlp, qgl_Light[0].shadowDir1.xyz) / length(qgl_Light[0].shadowDir1.xyz); + qgl_FragData[0].rgb = vec3(abs((mat3(mat_viewi)*_dlp).x)/100);*/ + + //qgl_FragData[0].rgb = vec3(abs(mat3(mat_viewi)* qgl_Light[0].direction.xyz)); + //qgl_FragData[0].rgb = vec3(texture(qgl_Light[0].shadow,shp.xyz/shp.w)/2); + //qgl_FragData[0].a = 0.; +} diff --git a/test/qglview/shaders/dsl_pass_1.vert b/test/qglview/shaders/dsl_pass_1.vert new file mode 100644 index 0000000..30d914a --- /dev/null +++ b/test/qglview/shaders/dsl_pass_1.vert @@ -0,0 +1,14 @@ +#version 150 + +in vec3 view_corner; +out vec3 view_dir; +out vec4 view_pos; + +void main(void) { + view_dir = view_corner / view_corner.z; + view_pos = vec4(qgl_ModelViewMatrix * vec4(qgl_Vertex.xy, 1, 1)); + view_pos /= view_pos.w; + qgl_FragTexture = qgl_Texture; + qgl_FragColor = qgl_Color; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/dsl_pass_2.frag b/test/qglview/shaders/dsl_pass_2.frag new file mode 100644 index 0000000..05bd70c --- /dev/null +++ b/test/qglview/shaders/dsl_pass_2.frag @@ -0,0 +1,13 @@ +#version 120 + +uniform sampler2D t0, depth; +uniform vec3 backColor; + +void main(void) { + //gl_FragColor.rgb=vec3(1,1,1); + if (texture2D(depth, gl_TexCoord[0].xy).x == 1.) { + gl_FragColor.rgb = backColor; + return; + } + gl_FragColor.rgb = texture2D(t0, gl_TexCoord[0].xy).rgb; +} diff --git a/test/qglview/shaders/dsl_pass_2.vert b/test/qglview/shaders/dsl_pass_2.vert new file mode 100644 index 0000000..a48fac9 --- /dev/null +++ b/test/qglview/shaders/dsl_pass_2.vert @@ -0,0 +1,6 @@ +#version 120 + +void main(void) { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); +} diff --git a/test/qglview/shaders/fbo_add.frag b/test/qglview/shaders/fbo_add.frag new file mode 100644 index 0000000..764fc65 --- /dev/null +++ b/test/qglview/shaders/fbo_add.frag @@ -0,0 +1,7 @@ +#version 150 + +uniform sampler2D t0, t1; + +void main(void) { + qgl_FragData[0].rgb = texelFetch(t0, ivec2(gl_FragCoord.xy), 0).rgb + texelFetch(t1, ivec2(gl_FragCoord.xy), 0).rgb; +} diff --git a/test/qglview/shaders/fbo_add.vert b/test/qglview/shaders/fbo_add.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/fbo_add.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/hdr.frag b/test/qglview/shaders/hdr.frag new file mode 100644 index 0000000..b3e50a3 --- /dev/null +++ b/test/qglview/shaders/hdr.frag @@ -0,0 +1,29 @@ +#version 150 + +uniform sampler2D t0; +uniform float exposure; + +float tA = 0.15; +float tB = 0.50; +float tC = 0.10; +float tD = 0.20; +float tE = 0.02; +float tF = 0.30; +float tW = 11.2; + +vec3 Uncharted2Tonemap(vec3 x) { + return ((x*(tA*x+tC*tB)+tD*tE)/(x*(tA*x+tB)+tD*tF))-tE/tF; +} + +void main(void) { + //qgl_FragData[0].rgb = texelFetch(t0, ivec2(gl_FragCoord.xy), 0).rgb; + //return; + vec3 inColor = texelFetch(t0, ivec2(gl_FragCoord.xy), 0).rgb; + inColor *= exposure / 1.45; + float ExposureBias = 1.; + vec3 curr = Uncharted2Tonemap(ExposureBias*inColor); + vec3 whiteScale = 1. / Uncharted2Tonemap(vec3(tW)); + vec3 color = curr * whiteScale; + vec3 retColor = color;//pow(color, vec3(1 / 1)); + qgl_FragData[0].rgb = retColor; +} diff --git a/test/qglview/shaders/hdr.vert b/test/qglview/shaders/hdr.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/hdr.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/hdr_scale_0.frag b/test/qglview/shaders/hdr_scale_0.frag new file mode 100644 index 0000000..9518aed --- /dev/null +++ b/test/qglview/shaders/hdr_scale_0.frag @@ -0,0 +1,13 @@ +#version 130 + +uniform sampler2D t0; +uniform vec2 vsize; + +const vec3 luma = vec3(0.299, 0.587, 0.114); + +void main(void) { + vec2 tc = gl_TexCoord[0].xy;//, dt = 1. / vsize; + vec3 dc0 = texture2D(t0, tc).rgb, dc1 = textureOffset(t0, tc, ivec2(1, 0)).rgb, dc2 = textureOffset(t0, tc, ivec2(0, 1)).rgb, dc3 = textureOffset(t0, tc, ivec2(1, 1)).rgb; + float l0 = dot(dc0, luma), l1 = dot(dc1, luma), l2 = dot(dc2, luma), l3 = dot(dc3, luma); + gl_FragColor.rgb = vec3(max(max(max(l0, l1), l2), l3), min(min(min(l0, l1), l2), l3), (l0 + l1 + l2 + l3) / 4.); +} diff --git a/test/qglview/shaders/hdr_scale_1.frag b/test/qglview/shaders/hdr_scale_1.frag new file mode 100644 index 0000000..833ee6d --- /dev/null +++ b/test/qglview/shaders/hdr_scale_1.frag @@ -0,0 +1,10 @@ +#version 130 + +uniform sampler2D t0; +uniform vec2 vsize; + +void main(void) { + vec2 tc = gl_TexCoord[0].xy;//, dt = 1. / vsize; + vec3 dc0 = texture2D(t0, tc).rgb, dc1 = textureOffset(t0, tc, ivec2(1, 0)).rgb, dc2 = textureOffset(t0, tc, ivec2(0, 1)).rgb, dc3 = textureOffset(t0, tc, ivec2(1, 1)).rgb; + gl_FragColor.rgb = vec3(max(max(max(dc0.r, dc1.r), dc2.r), dc3.r), min(min(min(dc0.g, dc1.g), dc2.g), dc3.g), (dc0.b + dc1.b + dc2.b + dc3.b) / 4.); +} diff --git a/test/qglview/shaders/light_models.frag b/test/qglview/shaders/light_models.frag new file mode 100644 index 0000000..57d787f --- /dev/null +++ b/test/qglview/shaders/light_models.frag @@ -0,0 +1,161 @@ +#version 130 + +const float e = 2.7182818284; +const float pi = 3.1415926; + + +float Phong_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + return max(dot(n, l), 0.); +} + +float Phong_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + return pow(max(dot(n, h), 0.), shininess); +} + + +float Cook_Torrance_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + return max(dot(n, l), 0.); +} + +float Cook_Torrance_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + float NdotL = max( dot( n, l ), 0. ); + float NdotV = max( dot( n, v ), 0. ); + float NdotH = max( dot( n, h ), 1.e-7 ); + float VdotH = max( dot( v, h ), 0. ); + float geometric = 2. * NdotH / VdotH; + geometric = min(1., geometric * min(NdotV, NdotL)); + float r_sq = 1. / (shininess * shininess); + float NdotH_sq = NdotH * NdotH; + float NdotH_sq_r = 1. / (NdotH_sq * r_sq); + float roughness_exp = (NdotH_sq - 1.) * (NdotH_sq_r); + float roughness = exp(roughness_exp) * NdotH_sq_r / (4. * NdotH_sq); + float fresnel = 1. / (1. + NdotV); + return min(1., (fresnel * geometric * roughness) / (NdotV * NdotL + 1.e-7)); +} + + +float Minnaert_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + return max(dot(n, l), 0.); +} + +float Minnaert_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + float k = shininess / 128.; + float d1 = pow(max(dot(n, l), 0.), 1. + k); + float d2 = pow(1. - dot(n, v), 1. - k); + return d1*d2; +} + + +float fresnel(float x, float kf) { + float dx = x - kf; + float d1 = 1.0 - kf; + float kf2 = kf * kf; + + return (1.0 / (dx * dx) - 1.0 / kf2) / (1.0 / (d1 * d1) - 1.0 / kf2 ); + //return 1.0; +} + +float shadow(float x, float ks) { + float dx = x - ks; + float d1 = 1.0 - ks; + float ks2 = ks * ks; + + //return (1.0 / (dx * dx) - 1.0 / ks2) / (1.0 / (d1 * d1) - 1.0 / ks2 ); + return 1.0; +} + +const float smooth_ = 0.5; +const float transp = 0; +const float k = 0.1; +const float kf = 1.12; +const float ks = 1.01; + +float Strauss_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + vec3 h2 = reflect(l, n); + float metal = shininess / 20.; + float nl = dot( n, l); + float nv = dot( n, v); + float hv = dot( h2, v); + float f = fresnel( nl, kf ); + float s3 = smooth_ * smooth_ * smooth_; + + // diffuse term + float d = ( 1.0 - metal * smooth_ ); + float Rd = ( 1.0 - s3 ) * ( 1.0 - transp ); + float diff = nl * d * Rd; + + // inputs into the specular term + + // composite the final result, ensuring + + return max(diff, 0.); +} + + +float Strauss_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + vec3 h2 = reflect(l, n); + float metal = shininess / 20.; + float nl = dot( n, l); + float nv = dot( n, v); + float hv = dot( h2, v); + float f = fresnel( nl, kf ); + float s3 = smooth_ * smooth_ * smooth_; + + // diffuse term + float d = ( 1.0 - metal * smooth_ ); + float Rd = ( 1.0 - s3 ) * ( 1.0 - transp ); + float diff = nl * d * Rd; + + // inputs into the specular term + float r = (1.0 - transp) - Rd; + float j = f * shadow ( nl, ks ) * shadow ( nv, ks ); + float refl = min ( 1.0, r + j * ( r + k ) ); + float Cs = 1. + metal * (1.0 - f); + float spec = Cs * refl; + + spec *= pow ( -hv, 3.0 / (1.0 - smooth_) ); + + // composite the final result, ensuring + + return max(spec, 0.); +} + +uniform float a, b; + +float Oren_Nayar_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + float nl = dot ( n, l ); + float nv = dot ( n, v ); + vec3 lProj = normalize ( l - n * nl ); + vec3 vProj = normalize ( v - n * nv ); + float cx = max ( dot ( lProj, vProj ), 0.0 ); + + float cosAlpha = nl > nv ? nl : nv; + float cosBeta = nl > nv ? nv : nl; + float dx = sqrt ( ( 1.0 - cosAlpha * cosAlpha ) * ( 1.0 - cosBeta * cosBeta ) ) / cosBeta; + + return max(0. , nl) * (a + b * cx * dx); + //return max(dot(n, l), 0.); +} + +float Oren_Nayar_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + return pow(max(dot(n, h), 0.), shininess); +} + +/* +float light_diffuse(int model, vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + if (model == 0) return Phong_diffuse(l, n, h, v, shininess); + if (model == 1) return Cook_Torrance_diffuse(l, n, h, v, shininess); + if (model == 2) return Minnaert_diffuse(l, n, h, v, shininess); + if (model == 3) return Strauss_diffuse(l, n, h, v, shininess); + if (model == 4) return Oren_Nayar_diffuse(l, n, h, v, shininess); + return 0.; +} + +float light_specular(int model, vec3 l, vec3 n, vec3 h, vec3 v, float shininess) { + if (model == 0) return Phong_specular(l, n, h, v, shininess); + if (model == 1) return Cook_Torrance_specular(l, n, h, v, shininess); + if (model == 2) return Minnaert_specular(l, n, h, v, shininess); + if (model == 3) return Strauss_specular(l, n, h, v, shininess); + if (model == 4) return Oren_Nayar_specular(l, n, h, v, shininess); + return 0.; +}*/ diff --git a/test/qglview/shaders/motion_blur.frag b/test/qglview/shaders/motion_blur.frag new file mode 100644 index 0000000..3a758c4 --- /dev/null +++ b/test/qglview/shaders/motion_blur.frag @@ -0,0 +1,24 @@ +#version 150 + +uniform sampler2D t0, ts; +uniform vec2 dt; +uniform float factor = 1.; +uniform int steps = 8; + +void main(void) { + vec2 cdt = dt / steps; + vec2 ct = qgl_FragTexture.xy; + vec3 speed = texelFetch(ts, ivec2(gl_FragCoord.xy), 0).rgb * 256 * factor; + int hsteps = steps / 2; + ct -= speed.xy * cdt * hsteps; + vec3 scol = vec3(0);//texture(t0, ct).rgb; + float sum = 0.; + for (int i = 0; i < steps; ++i) { + ct += speed.xy * cdt; + float mul = 1. - abs(i - hsteps) * 2. / steps; + scol += texture(t0, ct).rgb * mul; + sum += mul; + } + scol /= sum; + qgl_FragData[0].rgb = scol; +} diff --git a/test/qglview/shaders/motion_blur.vert b/test/qglview/shaders/motion_blur.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/motion_blur.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/post.frag b/test/qglview/shaders/post.frag new file mode 100644 index 0000000..22e297e --- /dev/null +++ b/test/qglview/shaders/post.frag @@ -0,0 +1,42 @@ +#version 130 + +uniform sampler2D t0, t1;//, t2; +uniform vec2 vsize; +uniform float clum; +uniform bool has_bloom; + +float unpackDepth(vec4 dv) { + return dot(dv.rgb, vec3(65025., 255., 1.)); +} + +void main(void) { + vec2 tc = gl_TexCoord[0].xy;//, dt = 2. / vsize, dt2 = dt + dt; + vec4 bloom; + float tone = 1. / clum; + if (has_bloom) { + bloom = (texture2D(t1, tc) * 3. + + textureOffset(t1, tc, ivec2(2, 0)) * 2. + + textureOffset(t1, tc, ivec2(0, 2)) * 2. + + textureOffset(t1, tc, ivec2(-2, 0)) * 2. + + textureOffset(t1, tc, ivec2(0, -2)) * 2. + + textureOffset(t1, tc, ivec2(2, 2)) * 1.5 + + textureOffset(t1, tc, ivec2(2, -2)) * 1.5 + + textureOffset(t1, tc, ivec2(-2, -2)) * 1.5 + + textureOffset(t1, tc, ivec2(-2, 2)) * 1.5 + + textureOffset(t1, tc, ivec2(4, 0)) + + textureOffset(t1, tc, ivec2(0, 4)) + + textureOffset(t1, tc, ivec2(-4, 0)) + + textureOffset(t1, tc, ivec2(0, -4)) + + textureOffset(t1, tc, ivec2(4, 2)) + + textureOffset(t1, tc, ivec2(4, -2)) + + textureOffset(t1, tc, ivec2(-4, -2)) + + textureOffset(t1, tc, ivec2(-4, 2)) + + textureOffset(t1, tc, ivec2(2, 4)) + + textureOffset(t1, tc, ivec2(2, -4)) + + textureOffset(t1, tc, ivec2(-2, -4)) + + textureOffset(t1, tc, ivec2(-2, 4))) / 29.; + vec4 diff = texture2D(t0, tc); + gl_FragColor = max(diff * tone, bloom);// + vec4(0.01/(clum+0.1)); + } else + gl_FragColor = texture2D(t0, tc) * tone;// + vec4(0.01/(clum+0.1)); +} diff --git a/test/qglview/shaders/ppl.frag b/test/qglview/shaders/ppl.frag new file mode 100644 index 0000000..c5aee6c --- /dev/null +++ b/test/qglview/shaders/ppl.frag @@ -0,0 +1,179 @@ +#version 130 + +#define lc 4 +varying vec4 diffuse[lc], pos, spos;//, ambient;//, col; +varying vec3 lightDir[lc]/*halfVector[lc], */; +//varying vec3 lightDir[lc]/*halfVector[lc], */; +varying vec3 normal, srcn; +varying float fogCoord, alpha; + +uniform int lightsCount; +uniform bool acc_light, acc_fog, has_diffuse, has_bump, is_glass, shadows, soft_shadows; +uniform float bump_scale, reflectivity, iof, cdis; +uniform vec2 vsize; +uniform sampler2D t0, t1, t2; +uniform sampler2D s0; +uniform samplerCube tc, tc0; +uniform mat4 mat; + +float light_diffuse(vec3 l, vec3 n, vec3 h, vec3 v, float shininess); +float light_specular(vec3 l, vec3 n, vec3 h, vec3 v, float shininess); + +const float bias = 1.; +vec2 sp; +vec4 sc, dc;//ambient; + +void calcLight(in int index, in vec3 n, in vec3 v) { + vec2 scoord, sdt, sdt2; + vec3 halfV, ldir; + vec4 lpos = gl_LightSource[index].position; + float spot, ldist = length(lpos.xyz - pos.xyz), shadow = 1., sdep, ddep; + ldir = normalize(lpos.xyz - (pos.xyz * lpos.w)); + halfV = normalize(ldir + v); + //if (dot(ldir, n) > 0) { + spot = step(0., dot(ldir, n)); + if (gl_LightSource[index].spotCutoff < 180.) { + spot = max(dot(-ldir, gl_LightSource[index].spotDirection.xyz), 0.); + spot *= step(gl_LightSource[index].spotExponent, spot); + spot = pow(spot, (gl_LightSource[index].spotCosCutoff + 0.001)); + if (spot > 0. && shadows) { + scoord = (spos.xyz / spos.w).xy / 2 + vec2(0.5); + sdep = texture(s0, scoord.xy).r + bias; + ddep = pow((spos.z - sdep) / 4., 0.5) * 2.; + shadow = clamp(sdep - spos.z, 0., 1.); + if (soft_shadows) { + sdt = ddep / vsize; + sdt2 = sdt + sdt; + shadow += (clamp(texture(s0, scoord.xy + vec2(sdt.x, 0.)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, 0.)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, 0)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, 0)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt2.x, 0)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, sdt2.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt2.x, 0)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(0, -sdt2.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt2.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt2.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt2.x, -sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt2.x, sdt.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, sdt2.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(sdt.x, -sdt2.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, -sdt2.y)).r + bias - spos.z, 0., 1.) + + clamp(texture(s0, scoord.xy + vec2(-sdt.x, sdt2.y)).r + bias - spos.z, 0., 1.)) / 29.; + shadow = clamp(pow(shadow, 0.5), 0., 1); + } + spot *= shadow; + } + } + spot /= (gl_LightSource[index].constantAttenuation + ldist * (gl_LightSource[index].linearAttenuation + ldist * gl_LightSource[index].quadraticAttenuation)); + dc += spot * diffuse[index] * light_diffuse(ldir, n, halfV, v, gl_FrontMaterial.shininess); + sc += spot * gl_FrontMaterial.specular * gl_LightSource[index].specular * light_specular(ldir, n, halfV, v, gl_FrontMaterial.shininess); + //} +} + +void main(void) { + /*vec3 scoord = spos.xyz / spos.w; + float sdep = texture(s0, scoord.xy / 2 + vec2(0.5)).r + 1; + //gl_FragColor.rgb = vec3(step(spos.z, sdep)); + gl_FragColor.rgb = vec3(spos.z - sdep)/10; + //gl_FragColor.rgb = vec3(texture(s0, scoord.xy+vec2(0.5)).r/100); + //gl_FragColor.rgb = vec3(texture(s0,).r/100); + return;*/ + //bias = (vsize.x - vsize.y) / max(vsize.x, vsize.y)*5 - 1.; + + if (!acc_light) { + if (has_diffuse) gl_FragColor = texture2D(t0, gl_TexCoord[0].xy); + //else gl_FragColor = gl_Color; + if (acc_fog) + gl_FragColor.xyz = mix(gl_FragColor.xyz, gl_Fog.color.xyz, fogCoord); + return; + } + + sp = gl_FragCoord.xy / vsize; + vec3 n, v = normalize(-pos.xyz); + //v.xy = vec2(1,1)-gl_FragCoord.xy / vsize*2 ;//- vec2(1,1); + if (has_bump) n = normalize(normal + (texture2D(t1, gl_TexCoord[0].xy).xyz - vec3(0.5, 0.5, 1.)) * bump_scale); + else n = normalize(normal); + vec4 color; + sc = vec4(0.); + dc = gl_LightModel.ambient * gl_FrontMaterial.ambient;//ambient; + if (lightsCount > 0) { + calcLight(0, n, v); + if (lightsCount > 1) { + calcLight(1, n, v); + if (lightsCount > 2) { + calcLight(2, n, v); + if (lightsCount > 3) { + calcLight(3, n, v); + /*if (lightsCount > 4) { + calcLight(4, n, v); + if (lightsCount > 5) { + calcLight(5, n, v); + if (lightsCount > 6) { + calcLight(6, n, v); + if (lightsCount > 7) { + calcLight(7, n, v); + } + } + } + }*/ + } + } + } + } + sc = max(sc, vec4(0.)); + vec2 spr = sp, spg = sp, spb = sp, spc = sp, spm = sp, spy = sp, dsp; + vec3 bgf; + float fresnel = pow(1. - max(dot(v, n), 0.), 1); + dsp = fresnel * n.xy * (1. - iof); + spg += dsp; + if (cdis > 0.) { + spr += dsp * (1. - cdis - cdis); + spy += dsp * (1. - cdis); + spc += dsp * (1. + cdis); + spb += dsp * (1. + cdis + cdis); + vec3 bg0 = texture2D(t2, spr).rgb, bg1 = texture2D(t2, spy).rgb, bg2 = texture2D(t2, spg).rgb, bg3 = texture2D(t2, spc).rgb, bg4 = texture2D(t2, spb).rgb; + //bgf = vec3(bg0.r, bg2.g, bg4.b); + bgf = vec3((bg0.r + bg1.r) / 2., (bg1.g + bg2.g + bg3.g) / 3., (bg3.b + bg4.b) / 2.); + } else bgf = texture2D(t2, spg).rgb; + if (has_diffuse) { + vec4 tex = texture2D(t0, gl_TexCoord[0].xy); + dc *= tex; + color.a = tex.a * alpha; + } else color.a = alpha; + color.rgb = dc.rgb; + vec3 ln = vec3(0); + if (is_glass) { + ln = reflect(v, n); + ln = ln * mat3(gl_ModelViewMatrix); + ln.y = -ln.y; + color.rgb = (mix(bgf, textureCube(tc, ln).rgb, 0.2 + fresnel * 0.8) * gl_FrontMaterial.diffuse.rgb + gl_FrontMaterial.emission.rgb) + sc.rgb * color.a; + color.a = 1.; + } else { + if (reflectivity > 0.) { + ln = reflect(v, n); + ln = ln * mat3(gl_ModelViewMatrix); + ln.y = -ln.y; + color.rgb = mix(color.rgb, textureCube(tc, ln).rgb, reflectivity); + } + color.rgb = mix(color.rgb * gl_FrontMaterial.diffuse.rgb + gl_FrontMaterial.emission.rgb, bgf, 1. - alpha) + sc.rgb * color.a; + color.a = 1; + } + if (acc_fog) + color.rgb = mix(color.rgb, gl_Fog.color.rgb, fogCoord); + color.r = 250; + gl_FragColor = color; + //gl_FragColor.rgb = mix(ldir, texture2D(t2, sp).rgb, 1. - alpha); +} diff --git a/test/qglview/shaders/ppl.vert b/test/qglview/shaders/ppl.vert new file mode 100644 index 0000000..a09a053 --- /dev/null +++ b/test/qglview/shaders/ppl.vert @@ -0,0 +1,66 @@ +#version 130 + +#define lc 4 +varying vec4 diffuse[lc], pos, spos;//, ambient;//, col; +varying vec3 lightDir[lc]/*halfVector[lc], */; +//varying vec3 lightDir[lc]/*halfVector[lc], */; +varying vec3 normal, srcn; +varying float fogCoord, alpha; + +uniform int lightsCount; +uniform bool acc_fog, shadows; +uniform mat4 mat; + +void prepareLight(in int index, in vec3 p) { + vec4 lpos = gl_LightSource[index].position; + lightDir[index] = normalize(lpos.xyz - (p * lpos.w)); + //halfVector[index] = normalize(lightDir[index] - normalize(p)); + diffuse[index] = gl_FrontMaterial.diffuse * gl_LightSource[index].diffuse; +} + +void main(void) { + float NdotL; + //col = gl_Color; + alpha = gl_Color.a; + normal = normalize(gl_NormalMatrix * gl_Normal); + srcn = gl_Normal; + pos = gl_ModelViewMatrix * gl_Vertex; + //v = normalize(-pos); + if (acc_fog) { + fogCoord = (gl_Fog.end - length(pos) * 0.85) / (gl_Fog.end - gl_Fog.start); + fogCoord = 1. - clamp(fogCoord, 0., 1.); + } + vec3 ldir; + if (lightsCount > 0) { + prepareLight(0, pos.xyz); + if (lightsCount > 1) { + prepareLight(1, pos.xyz); + if (lightsCount > 2) { + prepareLight(2, pos.xyz); + if (lightsCount > 3) { + prepareLight(3, pos.xyz); + diffuse[3] = gl_FrontMaterial.diffuse * gl_LightSource[3].diffuse; + /*if (lightsCount > 4) { + diffuse[4] = gl_FrontMaterial.diffuse * gl_LightSource[4].diffuse; + if (lightsCount > 5) { + diffuse[5] = gl_FrontMaterial.diffuse * gl_LightSource[5].diffuse; + if (lightsCount > 6) { + diffuse[6] = gl_FrontMaterial.diffuse * gl_LightSource[6].diffuse; + if (lightsCount > 6) { + diffuse[7] = gl_FrontMaterial.diffuse * gl_LightSource[7].diffuse; + } + } + } + }*/ + } + } + } + } + //ambient = gl_LightModel.ambient * gl_FrontMaterial.ambient; + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_TexCoord[2] = gl_MultiTexCoord2; + gl_TexCoord[3] = gl_MultiTexCoord3; + gl_Position = ftransform(); + spos = pos * mat; +} diff --git a/test/qglview/shaders/selection.frag b/test/qglview/shaders/selection.frag new file mode 100644 index 0000000..43ccb95 --- /dev/null +++ b/test/qglview/shaders/selection.frag @@ -0,0 +1,7 @@ +#version 150 + +uniform vec4 id; + +void main(void) { + qgl_FragData[0] = id; +} diff --git a/test/qglview/shaders/selection.vert b/test/qglview/shaders/selection.vert new file mode 100644 index 0000000..6f25e2b --- /dev/null +++ b/test/qglview/shaders/selection.vert @@ -0,0 +1,8 @@ +#version 150 + +uniform float z_near, z_far; + +void main(void) { + vec4 pos = qgl_ftransform(); + gl_Position = pos; +} diff --git a/test/qglview/shaders/selection_halo.frag b/test/qglview/shaders/selection_halo.frag new file mode 100644 index 0000000..d95f2e3 --- /dev/null +++ b/test/qglview/shaders/selection_halo.frag @@ -0,0 +1,20 @@ +#version 150 + +uniform vec2 dt; +uniform vec4 selected, color; +uniform sampler2D t0; +uniform float fill; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 ds0 = abs(texelFetchOffset(t0, tc, 0, ivec2(-1, 0)) - selected); + vec4 ds1 = abs(texelFetchOffset(t0, tc, 0, ivec2( 1, 0)) - selected); + vec4 ds2 = abs(texelFetchOffset(t0, tc, 0, ivec2(0, -1)) - selected); + vec4 ds3 = abs(texelFetchOffset(t0, tc, 0, ivec2(0, 1)) - selected); + float d0 = dot(ds0, vec4(1., 1., 1., 1.)), d1 = dot(ds1, vec4(1., 1., 1., 1.)), d2 = dot(ds2, vec4(1., 1., 1., 1.)), d3 = dot(ds3, vec4(1., 1., 1., 1.)); + float vs = step(1e-6, d0 + d1 + d2 + d3); + float vm = step(1e-3, (d0 * 25.) * (d1 * 25.) * (d2 * 255.) * (d3 * 255.)); + float v = mix(vs - vm, vs - vm - vm + 1, fill); + //qgl_FragData[0] = vec4(1,0,0,0.5);//vec4(color.rgb, v * color.a); + qgl_FragData[0] = vec4(color.bgr, v * color.a); +} diff --git a/test/qglview/shaders/selection_halo.vert b/test/qglview/shaders/selection_halo.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/selection_halo.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/shadow.frag b/test/qglview/shaders/shadow.frag new file mode 100644 index 0000000..145b87e --- /dev/null +++ b/test/qglview/shaders/shadow.frag @@ -0,0 +1,7 @@ +#version 150 + +in float w; + +void main(void) { + //;qgl_FragData[0].r = w; +} diff --git a/test/qglview/shaders/shadow.vert b/test/qglview/shaders/shadow.vert new file mode 100644 index 0000000..3b1faca --- /dev/null +++ b/test/qglview/shaders/shadow.vert @@ -0,0 +1,10 @@ +#version 150 + +out float w; + +void main(void) { + qgl_FragTexture = qgl_Texture; + vec4 pos = qgl_ftransform(); + w = pos.w; + gl_Position = pos; +} diff --git a/test/qglview/shaders/ssao_blur.frag b/test/qglview/shaders/ssao_blur.frag new file mode 100644 index 0000000..9a42ca9 --- /dev/null +++ b/test/qglview/shaders/ssao_blur.frag @@ -0,0 +1,55 @@ +#version 150 + +uniform sampler2D t0, ts, tg1; +uniform float radius = 2; +uniform vec2 dt; + +vec4 st; + +vec4 getTexel(vec2 tc, vec4 ptc, float mul) { + vec4 tv = texture(t0, tc), ts = texture(ts, tc); + if (radius == 1) { + tv = vec4(tv.w); + ts = tv; + } + return vec4(min(ptc.r, ts.r), max(ptc.g, ts.g), ptc.b + /*mix(tv.b, st.b, clamp(abs(st.w - tv.w), 0, 1))*/tv.b * mul, 0); +} + +void main(void) { + ivec2 itc = ivec2(gl_FragCoord.xy); + vec4 vg1 = texelFetch(tg1, itc, 0); + vec2 tc = qgl_FragTexture.xy; + float o = radius, o2 = radius * 2; + vec4 scol = texture(t0, tc); + if (radius == 1) + scol = vec4(scol.w); + st = scol; + scol.b *= 3.; + scol = getTexel(tc + dt * vec2( o, 0 ), scol, 2); + scol = getTexel(tc + dt * vec2( 0, o ), scol, 2); + scol = getTexel(tc + dt * vec2( -o, 0 ), scol, 2); + scol = getTexel(tc + dt * vec2( 0, -o), scol, 2); + scol = getTexel(tc + dt * vec2( o, o ), scol, 1.5); + scol = getTexel(tc + dt * vec2( o, -o), scol, 1.5); + scol = getTexel(tc + dt * vec2( -o, -o ), scol, 1.5); + scol = getTexel(tc + dt * vec2( -o, o ), scol, 1.5); + scol = getTexel(tc + dt * vec2( o2, 0 ), scol, 1); + scol = getTexel(tc + dt * vec2( 0, o2), scol, 1); + scol = getTexel(tc + dt * vec2(-o2, 0 ), scol, 1); + scol = getTexel(tc + dt * vec2( 0, -o2), scol, 1); + scol.b /= 21.; + /*vec4 scol = fp; + scol = min(scol, getTexel(tc + dt * vec2( o, 0 ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( 0, o ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( -o, 0 ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( 0, -o), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( o, o ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( o, -o), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( -o, -o ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( -o, o ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( o2, 0 ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( 0, o2), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2(-o2, 0 ), fp, fps)); + scol = min(scol, getTexel(tc + dt * vec2( 0, -o2), fp, fps));*/ + qgl_FragData[0].rgba = vec4(scol.rgb, texture(t0, tc).w); +} diff --git a/test/qglview/shaders/ssao_blur.vert b/test/qglview/shaders/ssao_blur.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/ssao_blur.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/ssao_merge.frag b/test/qglview/shaders/ssao_merge.frag new file mode 100644 index 0000000..ae49372 --- /dev/null +++ b/test/qglview/shaders/ssao_merge.frag @@ -0,0 +1,53 @@ +#version 150 + +uniform sampler2D t0, ts, tg1; +uniform sampler1D n0; +uniform float z_near, z_far; +uniform mat4 mat_proj; + +in vec3 view_dir; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 v0 = texelFetch(t0, tc, 0), vs = texelFetch(ts, tc, 0), vg1 = texelFetch(tg1, tc, 0); + vec3 n = vec3(vg1.xyz); + vec3 bn = cross(n, vec3(0, 0, 1)); + vec3 bn2 = cross(n, bn); + vec4 pos; + pos.w = 1; + pos.xyz = view_dir * vs.w; + vec4 spos = pos, tpos, tv0; + vec2 tsp; + float sclz = dot(vec3(0,0,1), n); + /*float l = 20 * 0.5; + pos.xyz += bn2 * l; + for (int i = 0; i < 16; ++i) { + tpos = mat_proj * pos; + tsp = -(tpos.xy / tpos.w) / 2. + 0.5; + tv0 = texture(ts, tsp); + l *= 0.5; + pos.xyz += bn2 * (step(pos.z, tv0.w) * 2. - 1.) * l; + }*/ + /*vec3 rn; + float ss = 0.; + for (int i = 0; i < 32; ++i) { + rn = texelFetch(n0, i, 0).rgb * 2 - 1; + rn *= step(0., dot(n, rn)) * 2 - 1.; + //rn /= 10; + rn /= vs.w; + spos = pos + vec4(rn, 0); + tpos = mat_proj * spos; + tsp = -(tpos.xy / tpos.w) / 2. + 0.5; + tv0 = texture(ts, tsp); + //spos.z = tv0.w; + ss += step(-0.1, tv0.w-vs.w); + //ss += step(vs.w, tv0.w); + } + ss /= 32;*/ + float minz = v0.r, maxz = v0.g, blurz = v0.b, curz = v0.w, avgz = (minz+maxz)/2; + float dz = maxz - minz; + //qgl_FragData[0].rgb = vec3(max(-(blurz - curz) / maxz,0.)* (curz-minz) / dz * 10); + qgl_FragData[0].rgb = vec3(blurz/10); + //qgl_FragData[0].rgb = vec3(length(pos.z - spos.z)/15); + //qgl_FragData[0].rgb = vec3(abs(v0.w/1)); +} diff --git a/test/qglview/shaders/ssao_merge.vert b/test/qglview/shaders/ssao_merge.vert new file mode 100644 index 0000000..0e2d5fe --- /dev/null +++ b/test/qglview/shaders/ssao_merge.vert @@ -0,0 +1,10 @@ +#version 150 + +in vec3 view_corner; +out vec3 view_dir; + +void main(void) { + view_dir = view_corner / view_corner.z; + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/ssr.frag b/test/qglview/shaders/ssr.frag new file mode 100644 index 0000000..04f1786 --- /dev/null +++ b/test/qglview/shaders/ssr.frag @@ -0,0 +1,67 @@ +#version 150 + +uniform sampler2D t0, t1, ts, td, tbs; +uniform float z_near, z_far; +uniform mat4 mat_proj; +uniform vec3 cam_aim, cam_pos; + +in vec3 view_dir; + +const float _pe = 2.4e-7; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 v0 = texelFetch(t0, tc, 0), v1 = texelFetch(t1, tc, 0), vs = texelFetch(ts, tc, 0); + vec3 sp = vec3(qgl_FragTexture.xy, v0.w); + vec2 tsp; + vec4 pos; + float z = texelFetch(td, tc, 0).r; + z = z + z - 1; + z = ((_pe - 2.) * z_near) / (z + _pe - 1.); // infinite depth + pos.w = 1; + pos.xyz = view_dir * z; + //pos.z = -pos.z; + vec4 spos = pos; + vec4 tpos; + + vec3 dc = v0.rgb, n = v1.xyz; + vec3 vd = -normalize(vec3(-view_dir.xy, view_dir.z)); + vec3 rn = reflect(vd, n); + //rn.z += 1.; + float coeff = clamp(1. - (dot(vec3(0,0,1), n)), 0, 1); + coeff = coeff*coeff; + /*coeff = coeff*coeff; + coeff = coeff*coeff; + coeff = coeff*coeff;*/ + float reflectivity = 0.; + float specularity = modf(v1.w, reflectivity); + + rn.z = -rn.z; + int i = 0; + vec4 tv0; + float l = z_far * 0.5; + pos.xyz += rn * l; + float cz; + for (i = 0; i < 24; ++i) { + tpos = mat_proj * pos; + tsp = -(tpos.xy / tpos.w) / 2. + 0.5; + cz = texture(td, tsp).r; + cz = cz + cz - 1; + cz = ((_pe - 2.) * z_near) / (cz + _pe - 1.); // infinite depth + l *= 0.5; + pos.xyz += rn * (step(pos.z, cz) * 2. - 1.) * l; + } + + vec2 ess = abs(tsp - vec2(0.5, 0.5)) - vec2(0.3, 0.3); + ess = clamp(ess, vec2(0, 0), vec2(0.2, 0.2)); + ess = smoothstep(vec2(0.2, 0.2), vec2(0, 0), ess); + coeff *= min(ess.x, ess.y); + vec4 pr_pos = mat_proj * pos, pr_spos = mat_proj * spos; + pr_pos.xyz /= pr_pos.w; + pr_spos.xyz /= pr_spos.w; + float blur = step(0., coeff) * length(pr_pos.xyz - pr_spos.xyz) * (1. - specularity); + vec3 rvs = texture(ts, tsp).rgb; + + qgl_FragData[0] = vec4(rvs.rgb, coeff / 1.1 + clamp(round(blur * 10), 0, 1000)); + //qgl_FragData[0] = vec4(rvs.rgb, cz/5); +} diff --git a/test/qglview/shaders/ssr.vert b/test/qglview/shaders/ssr.vert new file mode 100644 index 0000000..0e2d5fe --- /dev/null +++ b/test/qglview/shaders/ssr.vert @@ -0,0 +1,10 @@ +#version 150 + +in vec3 view_corner; +out vec3 view_dir; + +void main(void) { + view_dir = view_corner / view_corner.z; + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/ssr_blur.frag b/test/qglview/shaders/ssr_blur.frag new file mode 100644 index 0000000..8fbefd3 --- /dev/null +++ b/test/qglview/shaders/ssr_blur.frag @@ -0,0 +1,38 @@ +#version 150 + +uniform sampler2D t0; +uniform float radius = 2; +uniform vec2 dt; + +void main(void) { + vec2 tc = qgl_FragTexture.xy; + float r = float(radius); + vec4 v0 = texture(t0, tc); + float rad; + float coeff = modf(v0.w, rad) * 1.1; + rad /= 10.; + rad *= 2; + float o = radius * rad, o2 = radius * rad * 2; + vec3 scol = (v0.rgb * 3. + + texture(t0, tc + dt * vec2( o, 0 )).rgb * 2. + + texture(t0, tc + dt * vec2( 0, o )).rgb * 2. + + texture(t0, tc + dt * vec2( -o, 0 )).rgb * 2. + + texture(t0, tc + dt * vec2( 0, -o)).rgb * 2. + + texture(t0, tc + dt * vec2( o, o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( o, -o)).rgb * 1.5 + + texture(t0, tc + dt * vec2( -o, -o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( -o, o )).rgb * 1.5 + + texture(t0, tc + dt * vec2( o2, 0 )).rgb + + texture(t0, tc + dt * vec2( 0, o2)).rgb + + texture(t0, tc + dt * vec2(-o2, 0 )).rgb + + texture(t0, tc + dt * vec2( 0, -o2)).rgb/* + + texture(t0, tc + dt * vec2( o2, o )).rgb + + texture(t0, tc + dt * vec2( o2, -o)).rgb + + texture(t0, tc + dt * vec2(-o2, -o )).rgb + + texture(t0, tc + dt * vec2(-o2, o )).rgb + + texture(t0, tc + dt * vec2( o, o2)).rgb + + texture(t0, tc + dt * vec2( o, -o2)).rgb + + texture(t0, tc + dt * vec2( -o, -o2)).rgb + + texture(t0, tc + dt * vec2( -o, o2)).rgb*/) / 21.; + qgl_FragData[0] = vec4(scol, v0.w); +} diff --git a/test/qglview/shaders/ssr_blur.vert b/test/qglview/shaders/ssr_blur.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/ssr_blur.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} diff --git a/test/qglview/shaders/ssr_merge.frag b/test/qglview/shaders/ssr_merge.frag new file mode 100644 index 0000000..9f0f81b --- /dev/null +++ b/test/qglview/shaders/ssr_merge.frag @@ -0,0 +1,15 @@ +#version 150 + +uniform sampler2D t0, tg1, ts; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 v0 = texelFetch(t0, tc, 0), vg1 = texelFetch(tg1, tc, 0), vs = texelFetch(ts, tc, 0); + float rad; + float coeff = clamp(modf(v0.w, rad) * 1.1, 0., 1.); + float reflectivity = 0.; + float specularity = modf(vg1.w, reflectivity); + reflectivity = clamp(reflectivity / 100., 0., 1.); + qgl_FragData[0].rgb = mix(vs.rgb, v0.rgb, coeff * reflectivity); + //qgl_FragData[0].rgb = vec3(v0.w); +} diff --git a/test/qglview/shaders/ssr_merge.vert b/test/qglview/shaders/ssr_merge.vert new file mode 100644 index 0000000..6bc001e --- /dev/null +++ b/test/qglview/shaders/ssr_merge.vert @@ -0,0 +1,6 @@ +#version 150 + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +}